diff --git a/.gitignore b/.gitignore index 5ef6a52..62fcee8 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +/lib/generated/prisma diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..f3d19b5 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib", + "WillLuke.nextjs.addTypesOnSave": true, + "WillLuke.nextjs.hasPrompted": true +} diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 0000000..c55a45e --- /dev/null +++ b/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,3 @@ +import { handlers } from "@/lib/auth"; + +export const { GET, POST } = handlers; diff --git a/app/dashboard/_components/DashboardSidebar.tsx b/app/dashboard/_components/DashboardSidebar.tsx new file mode 100644 index 0000000..9900c12 --- /dev/null +++ b/app/dashboard/_components/DashboardSidebar.tsx @@ -0,0 +1,93 @@ +"use client"; + +import { + Book1Outlined, + Folder1Outlined, + PlusOutlined, +} from "@lineiconshq/free-icons"; +import { Lineicons } from "@lineiconshq/react-lineicons"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; + +const navItems = [ + { + href: "/dashboard", + label: "Tutoriels", + icon: Book1Outlined, + exact: true, + }, + { + href: "/dashboard/nouveau", + label: "Nouveau", + icon: PlusOutlined, + }, + { + href: "/dashboard/categories", + label: "Catégories", + icon: Folder1Outlined, + }, +]; + +export function DashboardSidebar() { + const pathname = usePathname(); + + const isActive = (href: string, exact?: boolean) => { + if (exact) return pathname === href; + return pathname.startsWith(href); + }; + + return ( + + ); +} diff --git a/app/dashboard/_components/tutorial-form.tsx b/app/dashboard/_components/tutorial-form.tsx new file mode 100644 index 0000000..57263d7 --- /dev/null +++ b/app/dashboard/_components/tutorial-form.tsx @@ -0,0 +1,524 @@ +"use client"; + +import { MarkdownRenderer } from "@/app/tutos/[slug]/markdown-renderer"; +import type { Category, Tutorial } from "@/lib/tutorials"; +import { + AlignTextLeftOutlined, + Books2Outlined, + CheckCircle1Outlined, + Code1Outlined, + EyeOutlined, + Folder1Outlined, + KeyboardOutlined, + Layers1Outlined, + Pencil1Outlined, + StopwatchOutlined, +} from "@lineiconshq/free-icons"; +import { Lineicons } from "@lineiconshq/react-lineicons"; +import { useCallback, useEffect, useRef, useState } from "react"; + +type TutorialFormProps = { + categories: Category[]; + action: (formData: FormData) => Promise; + submitLabel: string; + initialData?: Partial; +}; + +// Wrapper pour simplifier l'utilisation des icônes LineIcons +function Icon({ + icon, + size = 16, + className = "", +}: { + icon: React.ComponentType>; + size?: number; + className?: string; +}) { + return ; +} + +// Composant pour les labels avec icône +function FormLabel({ + icon, + children, + required, +}: { + icon: React.ComponentType>; + children: React.ReactNode; + required?: boolean; +}) { + return ( + + ); +} + +// Composant input stylisé +function FormInput({ + className = "", + ...props +}: React.InputHTMLAttributes) { + return ( + + ); +} + +// Composant select stylisé +function FormSelect({ + children, + className = "", + ...props +}: React.SelectHTMLAttributes) { + return ( + + ); +} + +// Composant textarea stylisé +function FormTextarea({ + className = "", + ...props +}: React.TextareaHTMLAttributes) { + return ( +