diff --git a/.kilocode/mcp.json b/.kilocode/mcp.json new file mode 100644 index 0000000..b7fb7fb --- /dev/null +++ b/.kilocode/mcp.json @@ -0,0 +1,15 @@ +{ + "mcpServers": { + "git": { + "command": "uvx", + "args": [ + "mcp-server-git", + "--repository", + "/home/sepehr/dev/reactjs/tuto1" + ], + "alwaysAllow": [ + "git_status" + ] + } + } +} \ No newline at end of file diff --git a/.kilocodemodes b/.kilocodemodes new file mode 100644 index 0000000..685c1e4 --- /dev/null +++ b/.kilocodemodes @@ -0,0 +1,24 @@ +customModes: + - slug: code-reviewer + name: Code Reviewer + roleDefinition: | + You are a senior software engineer conducting thorough code reviews. You focus on code quality, security, performance, and maintainability. + groups: + - read + - browser + customInstructions: | + Provide constructive feedback on code patterns, potential bugs, security issues, and improvement opportunities. Be specific and actionable in suggestions. + source: project + - slug: docs-specialist + name: Documentation Specialist + roleDefinition: | + You are a technical writing expert specializing in clear, comprehensive documentation. You excel at explaining complex concepts simply and creating well-structured docs. + groups: + - read + - command + - - edit + - fileRegex: \.(md|mdx|txt|rst|adoc)$|README$|CHANGELOG$ + description: Documentation files only + customInstructions: | + Focus on clarity, proper formatting, and comprehensive examples. Always check for broken links and ensure consistency in tone and style. + source: project diff --git a/README.md b/README.md index e215bc4..5cc9000 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,274 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +# Tuto1 - Next.js Learning Project -## Getting Started +A modern Next.js application built with React 19, TypeScript, and Tailwind CSS. This project serves as a learning tutorial for modern web development practices and demonstrates component-based architecture. -First, run the development server: +## πŸš€ Features -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev +- **Next.js 15** with App Router +- **React 19** with TypeScript +- **Tailwind CSS** for styling +- **Radix UI** components for accessibility +- **Heroicons** for iconography +- **Responsive design** with mobile-first approach +- **Component-based architecture** + +## πŸ“‹ Table of Contents + +- [Quick Start](#quick-start) +- [Project Structure](#project-structure) +- [Components](#components) +- [API Reference](#api-reference) +- [Usage Examples](#usage-examples) +- [Development](#development) +- [Tech Stack](#tech-stack) + +## πŸš€ Quick Start + +### Prerequisites + +- Node.js 18+ +- npm, yarn, or pnpm + +### Installation + +1. **Clone the repository** + ```bash + git clone + cd tuto1 + ``` + +2. **Install dependencies** + ```bash + npm install + # or + pnpm install + # or + yarn install + ``` + +3. **Start development server** + ```bash + npm run dev + # or + pnpm dev + # or + yarn dev + ``` + +4. **Open your browser** + + Navigate to [http://localhost:3000](http://localhost:3000) to see the application. + +## πŸ“ Project Structure + +``` +src/ +β”œβ”€β”€ app/ # Next.js App Router +β”‚ β”œβ”€β”€ globals.css # Global styles +β”‚ β”œβ”€β”€ layout.tsx # Root layout component +β”‚ β”œβ”€β”€ page.tsx # Home page +β”‚ └── tuto1/ +β”‚ └── page.tsx # Tutorial examples page +β”œβ”€β”€ components/ # Reusable components +β”‚ β”œβ”€β”€ ui/ # UI component library +β”‚ β”‚ β”œβ”€β”€ avatar.tsx +β”‚ β”‚ β”œβ”€β”€ button.tsx +β”‚ β”‚ └── card.tsx +β”‚ └── user-card.tsx # User card component +└── lib/ + └── utils.ts # Utility functions ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +## 🧩 Components -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +### UserCard Component -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +A reusable card component that displays user information with an avatar, name, role, and email. -## Learn More +**Location:** [`src/components/user-card.tsx`](src/components/user-card.tsx) -To learn more about Next.js, take a look at the following resources: +#### Props -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +| Prop | Type | Required | Description | +|------|--------|----------|------------------------------| +| `user` | `User` | Yes | User object with id, name, email, and role | -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! +#### User Interface -## Deploy on Vercel +```typescript +interface User { + id: number; + name: string; + email: string; + role: string; +} +``` -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +#### Example Usage -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +```tsx +import { UserCard } from '@/components/user-card'; + +const user = { + id: 1, + name: "Jean Dupont", + email: "jean.dupont@example.com", + role: "DΓ©veloppeur" +}; + +function MyComponent() { + return ; +} +``` + +#### Features + +- **Avatar Display**: Shows user's initials in a circular avatar +- **Role Badge**: Displays user role with an icon +- **Email Display**: Shows email with envelope icon +- **Responsive Design**: Adapts to different screen sizes +- **Accessible**: Built with Radix UI primitives for screen reader support + +## 🏠 Main Page + +The home page (`src/app/page.tsx`) demonstrates a complete application layout with: + +### Layout Structure + +- **Header**: Navigation bar with application title +- **Main Content**: + - Action button for user interaction + - User card display + - Feature cards showcasing technologies + +### Features Demonstrated + +1. **Component Composition**: How components work together +2. **Props Passing**: Data flow from parent to child components +3. **Responsive Grid**: CSS Grid layout that adapts to screen size +4. **Styling**: Tailwind CSS utility classes +5. **TypeScript Integration**: Type-safe development + +## 🎯 API Reference + +### TypeScript Interfaces + +#### User Interface + +```typescript +interface User { + id: number; // Unique identifier + name: string; // Full name + email: string; // Email address + role: string; // Job role or position +} +``` + +## πŸ’‘ Usage Examples + +### Basic Component Usage + +```tsx +// Importing components +import { UserCard } from '@/components/user-card'; +import { Button } from '@/components/ui/button'; + +// Using the UserCard component +const sampleUser: User = { + id: 1, + name: "Marie Martin", + email: "marie.martin@example.com", + role: "Designer" +}; + +function HomePage() { + return ( +
+

