import { PrismaClient } from '@prisma/client' import { PrismaPg } from '@prisma/adapter-pg' import { betterAuth } from 'better-auth' import { emailOTP } from 'better-auth/plugins' const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL }) export const prisma = new PrismaClient({ adapter }) export const auth = betterAuth({ database: prismaAdapter(prisma), plugins: [ emailOTP({ otpLength: 6, expiresIn: 300, }), ], }) export async function getPublishedProjects() { return prisma.project.findMany({ where: { status: 'published' }, orderBy: [ { featured: 'desc' }, { sortOrder: 'asc' }, ], }) } "use server" export async function createProjectAction( data: FormData ) { const session = await auth.api .getSession({ headers }) if (!session) throw new Error() const validated = schema.parse(data) const result = await createProject(validated) revalidatePath("/admin/projects") revalidatePath("/") return result } const projectSchema = z.object({ title: z.string().min(1), slug: z.string().min(1), description: z.string().min(10), status: z.enum([ 'draft', 'published', 'archived' ]), techStack: z.array(z.string()), featured: z.boolean(), }) export default function RootLayout({ children, }: { children: React.ReactNode }) { return ( <html lang="fr"> <body> <ThemeProvider> <MotionProvider> {children} </MotionProvider> </ThemeProvider> </body> </html> ) }import { PrismaClient } from '@prisma/client' import { PrismaPg } from '@prisma/adapter-pg' import { betterAuth } from 'better-auth' import { emailOTP } from 'better-auth/plugins' const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL }) export const prisma = new PrismaClient({ adapter }) export const auth = betterAuth({ database: prismaAdapter(prisma), plugins: [ emailOTP({ otpLength: 6, expiresIn: 300, }), ], }) export async function getPublishedProjects() { return prisma.project.findMany({ where: { status: 'published' }, orderBy: [ { featured: 'desc' }, { sortOrder: 'asc' }, ], }) } "use server" export async function createProjectAction( data: FormData ) { const session = await auth.api .getSession({ headers }) if (!session) throw new Error() const validated = schema.parse(data) const result = await createProject(validated) revalidatePath("/admin/projects") revalidatePath("/") return result } const projectSchema = z.object({ title: z.string().min(1), slug: z.string().min(1), description: z.string().min(10), status: z.enum([ 'draft', 'published', 'archived' ]), techStack: z.array(z.string()), featured: z.boolean(), }) export default function RootLayout({ children, }: { children: React.ReactNode }) { return ( <html lang="fr"> <body> <ThemeProvider> <MotionProvider> {children} </MotionProvider> </ThemeProvider> </body> </html> ) }
"use client" import { m, useScroll } from 'motion/react' import { useReducedMotion } from 'motion/react' export function FadeIn({ children, delay = 0, direction = "up", }: FadeInProps) { const prefersReducedMotion = useReducedMotion() return ( <m.div initial={{ opacity: 0, y: 24 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.5, delay, ease: "easeOut" }} > {children} </m.div> ) } // Claude Code agent workflow const agent = defineAgent({ name: 'researcher', tools: [Glob, Grep, Read], prompt: 'Explore the codebase', }) await agent.run({ task: 'Find all API routes', maxTurns: 10, }) // Skill definition const skill = defineSkill({ name: 'deploy', steps: [ 'step-00-init.md', 'step-01-build.md', 'step-02-deploy.md', ], }) export function Hero({ profile }) { const name = profile?.name const nameParts = name.split(" ") const firstName = nameParts[0] const lastName = nameParts.at(-1) return ( <section className="min-h-svh"> <h1> {firstName}{" "} <span className="text-gradient"> {lastName} </span> </h1> <p>{profile?.tagline}</p> <Button asChild> <a href="#contact"> Discutons de votre projet </a> </Button> </section> ) } function useScrollProgress() { const { scrollYProgress } = useScroll() return useSpring(scrollYProgress, { stiffness: 100, damping: 30, }) } const config = defineConfig({ schema: "prisma/schema.prisma", datasource: { url: process.env.DATABASE_URL, }, })"use client" import { m, useScroll } from 'motion/react' import { useReducedMotion } from 'motion/react' export function FadeIn({ children, delay = 0, direction = "up", }: FadeInProps) { const prefersReducedMotion = useReducedMotion() return ( <m.div initial={{ opacity: 0, y: 24 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.5, delay, ease: "easeOut" }} > {children} </m.div> ) } // Claude Code agent workflow const agent = defineAgent({ name: 'researcher', tools: [Glob, Grep, Read], prompt: 'Explore the codebase', }) await agent.run({ task: 'Find all API routes', maxTurns: 10, }) // Skill definition const skill = defineSkill({ name: 'deploy', steps: [ 'step-00-init.md', 'step-01-build.md', 'step-02-deploy.md', ], }) export function Hero({ profile }) { const name = profile?.name const nameParts = name.split(" ") const firstName = nameParts[0] const lastName = nameParts.at(-1) return ( <section className="min-h-svh"> <h1> {firstName}{" "} <span className="text-gradient"> {lastName} </span> </h1> <p>{profile?.tagline}</p> <Button asChild> <a href="#contact"> Discutons de votre projet </a> </Button> </section> ) } function useScrollProgress() { const { scrollYProgress } = useScroll() return useSpring(scrollYProgress, { stiffness: 100, damping: 30, }) } const config = defineConfig({ schema: "prisma/schema.prisma", datasource: { url: process.env.DATABASE_URL, }, })
prapp.dev
Mon ParcoursStackProjetsServicesBlogContact

Mentions légales

Éditeur du site

Pierre Renollet
Entrepreneur individuel
Tenerife, Espagne
Contact : Réserver un appel

Hébergement

Ce site est hébergé par Vercel Inc.
440 N Barranca Avenue #4133, Covina, CA 91723, États-Unis

Propriété intellectuelle

L'ensemble du contenu de ce site (textes, images, code, design) est la propriété exclusive de Pierre Renollet, sauf mention contraire. Toute reproduction, même partielle, est interdite sans autorisation préalable.

Données personnelles

Ce site ne collecte aucune donnée personnelle des visiteurs. Aucun cookie de tracking n'est utilisé. Seul un cookie de session est utilisé pour l'authentification de l'administrateur.

Responsabilité

Pierre Renollet s'efforce de fournir des informations exactes et à jour sur ce site. Toutefois, il ne saurait être tenu responsable des erreurs, omissions ou résultats obtenus suite à l'utilisation de ces informations.

← Retour à l'accueil
prapp.dev
BlogMentions légales

© 2026 Pierre Renollet

Construit avec&Claude Code