My Application

+ + +
+ ); +} +``` + +### Styling with Tailwind CSS + +```tsx +// Responsive design example +
+
+

React

+

Bibliothèque pour construire des interfaces utilisateur

+
+
+``` + +## πŸ› οΈ Development + +### Available Scripts + +- `npm run dev` - Start development server with Turbopack +- `npm run build` - Build for production using Turbopack +- `npm run start` - Start production server +- `npm run lint` - Run ESLint for code quality + +### Development Guidelines + +1. **Component Structure**: Follow the existing pattern with proper TypeScript interfaces +2. **Styling**: Use Tailwind CSS utility classes for consistent design +3. **Accessibility**: Leverage Radix UI components for better accessibility +4. **Type Safety**: Define proper TypeScript interfaces for all data structures + +## πŸ› οΈ Tech Stack + +### Core Technologies + +- **[Next.js 15](https://nextjs.org/)** - React framework with App Router +- **[React 19](https://react.dev/)** - UI library +- **[TypeScript](https://www.typescriptlang.org/)** - Type-safe JavaScript +- **[Tailwind CSS](https://tailwindcss.com/)** - Utility-first CSS framework + +### UI Components + +- **[@radix-ui/react-avatar](https://www.radix-ui.com/primitives/docs/components/avatar)** - Avatar component +- **[@radix-ui/react-slot](https://www.radix-ui.com/primitives/docs/components/slot)** - Slot component +- **[class-variance-authority](https://cva.style/)** - Component variant management +- **[clsx](https://github.com/lukeed/clsx)** - Conditional CSS classes + +### Icons + +- **[@heroicons/react](https://heroicons.com/)** - Beautiful hand-crafted SVG icons + +### Development Tools + +- **[ESLint](https://eslint.org/)** - Code linting +- **[TypeScript](https://www.typescriptlang.org/)** - Type checking +- **[Turbopack](https://turbopack.dev/)** - Fast bundler + +## πŸ“š Learning Resources + +This project demonstrates several key concepts: + +1. **Modern React Development**: Hooks, components, and JSX +2. **Next.js App Router**: File-based routing and layouts +3. **TypeScript Integration**: Type safety and IntelliSense +4. **Component Libraries**: Reusable UI components +5. **Responsive Design**: Mobile-first CSS with Tailwind +6. **Accessibility**: Building inclusive web applications + +## 🀝 Contributing + +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Add tests if applicable +5. Submit a pull request + +## πŸ“„ License + +This project is private and not licensed for external use. + +--- + +**Note**: This is a learning project created for educational purposes to demonstrate modern web development practices with Next.js, React, and TypeScript. diff --git a/components.json b/components.json new file mode 100644 index 0000000..edcaef2 --- /dev/null +++ b/components.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "registries": {} +} diff --git a/package.json b/package.json index b182731..eec6b8d 100644 --- a/package.json +++ b/package.json @@ -9,19 +9,27 @@ "lint": "eslint" }, "dependencies": { + "@heroicons/react": "^2.2.0", + "@radix-ui/react-avatar": "^1.1.10", + "@radix-ui/react-slot": "^1.2.3", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.544.0", + "next": "15.5.4", "react": "19.1.0", "react-dom": "19.1.0", - "next": "15.5.4" + "tailwind-merge": "^3.3.1" }, "devDependencies": { - "typescript": "^5", + "@eslint/eslintrc": "^3", + "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", - "@tailwindcss/postcss": "^4", - "tailwindcss": "^4", "eslint": "^9", "eslint-config-next": "15.5.4", - "@eslint/eslintrc": "^3" + "tailwindcss": "^4", + "tw-animate-css": "^1.4.0", + "typescript": "^5" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 73a09a8..51d3ceb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,24 @@ importers: .: dependencies: + '@heroicons/react': + specifier: ^2.2.0 + version: 2.2.0(react@19.1.0) + '@radix-ui/react-avatar': + specifier: ^1.1.10 + version: 1.1.10(@types/react-dom@19.2.0(@types/react@19.2.0))(@types/react@19.2.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': + specifier: ^1.2.3 + version: 1.2.3(@types/react@19.2.0)(react@19.1.0) + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + lucide-react: + specifier: ^0.544.0 + version: 0.544.0(react@19.1.0) next: specifier: 15.5.4 version: 15.5.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -17,6 +35,9 @@ importers: react-dom: specifier: 19.1.0 version: 19.1.0(react@19.1.0) + tailwind-merge: + specifier: ^3.3.1 + version: 3.3.1 devDependencies: '@eslint/eslintrc': specifier: ^3 @@ -42,6 +63,9 @@ importers: tailwindcss: specifier: ^4 version: 4.1.14 + tw-animate-css: + specifier: ^1.4.0 + version: 1.4.0 typescript: specifier: ^5 version: 5.9.3 @@ -99,6 +123,11 @@ packages: resolution: {integrity: sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@heroicons/react@2.2.0': + resolution: {integrity: sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==} + peerDependencies: + react: '>= 16 || ^19.0.0-rc' + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -334,6 +363,86 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} + '@radix-ui/react-avatar@1.1.10': + resolution: {integrity: sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.2': + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.1': + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-is-hydrated@0.1.0': + resolution: {integrity: sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} @@ -723,9 +832,16 @@ packages: resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} engines: {node: '>=18'} + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1341,6 +1457,11 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + lucide-react@0.544.0: + resolution: {integrity: sha512-t5tS44bqd825zAW45UQxpG2CvcC4urOwn2TrwSH8u+MjeE+1NnWl6QqeQ/6NdjMqdOygyiT9p3Ev0p1NJykxjw==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + magic-string@0.30.19: resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} @@ -1681,6 +1802,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + tailwind-merge@3.3.1: + resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==} + tailwindcss@4.1.14: resolution: {integrity: sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==} @@ -1712,6 +1836,9 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tw-animate-css@1.4.0: + resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -1750,6 +1877,11 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -1849,6 +1981,10 @@ snapshots: '@eslint/core': 0.16.0 levn: 0.4.1 + '@heroicons/react@2.2.0(react@19.1.0)': + dependencies: + react: 19.1.0 + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.7': @@ -2023,6 +2159,66 @@ snapshots: '@nolyfill/is-core-module@1.0.39': {} + '@radix-ui/react-avatar@1.1.10(@types/react-dom@19.2.0(@types/react@19.2.0))(@types/react@19.2.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-context': 1.1.2(@types/react@19.2.0)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.0(@types/react@19.2.0))(@types/react@19.2.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.0)(react@19.1.0) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.0)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.0)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.0 + '@types/react-dom': 19.2.0(@types/react@19.2.0) + + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.0)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.2.0 + + '@radix-ui/react-context@1.1.2(@types/react@19.2.0)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.2.0 + + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.0(@types/react@19.2.0))(@types/react@19.2.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.0)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.0 + '@types/react-dom': 19.2.0(@types/react@19.2.0) + + '@radix-ui/react-slot@1.2.3(@types/react@19.2.0)(react@19.1.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.0)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.2.0 + + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.0)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.2.0 + + '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.0)(react@19.1.0)': + dependencies: + react: 19.1.0 + use-sync-external-store: 1.6.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.0 + + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.0)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.2.0 + '@rtsao/scc@1.1.0': {} '@rushstack/eslint-patch@1.13.0': {} @@ -2421,8 +2617,14 @@ snapshots: chownr@3.0.0: {} + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + client-only@0.0.1: {} + clsx@2.1.1: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -3177,6 +3379,10 @@ snapshots: dependencies: js-tokens: 4.0.0 + lucide-react@0.544.0(react@19.1.0): + dependencies: + react: 19.1.0 + magic-string@0.30.19: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -3580,6 +3786,8 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + tailwind-merge@3.3.1: {} + tailwindcss@4.1.14: {} tapable@2.3.0: {} @@ -3614,6 +3822,8 @@ snapshots: tslib@2.8.1: {} + tw-animate-css@1.4.0: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -3690,6 +3900,10 @@ snapshots: dependencies: punycode: 2.3.1 + use-sync-external-store@1.6.0(react@19.1.0): + dependencies: + react: 19.1.0 + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 diff --git a/src/app/globals.css b/src/app/globals.css index a2dc41e..7c279ef 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,26 +1,175 @@ @import "tailwindcss"; +@import "tw-animate-css"; -:root { - --background: #ffffff; - --foreground: #171717; -} +@custom-variant dark (&:is(.dark *)); @theme inline { --color-background: var(--background); --color-foreground: var(--foreground); --font-sans: var(--font-geist-sans); --font-mono: var(--font-geist-mono); + --color-sidebar-ring: var(--sidebar-ring); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar: var(--sidebar); + --color-chart-5: var(--chart-5); + --color-chart-4: var(--chart-4); + --color-chart-3: var(--chart-3); + --color-chart-2: var(--chart-2); + --color-chart-1: var(--chart-1); + --color-ring: var(--ring); + --color-input: var(--input); + --color-border: var(--border); + --color-destructive: var(--destructive); + --color-accent-foreground: var(--accent-foreground); + --color-accent: var(--accent); + --color-muted-foreground: var(--muted-foreground); + --color-muted: var(--muted); + --color-secondary-foreground: var(--secondary-foreground); + --color-secondary: var(--secondary); + --color-primary-foreground: var(--primary-foreground); + --color-primary: var(--primary); + --color-popover-foreground: var(--popover-foreground); + --color-popover: var(--popover); + --color-card-foreground: var(--card-foreground); + --color-card: var(--card); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); } -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; +:root { + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); :root { + --background: 202 35% 99%; + --foreground: 202 67% 2%; + --muted: 202 18% 87%; + --muted-foreground: 202 6% 35%; + --popover: 202 35% 99%; + --popover-foreground: 202 67% 2%; + --card: 202 35% 98%; + --card-foreground: 202 67% 1%; + --border: 202 13% 89%; + --input: 202 13% 89%; + --primary: 202 20% 61%; + --primary-foreground: 202 20% 1%; + --secondary: 202 19% 80%; + --secondary-foreground: 202 19% 20%; + --accent: 202 19% 80%; + --accent-foreground: 202 19% 20%; + --destructive: 12 100% 27%; + --destructive-foreground: 12 100% 87%; + --ring: 202 20% 61%; + --chart-1: 202 20% 61%; + --chart-2: 202 19% 80%; + --chart-3: 202 19% 80%; + --chart-4: 202 19% 83%; + --chart-5: 202 23% 61%; + --radius: 0.5rem; + } + + .dark { + --background: 202 50% 1%; + --foreground: 202 13% 100%; + --muted: 202 18% 13%; + --muted-foreground: 202 6% 65%; + --popover: 202 50% 1%; + --popover-foreground: 202 13% 100%; + --card: 202 50% 2%; + --card-foreground: 0 0% 100%; + --border: 202 13% 15%; + --input: 202 13% 15%; + --primary: 202 20% 61%; + --primary-foreground: 202 20% 1%; + --secondary: 202 20% 10%; + --secondary-foreground: 202 20% 70%; + --accent: 202 20% 10%; + --accent-foreground: 202 20% 70%; + --destructive: 12 100% 50%; + --destructive-foreground: 0 0% 100%; + --ring: 202 20% 61%; + --chart-1: 202 20% 61%; + --chart-2: 202 20% 10%; + --chart-3: 202 20% 10%; + --chart-4: 202 20% 13%; + --chart-5: 202 23% 61%; + } + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; } } - -body { - background: var(--background); - color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; -} diff --git a/src/app/page.tsx b/src/app/page.tsx index a932894..6453f24 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,103 +1,57 @@ -import Image from "next/image"; +// app/page.tsx +import Link from 'next/link'; +import { Button } from "@/components/ui/button"; +import { UserCard } from "@/components/user-card"; + +interface User { + id: number; + name: string; + email: string; + role: string; +} export default function Home() { - return ( -
-
- Next.js logo -
    -
  1. - Get started by editing{" "} - - src/app/page.tsx - - . -
  2. -
  3. - Save and see your changes instantly. -
  4. -
+ const user: User = { + id: 1, + name: "Jean Dupont", + email: "jean.dupont@example.com", + role: "DΓ©veloppeur" + }; -
- - Vercel logomark - Deploy now - - - Read our docs - + return ( +
+
+
+

Mon Application

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

React

+

Bibliothèque pour construire des interfaces utilisateur

+
+ +
+

NextJS

+

Framework pour applications web React

+
+ +
+

Tailwind

+

CSS utility-first framework

+
-
); -} +} \ No newline at end of file diff --git a/src/app/tuto1/page.tsx b/src/app/tuto1/page.tsx new file mode 100644 index 0000000..e78089d --- /dev/null +++ b/src/app/tuto1/page.tsx @@ -0,0 +1,14 @@ +// Composant fonctionnel +const Hello = () => { + return

Bonjour React!

; +}; + +// Composant avec props +const UserCard = ({ name, age }: { name: string; age: number }) => { + return ( +
+

{name}

+

Γ‚ge: {age}

+
+ ); +}; \ No newline at end of file diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx new file mode 100644 index 0000000..71e428b --- /dev/null +++ b/src/components/ui/avatar.tsx @@ -0,0 +1,53 @@ +"use client" + +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +function Avatar({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarImage({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarFallback({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx new file mode 100644 index 0000000..21409a0 --- /dev/null +++ b/src/components/ui/button.tsx @@ -0,0 +1,60 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: + "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2 has-[>svg]:px-3", + sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", + lg: "h-10 rounded-md px-6 has-[>svg]:px-4", + icon: "size-9", + "icon-sm": "size-8", + "icon-lg": "size-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +function Button({ + className, + variant, + size, + asChild = false, + ...props +}: React.ComponentProps<"button"> & + VariantProps & { + asChild?: boolean + }) { + const Comp = asChild ? Slot : "button" + + return ( + + ) +} + +export { Button, buttonVariants } diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx new file mode 100644 index 0000000..681ad98 --- /dev/null +++ b/src/components/ui/card.tsx @@ -0,0 +1,92 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +function Card({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardDescription({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardAction({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardContent({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardAction, + CardDescription, + CardContent, +} diff --git a/src/components/user-card.tsx b/src/components/user-card.tsx new file mode 100644 index 0000000..6835ef3 --- /dev/null +++ b/src/components/user-card.tsx @@ -0,0 +1,42 @@ +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { UserIcon, EnvelopeIcon } from "@heroicons/react/24/outline"; +import { UserIcon as UserIconSolid } from "@heroicons/react/24/solid"; + +interface User { + id: number; + name: string; + email: string; + role: string; +} + +interface UserCardProps { + user: User; +} + +export function UserCard({ user }: UserCardProps) { + return ( + + +
+ + {user.name.charAt(0)} + +
+ {user.name} +
+ + {user.role} +
+
+
+
+ +
+ + {user.email} +
+
+
+ ); +} \ No newline at end of file diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..bd0c391 --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +}