Compare commits

...

2 Commits

Author SHA1 Message Date
1f77255a6b cgu + cgv et traduction 2026-03-07 00:36:31 +01:00
6a4a5ab543 ajout CGU CGV, bandeau cookie, font 2026-03-06 17:38:09 +01:00
19 changed files with 1101 additions and 73 deletions

View File

@@ -100,6 +100,7 @@ model Entity {
id String @id @default(cuid())
type String
name String
avatar String? @db.Text // Base64 image or URL
description String @default("")
details String @default("")
storyContext String?

11
scripts/test-prisma.ts Normal file
View File

@@ -0,0 +1,11 @@
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
console.log(Object.keys(prisma.entity.fields));
const e = await prisma.entity.findFirst();
console.log(e);
}
main().catch(console.error).finally(() => prisma.$disconnect());

View File

@@ -30,6 +30,7 @@ export async function PUT(
data: {
...(body.name !== undefined && { name: body.name }),
...(body.type !== undefined && { type: body.type }),
...(body.avatar !== undefined && { avatar: body.avatar }),
...(body.description !== undefined && { description: body.description }),
...(body.details !== undefined && { details: body.details }),
...(body.storyContext !== undefined && { storyContext: body.storyContext }),

View File

@@ -24,6 +24,7 @@ export async function POST(request: NextRequest) {
data: {
type: body.type,
name: body.name || 'Nouvelle entité',
avatar: body.avatar || null,
description: body.description || '',
details: body.details || '',
storyContext: body.storyContext || null,

View File

@@ -27,10 +27,148 @@ export default function CGUPage() {
</nav>
<main className="max-w-4xl mx-auto py-20 px-4 md:px-8">
<h1 className="text-4xl md:text-5xl font-black text-slate-900 mb-8 tracking-tight">{t('legal.cgu_title')}</h1>
<div className="bg-white p-6 sm:p-12 rounded-3xl shadow-xl border border-indigo-50 text-slate-600 leading-relaxed space-y-6">
<p>{t('legal.cgu_content')}</p>
<p><i>(Ceci est un document type en attente de la version finale par un conseiller juridique)</i></p>
<h1 className="text-4xl md:text-5xl font-black text-slate-900 mb-8 tracking-tight">{t('cgu.title')}</h1>
<div className="bg-white p-6 sm:p-12 rounded-3xl shadow-xl border border-indigo-50 text-slate-600 leading-relaxed space-y-8">
<div>
<p className="font-semibold text-slate-500 mb-6">{t('cgu.version')}</p>
<h2 className="text-xl font-bold text-slate-900 mb-4 uppercase tracking-wider">{t('cgu.preamble_title')}</h2>
<p className="mb-4">
{t('cgu.preamble_text1')}
</p>
<p>
{t('cgu.preamble_text2')}
</p>
</div>
<div>
<h2 className="text-xl font-bold text-slate-900 mb-4 uppercase tracking-wider">{t('cgu.art1_title')}</h2>
<ul className="list-disc pl-5 space-y-2">
<li>{t('cgu.art1_user')}</li>
<li>{t('cgu.art1_service')}</li>
<li>{t('cgu.art1_content')}</li>
<li>{t('cgu.art1_prompt')}</li>
</ul>
</div>
<div>
<h2 className="text-xl font-bold text-slate-900 mb-4 uppercase tracking-wider">{t('cgu.art2_title')}</h2>
<p>
{t('cgu.art2_text')}
</p>
</div>
<div>
<h2 className="text-xl font-bold text-slate-900 mb-4 uppercase tracking-wider">{t('cgu.art3_title')}</h2>
<h3 className="font-bold text-slate-800 mt-4 mb-2">{t('cgu.art3_1_title')}</h3>
<p className="mb-2">
{t('cgu.art3_1_text')}
</p>
<ul className="list-disc pl-5 space-y-2 mb-4">
<li>{t('cgu.art3_1_sub1')}</li>
<li>{t('cgu.art3_1_sub2')}</li>
</ul>
<h3 className="font-bold text-slate-800 mt-4 mb-2">{t('cgu.art3_2_title')}</h3>
<p>
{t('cgu.art3_2_text')}
</p>
</div>
<div>
<h2 className="text-xl font-bold text-slate-900 mb-4 uppercase tracking-wider">{t('cgu.art4_title')}</h2>
<p className="mb-4">{t('cgu.art4_text')}</p>
<h3 className="font-bold text-slate-800 mt-4 mb-2">{t('cgu.art4_1_title')}</h3>
<p className="mb-2">{t('cgu.art4_1_text')}</p>
<ul className="list-disc pl-5 space-y-2 mb-4">
<li>{t('cgu.art4_1_item1')}</li>
<li>{t('cgu.art4_1_item2')}</li>
<li>{t('cgu.art4_1_item3')}</li>
</ul>
<h3 className="font-bold text-slate-800 mt-4 mb-2">{t('cgu.art4_2_title')}</h3>
<p>
{t('cgu.art4_2_text')}
</p>
</div>
<div>
<h2 className="text-xl font-bold text-slate-900 mb-4 uppercase tracking-wider">{t('cgu.art5_title')}</h2>
<h3 className="font-bold text-slate-800 mt-4 mb-2">{t('cgu.art5_1_title')}</h3>
<p className="mb-2">
{t('cgu.art5_1_text')}
</p>
<div className="bg-slate-50 p-4 rounded-xl border border-slate-200 tex-sm my-4">
{t('cgu.art5_1_note')}
</div>
<h3 className="font-bold text-slate-800 mt-4 mb-2">{t('cgu.art5_2_title')}</h3>
<p>
{t('cgu.art5_2_text')}
</p>
</div>
<div>
<h2 className="text-xl font-bold text-slate-900 mb-4 uppercase tracking-wider">{t('cgu.art6_title')}</h2>
<h3 className="font-bold text-slate-800 mt-4 mb-2">{t('cgu.art6_1_title')}</h3>
<p className="mb-2">
{t('cgu.art6_1_text1')}
</p>
<p className="mb-2">
{t('cgu.art6_1_text2')}
</p>
<p className="mb-4">
{t('cgu.art6_1_text3')}
</p>
<h3 className="font-bold text-slate-800 mt-4 mb-2">{t('cgu.art6_2_title')}</h3>
<p>
{t('cgu.art6_2_text')}
</p>
</div>
<div>
<h2 className="text-xl font-bold text-slate-900 mb-4 uppercase tracking-wider">{t('cgu.art7_title')}</h2>
<h3 className="font-bold text-slate-800 mt-4 mb-2">{t('cgu.art7_1_title')}</h3>
<p className="mb-4">
{t('cgu.art7_1_text')}
</p>
<h3 className="font-bold text-slate-800 mt-4 mb-2">{t('cgu.art7_2_title')}</h3>
<p>
{t('cgu.art7_2_text')}
</p>
</div>
<div>
<h2 className="text-xl font-bold text-slate-900 mb-4 uppercase tracking-wider">{t('cgu.art8_title')}</h2>
<p>
{t('cgu.art8_text')}
</p>
</div>
<div>
<h2 className="text-xl font-bold text-slate-900 mb-4 uppercase tracking-wider">{t('cgu.art9_title')}</h2>
<h3 className="font-bold text-slate-800 mt-4 mb-2">{t('cgu.art9_1_title')}</h3>
<p className="mb-4">
{t('cgu.art9_1_text')}
</p>
<h3 className="font-bold text-slate-800 mt-4 mb-2">{t('cgu.art9_2_title')}</h3>
<p>
{t('cgu.art9_2_text')}
</p>
</div>
<div>
<h2 className="text-xl font-bold text-slate-900 mb-4 uppercase tracking-wider">{t('cgu.art10_title')}</h2>
<p>
{t('cgu.art10_text')}
</p>
</div>
</div>
</main>
</div>

View File

@@ -27,10 +27,114 @@ export default function CGVPage() {
</nav>
<main className="max-w-4xl mx-auto py-20 px-4 md:px-8">
<h1 className="text-4xl md:text-5xl font-black text-slate-900 mb-8 tracking-tight">{t('legal.cgv_title')}</h1>
<div className="bg-white p-6 sm:p-12 rounded-3xl shadow-xl border border-indigo-50 text-slate-600 leading-relaxed space-y-6">
<p>{t('legal.cgv_content')}</p>
<p><i>(Ceci est un document type en attente de la version finale par un conseiller juridique)</i></p>
<h1 className="text-4xl md:text-5xl font-black text-slate-900 mb-8 tracking-tight">{t('cgv.title')}</h1>
<div className="bg-white p-6 sm:p-12 rounded-3xl shadow-xl border border-indigo-50 text-slate-600 leading-relaxed space-y-8">
<div>
<p className="font-semibold text-slate-500 mb-6">{t('cgv.version')}</p>
<h2 className="text-xl font-bold text-slate-900 mb-4 uppercase tracking-wider">{t('cgv.art1_title')}</h2>
<p className="mb-4">
{t('cgv.art1_text1')}
</p>
<p>
{t('cgv.art1_text2')}
</p>
</div>
<div>
<h2 className="text-xl font-bold text-slate-900 mb-4 uppercase tracking-wider">{t('cgv.art2_title')}</h2>
<p className="mb-2">{t('cgv.art2_text')}</p>
<ul className="list-disc pl-5 space-y-2 mb-4">
<li>{t('cgv.art2_item1')}</li>
<li>{t('cgv.art2_item2')}</li>
</ul>
</div>
<div>
<h2 className="text-xl font-bold text-slate-900 mb-4 uppercase tracking-wider">{t('cgv.art3_title')}</h2>
<p>
{t('cgv.art3_text')}
</p>
</div>
<div>
<h2 className="text-xl font-bold text-slate-900 mb-4 uppercase tracking-wider">{t('cgv.art4_title')}</h2>
<h3 className="font-bold text-slate-800 mt-4 mb-2">{t('cgv.art4_1_title')}</h3>
<p className="mb-4">
{t('cgv.art4_1_text')}
</p>
<h3 className="font-bold text-slate-800 mt-4 mb-2">{t('cgv.art4_2_title')}</h3>
<p className="mb-2">{t('cgv.art4_2_text')}</p>
<ul className="list-disc pl-5 space-y-2 mb-4">
<li>{t('cgv.art4_2_item1')}</li>
<li>{t('cgv.art4_2_item2')}</li>
</ul>
</div>
<div>
<h2 className="text-xl font-bold text-slate-900 mb-4 uppercase tracking-wider">{t('cgv.art5_title')}</h2>
<h3 className="font-bold text-slate-800 mt-4 mb-2">{t('cgv.art5_1_title')}</h3>
<p className="mb-4">
{t('cgv.art5_1_text')}
</p>
<h3 className="font-bold text-slate-800 mt-4 mb-2">{t('cgv.art5_2_title')}</h3>
<p className="mb-2">{t('cgv.art5_2_text')}</p>
<ul className="list-disc pl-5 space-y-2 mb-4">
<li>{t('cgv.art5_2_item1')}</li>
<li>{t('cgv.art5_2_item2')}</li>
</ul>
</div>
<div>
<h2 className="text-xl font-bold text-slate-900 mb-4 uppercase tracking-wider">{t('cgv.art6_title')}</h2>
<p className="mb-4">
{t('cgv.art6_text')}
</p>
<div className="bg-amber-50 text-amber-900 p-4 rounded-xl border border-amber-200 tex-sm my-4 font-medium">
{t('cgv.art6_warning')}
</div>
</div>
<div>
<h2 className="text-xl font-bold text-slate-900 mb-4 uppercase tracking-wider">{t('cgv.art7_title')}</h2>
<p>
{t('cgv.art7_text')}
</p>
</div>
<div>
<h2 className="text-xl font-bold text-slate-900 mb-4 uppercase tracking-wider">{t('cgv.art8_title')}</h2>
<h3 className="font-bold text-slate-800 mt-4 mb-2">{t('cgv.art8_1_title')}</h3>
<p className="mb-4">
{t('cgv.art8_1_text')}
</p>
<h3 className="font-bold text-slate-800 mt-4 mb-2">{t('cgv.art8_2_title')}</h3>
<p>
{t('cgv.art8_2_text')}
</p>
</div>
<div>
<h2 className="text-xl font-bold text-slate-900 mb-4 uppercase tracking-wider">{t('cgv.art9_title')}</h2>
<p className="mb-2">{t('cgv.art9_text')}</p>
<ul className="list-disc pl-5 space-y-2 mb-4">
<li>{t('cgv.art9_item1')}</li>
<li>{t('cgv.art9_item2')}</li>
</ul>
</div>
<div>
<h2 className="text-xl font-bold text-slate-900 mb-4 uppercase tracking-wider">{t('cgv.art10_title')}</h2>
<p>
{t('cgv.art10_text')}
</p>
</div>
</div>
</main>
</div>

View File

@@ -3,6 +3,9 @@
@theme {
--font-sans: 'Inter', sans-serif;
--font-serif: 'Merriweather', serif;
--font-lora: 'Lora', serif;
--font-garamond: 'EB Garamond', serif;
--font-playfair: 'Playfair Display', serif;
--color-paper: #fcfbf7;
/* Global Theme Colors */

View File

@@ -1,8 +1,10 @@
import type { Metadata } from "next";
import { Inter, Merriweather } from "next/font/google";
import { Inter, Merriweather, Lora, EB_Garamond, Playfair_Display } from "next/font/google";
import Script from "next/script";
import { AuthProvider } from "@/providers/AuthProvider";
import { LanguageProvider } from "@/providers/LanguageProvider";
import { CookieBanner } from "@/components/CookieBanner";
import { Analytics } from "@/components/Analytics";
import "./globals.css";
const inter = Inter({
@@ -16,6 +18,21 @@ const merriweather = Merriweather({
variable: "--font-serif",
});
const lora = Lora({
subsets: ["latin"],
variable: "--font-lora",
});
const garamond = EB_Garamond({
subsets: ["latin"],
variable: "--font-garamond",
});
const playfair = Playfair_Display({
subsets: ["latin"],
variable: "--font-playfair",
});
export const metadata: Metadata = {
title: "Pluume - Éditeur Intelligent",
description: "Votre assistant éditorial intelligent propulsé par l'IA pour écrire votre prochain roman.",
@@ -29,17 +46,13 @@ export default function RootLayout({
return (
<html lang="en">
<head>
<Script
defer
src="https://stats.kaelstudio.tech/script.js"
data-website-id="bce265f0-c9d4-4542-813f-f3bd9bf151bc"
strategy="afterInteractive"
/>
</head>
<body className={`${inter.variable} ${merriweather.variable} font-sans h-screen overflow-x-hidden overflow-y-auto antialiased bg-theme-bg text-theme-text transition-colors duration-300`}>
<body className={`${inter.variable} ${merriweather.variable} ${lora.variable} ${garamond.variable} ${playfair.variable} font-sans h-screen overflow-x-hidden overflow-y-auto antialiased bg-theme-bg text-theme-text transition-colors duration-300`}>
<AuthProvider>
<LanguageProvider>
<Analytics />
{children}
<CookieBanner />
</LanguageProvider>
</AuthProvider>
</body>

View File

@@ -0,0 +1,40 @@
export async function GET() {
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://pluume.fr';
// Dates can be dynamically fetched in a real scenario, here we use current date for static
const currentDate = new Date().toISOString();
const staticRoutes = [
'',
'/features',
'/pricing',
'/login',
'/signup',
'/cgu',
'/cgv',
'/sitemap',
];
const sitemapEntries = staticRoutes.map((route) => {
const priority = route === '' ? '1.0' : '0.8';
const changeFrequency = route === '' ? 'weekly' : 'monthly';
return `
<url>
<loc>${baseUrl}${route}</loc>
<lastmod>${currentDate}</lastmod>
<changefreq>${changeFrequency}</changefreq>
<priority>${priority}</priority>
</url>`;
}).join('');
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${sitemapEntries}
</urlset>`;
return new Response(xml, {
headers: {
'Content-Type': 'application/xml',
},
});
}

View File

@@ -1,17 +1,110 @@
'use client';
import React from 'react';
import React, { useState } from 'react';
import { useLanguage } from '@/providers/LanguageProvider';
import { ArrowLeft, Book, Link as LinkIcon } from 'lucide-react';
import { ArrowLeft, Book, Link as LinkIcon, ChevronDown, Shield, Home, Component, LogIn, Mail } from 'lucide-react';
import Link from 'next/link';
import { LanguageSwitcher } from '@/components/LanguageSwitcher';
interface SitemapLink {
title: string;
href: string;
icon: React.ElementType;
}
interface SitemapCategory {
id: string;
title: string;
color: string;
icon: React.ElementType;
links: SitemapLink[];
}
const SitemapCard = ({ category, defaultOpen = false }: { category: SitemapCategory, defaultOpen?: boolean }) => {
const [isOpen, setIsOpen] = useState(defaultOpen);
const Icon = category.icon;
return (
<div className="flex flex-col rounded-xl shadow-sm border border-slate-200 bg-white transition-all hover:shadow-md overflow-hidden">
{/* Colored Top Bar */}
<div className="h-2 w-full" style={{ backgroundColor: category.color }} />
{/* Header (Toggle) */}
<button
onClick={() => setIsOpen(!isOpen)}
className="flex items-center justify-between p-4 w-full text-left bg-white hover:bg-slate-50 transition-colors focus:outline-none"
>
<div className="flex items-center gap-3">
<div className="p-2 rounded-lg" style={{ backgroundColor: `${category.color}15`, color: category.color }}>
<Icon size={20} />
</div>
<span className="font-bold text-slate-800 text-lg">{category.title}</span>
</div>
<div className={`p-1 rounded-full text-slate-400 hover:text-slate-600 transition-transform duration-300 ${isOpen ? 'rotate-180' : ''}`}>
<ChevronDown size={20} />
</div>
</button>
{/* Foldable Content */}
<div
className={`grid transition-all duration-300 ease-in-out ${isOpen ? 'grid-rows-[1fr] opacity-100' : 'grid-rows-[0fr] opacity-0'}`}
>
<div className="overflow-hidden">
<div className="p-4 pt-0 border-t border-slate-100 bg-slate-50/50">
<ul className="space-y-2 mt-3">
{category.links.map((link, idx) => {
const LinkIconComponent = link.icon || LinkIcon;
return (
<li key={idx}>
<Link
href={link.href}
className="flex items-center gap-3 px-3 py-2 rounded-lg text-slate-600 hover:text-indigo-600 hover:bg-indigo-50/50 transition-all font-medium group"
>
<LinkIconComponent size={16} className="text-slate-400 group-hover:text-indigo-500 transition-colors" />
{link.title}
</Link>
</li>
);
})}
</ul>
</div>
</div>
</div>
</div>
);
};
export default function SitemapPage() {
const { t } = useLanguage();
const categories: SitemapCategory[] = [
{
id: 'general',
title: 'Navigation Principale',
color: '#3b82f6', // blue-500
icon: Home,
links: [
{ title: 'Accueil', href: '/', icon: Home },
{ title: 'Fonctionnalités', href: '/#features', icon: Component },
{ title: 'Authentification', href: '/auth', icon: LogIn },
{ title: 'Contact', href: 'mailto:contact@pluu.me', icon: Mail },
]
},
{
id: 'legal',
title: 'Informations Légales',
color: '#f59e0b', // amber-500
icon: Shield,
links: [
{ title: t('legal.cgu_title') || 'Conditions Générales d\'Utilisation', href: '/cgu', icon: Book },
{ title: t('legal.cgv_title') || 'Conditions Générales de Vente', href: '/cgv', icon: Book },
]
}
];
return (
<div className="min-h-screen bg-[#eef2ff] font-sans selection:bg-blue-200">
<nav className="bg-white/80 backdrop-blur-md z-50 border-b border-indigo-100 px-8 h-16 flex items-center justify-between sticky top-0">
<nav className="bg-white/80 backdrop-blur-md z-50 border-b border-indigo-100 px-4 md:px-8 h-16 flex items-center justify-between sticky top-0">
<Link href="/" className="flex items-center gap-2 hover:opacity-80 transition-opacity">
<div className="bg-blue-600 p-1.5 rounded-lg">
<Book className="text-white" size={24} />
@@ -26,36 +119,16 @@ export default function SitemapPage() {
</div>
</nav>
<main className="max-w-4xl mx-auto py-20 px-8">
<h1 className="text-4xl md:text-5xl font-black text-slate-900 mb-8 tracking-tight">{t('legal.sitemap_title')}</h1>
<div className="bg-white p-8 sm:p-12 rounded-3xl shadow-xl border border-indigo-50">
<ul className="space-y-4">
<li>
<Link href="/" className="flex items-center gap-3 text-lg font-bold text-slate-700 hover:text-blue-600 transition-colors">
<LinkIcon size={18} className="text-slate-400" /> Accueil
</Link>
</li>
<li>
<Link href="/auth" className="flex items-center gap-3 text-lg font-bold text-slate-700 hover:text-blue-600 transition-colors">
<LinkIcon size={18} className="text-slate-400" /> Authentification
</Link>
</li>
<li className="pt-4 mt-4 border-t border-slate-100">
<span className="text-xs font-black uppercase text-slate-400 tracking-widest block mb-4">Légal</span>
<ul className="space-y-4 pl-4">
<li>
<Link href="/cgu" className="flex items-center gap-3 text-base text-slate-600 hover:text-blue-600 transition-colors">
<LinkIcon size={16} className="text-slate-400" /> {t('legal.cgu_title')}
</Link>
</li>
<li>
<Link href="/cgv" className="flex items-center gap-3 text-base text-slate-600 hover:text-blue-600 transition-colors">
<LinkIcon size={16} className="text-slate-400" /> {t('legal.cgv_title')}
</Link>
</li>
</ul>
</li>
</ul>
<main className="max-w-4xl mx-auto py-12 md:py-20 px-4 md:px-8">
<div className="text-center mb-12">
<h1 className="text-4xl md:text-5xl font-black text-slate-900 mb-4 tracking-tight">{t('legal.sitemap_title') || 'Plan du site'}</h1>
<p className="text-slate-500 text-lg">Retrouvez facilement toutes les pages de l'application Pluume.</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{categories.map((cat, idx) => (
<SitemapCard key={cat.id} category={cat} defaultOpen={true} />
))}
</div>
</main>
</div>

View File

@@ -0,0 +1,36 @@
'use client';
import React, { useEffect, useState } from 'react';
import Script from 'next/script';
export const Analytics = () => {
const [hasConsent, setHasConsent] = useState(false);
const checkConsent = () => {
const consent = localStorage.getItem('cookie-consent');
setHasConsent(consent === 'accepted');
};
useEffect(() => {
// Check initial state
checkConsent();
// Listen for updates from CookieBanner
window.addEventListener('cookie-consent-updated', checkConsent);
return () => {
window.removeEventListener('cookie-consent-updated', checkConsent);
};
}, []);
if (!hasConsent) return null;
return (
<Script
defer
src="https://stats.kaelstudio.tech/script.js"
data-website-id="bce265f0-c9d4-4542-813f-f3bd9bf151bc"
strategy="afterInteractive"
/>
);
};

View File

@@ -16,6 +16,7 @@ const AuthPage: React.FC<AuthPageProps> = ({ onBack, onSuccess, initialMode = 's
const [loading, setLoading] = useState(false);
const [formData, setFormData] = useState({ name: '', email: '', password: '' });
const [error, setError] = useState('');
const [cguAccepted, setCguAccepted] = useState(false);
// On récupère les fonctions de connexion directement du hook
const { user, login, signup } = useAuthContext();
@@ -156,6 +157,38 @@ const AuthPage: React.FC<AuthPageProps> = ({ onBack, onSuccess, initialMode = 's
</div>
)}
{mode === 'signup' && (
<div className="pt-2 pb-1">
<label className="flex items-start gap-3 cursor-pointer group">
<div className="relative flex items-center mt-1 text-slate-900 font-bold mb-3">
<input
type="checkbox"
required
checked={cguAccepted}
onChange={(e) => setCguAccepted(e.target.checked)}
className="peer shrink-0 appearance-none w-5 h-5 border-2 border-slate-300 rounded-md bg-white checked:bg-blue-600 checked:border-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-all cursor-pointer"
/>
<svg
className="absolute w-5 h-5 pointer-events-none opacity-0 peer-checked:opacity-100 peer-checked:text-white transition-opacity text-white stroke-white fill-none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="3"
strokeLinecap="round"
strokeLinejoin="round"
>
<polyline points="20 6 9 17 4 12" />
</svg>
</div>
<span className="text-sm text-slate-600 font-medium leading-relaxed group-hover:text-slate-800 transition-colors">
{t('auth.accept_cgu')}
<a href="/cgu" target="_blank" rel="noopener noreferrer" className="text-blue-600 font-bold hover:underline hover:text-blue-700">
{t('auth.cgu_link')}
</a>
</span>
</label>
</div>
)}
<button
type="submit"
data-umami-event={mode === 'signin' ? "Login" : mode === 'signup' ? "Signup" : "Send Reset"}

View File

@@ -0,0 +1,72 @@
'use client';
import React, { useState, useEffect } from 'react';
import { X, ShieldCheck } from 'lucide-react';
export const CookieBanner = () => {
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
const consent = localStorage.getItem('cookie-consent');
if (!consent) {
setIsVisible(true);
}
}, []);
const handleAccept = () => {
localStorage.setItem('cookie-consent', 'accepted');
setIsVisible(false);
// Dispatch an event so Analytics component can react immediately without refresh
window.dispatchEvent(new Event('cookie-consent-updated'));
};
const handleDecline = () => {
localStorage.setItem('cookie-consent', 'declined');
setIsVisible(false);
window.dispatchEvent(new Event('cookie-consent-updated'));
};
if (!isVisible) return null;
return (
<div className="fixed bottom-0 left-0 right-0 z-[100] p-4 md:p-6 bg-slate-900/95 backdrop-blur shadow-2xl border-t border-slate-700/50 transform transition-transform duration-500 ease-out translate-y-0">
<div className="max-w-7xl mx-auto flex flex-col md:flex-row items-start md:items-center justify-between gap-4">
<div className="flex items-start gap-4">
<div className="p-3 bg-blue-500/20 text-blue-400 rounded-full shrink-0">
<ShieldCheck size={24} />
</div>
<div>
<h3 className="text-white font-bold text-lg mb-1">
Nous respectons votre vie privée
</h3>
<p className="text-slate-300 text-sm leading-relaxed max-w-3xl">
Nous utilisons des cookies (via Umami Analytics) exclusivement pour analyser le trafic de manière anonymisée et améliorer votre expérience sur l'application. Aucun parcours n'est lié à votre identité.
</p>
</div>
</div>
<div className="flex flex-col sm:flex-row w-full md:w-auto items-center gap-3 shrink-0">
<button
onClick={handleDecline}
className="w-full sm:w-auto px-6 py-2.5 rounded-xl border border-slate-600 text-slate-300 font-semibold hover:bg-slate-800 transition-colors text-sm"
>
Continuer sans accepter
</button>
<button
onClick={handleAccept}
className="w-full sm:w-auto px-6 py-2.5 rounded-xl bg-blue-600 text-white font-bold shadow-lg shadow-blue-600/20 hover:bg-blue-500 hover:-translate-y-0.5 transition-all text-sm"
>
Accepter
</button>
<button
onClick={() => setIsVisible(false)}
className="absolute top-4 right-4 md:hidden text-slate-400 p-2"
aria-label="Fermer"
>
<X size={20} />
</button>
</div>
</div>
</div>
);
};

View File

@@ -40,6 +40,10 @@ const RichTextEditor = forwardRef<RichTextEditorHandle, RichTextEditorProps>(({
const scrollContainerRef = useRef<HTMLDivElement>(null);
const [isFocused, setIsFocused] = useState(false);
// Appearance State
const [editorFont, setEditorFont] = useState('font-serif');
const [showFontMenu, setShowFontMenu] = useState(false);
// Auto-Save State
const [saveStatus, setSaveStatus] = useState<'saved_local' | 'saved_db' | 'saving' | 'unsaved'>('saved_db');
const saveTimeoutRef = useRef<NodeJS.Timeout | null>(null);
@@ -198,6 +202,20 @@ const RichTextEditor = forwardRef<RichTextEditorHandle, RichTextEditorProps>(({
}
}, [initialContent, editorId]);
// Load saved font preference
useEffect(() => {
const savedFont = localStorage.getItem('rte_font_preference');
if (savedFont) {
setEditorFont(savedFont);
}
}, []);
const handleFontChange = (fontClass: string) => {
setEditorFont(fontClass);
localStorage.setItem('rte_font_preference', fontClass);
setShowFontMenu(false);
};
// Flush pending save on unmount
useEffect(() => {
return () => {
@@ -405,6 +423,36 @@ const RichTextEditor = forwardRef<RichTextEditorHandle, RichTextEditorProps>(({
<ToolbarButton icon={AlignRight} cmd="justifyRight" label="Aligner à droite" />
<div className="w-px h-6 bg-slate-300 mx-1" />
<ToolbarButton icon={List} cmd="insertUnorderedList" label="Liste" />
<div className="w-px h-6 bg-slate-300 mx-1" />
{/* Font Selector */}
<div className="relative">
<button
onClick={() => setShowFontMenu(!showFontMenu)}
className="flex items-center gap-2 px-2 py-1.5 text-xs font-semibold text-slate-600 hover:bg-slate-200 rounded transition-colors"
title="Choisir la police d'écriture"
>
<div className="flex items-center gap-1">
<span className="opacity-70">A</span>
<span className="font-bold">Aa</span>
</div>
<ChevronDown size={14} className="opacity-50" />
</button>
{showFontMenu && (
<>
<div className="fixed inset-0 z-40 bg-transparent" onClick={() => setShowFontMenu(false)} />
<div className="absolute top-full mt-1 left-0 w-48 bg-white shadow-xl border border-slate-200 rounded-lg p-1.5 z-50 animate-in fade-in zoom-in-95 duration-100 flex flex-col gap-1">
<div className="px-2 py-1 text-[10px] font-bold text-slate-400 uppercase tracking-wider mb-1">Polices</div>
<button onClick={() => handleFontChange('font-sans')} className={`text-left px-3 py-2 text-sm rounded transition-colors font-sans ${editorFont === 'font-sans' ? 'bg-indigo-50 text-indigo-700' : 'hover:bg-slate-50 text-slate-700'}`}>Classique Sans (Inter)</button>
<button onClick={() => handleFontChange('font-serif')} className={`text-left px-3 py-2 text-sm rounded transition-colors font-serif ${editorFont === 'font-serif' ? 'bg-indigo-50 text-indigo-700' : 'hover:bg-slate-50 text-slate-700'}`}>Classique Serif (Merriweather)</button>
<button onClick={() => handleFontChange('font-lora')} className={`text-left px-3 py-2 text-sm rounded transition-colors font-lora ${editorFont === 'font-lora' ? 'bg-indigo-50 text-indigo-700' : 'hover:bg-slate-50 text-slate-700'}`} style={{ fontFamily: 'var(--font-lora)' }}>Littéraire (Lora)</button>
<button onClick={() => handleFontChange('font-garamond')} className={`text-left px-3 py-2 text-sm rounded transition-colors font-garamond ${editorFont === 'font-garamond' ? 'bg-indigo-50 text-indigo-700' : 'hover:bg-slate-50 text-slate-700'}`} style={{ fontFamily: 'var(--font-garamond)' }}>Ancienne (EB Garamond)</button>
<button onClick={() => handleFontChange('font-playfair')} className={`text-left px-3 py-2 text-sm rounded transition-colors font-playfair ${editorFont === 'font-playfair' ? 'bg-indigo-50 text-indigo-700' : 'hover:bg-slate-50 text-slate-700'}`} style={{ fontFamily: 'var(--font-playfair)' }}>Élégante (Playfair)</button>
</div>
</>
)}
</div>
<div className="flex-1" />
@@ -439,7 +487,7 @@ const RichTextEditor = forwardRef<RichTextEditorHandle, RichTextEditorProps>(({
suppressContentEditableWarning
spellCheck={true}
lang="fr-FR"
className="bg-theme-editor-bg shadow-sm w-[800px] min-h-[1000px] p-12 outline-none font-serif text-lg leading-relaxed text-theme-editor-text editor-content transition-colors duration-300"
className={`bg-theme-editor-bg shadow-sm w-[800px] min-h-[1000px] p-12 outline-none text-lg leading-relaxed text-theme-editor-text editor-content transition-all duration-300 ${editorFont}`}
onInput={handleInput}
onBlur={() => { setIsFocused(false); saveSelection(); }}
onFocus={() => setIsFocused(true)}

View File

@@ -3,7 +3,7 @@
import React, { useState, useMemo, useEffect } from 'react';
import { useLanguage } from '@/providers/LanguageProvider';
import { Entity, EntityType, CharacterAttributes, EntityTemplate, CustomFieldDefinition, CustomFieldType } from '@/lib/types';
import { Plus, Trash2, Save, X, Sparkles, User, Activity, Brain, Ruler, Settings, Layout, List, ToggleLeft } from 'lucide-react';
import { Plus, Trash2, Save, X, Sparkles, User, Activity, Brain, Ruler, Settings, Layout, List, ToggleLeft, Camera } from 'lucide-react';
import { ENTITY_ICONS, ENTITY_COLORS, HAIR_COLORS, EYE_COLORS, ARCHETYPES } from '@/lib/constants';
interface WorldBuilderProps {
@@ -146,6 +146,18 @@ const WorldBuilder: React.FC<WorldBuilderProps> = ({ entities, onCreate, onUpdat
}
};
const handleAvatarUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file || !tempEntity) return;
const reader = new FileReader();
reader.onload = (event) => {
const base64 = event.target?.result as string;
setTempEntity({ ...tempEntity, avatar: base64 });
};
reader.readAsDataURL(file);
};
// --- TEMPLATE ACTIONS ---
const addCustomField = (type: EntityType) => {
@@ -614,8 +626,19 @@ const WorldBuilder: React.FC<WorldBuilderProps> = ({ entities, onCreate, onUpdat
className={`p-3 cursor-pointer hover:bg-blue-500/10 transition-colors flex justify-between group ${editingId === entity.id ? 'bg-blue-500/10 border-l-4 border-blue-500' : ''}`}
>
<div>
<div className="font-medium text-theme-text">{entity.name}</div>
<div className="text-xs text-theme-muted truncate">{entity.description}</div>
<div className="flex items-center gap-3">
{entity.avatar ? (
<img src={entity.avatar} alt={entity.name} className="w-8 h-8 rounded-full object-cover border border-theme-border shadow-sm" />
) : (
<div className={`w-8 h-8 rounded-full flex items-center justify-center font-bold text-xs ${ENTITY_COLORS[entity.type]} shadow-sm`}>
{entity.name.substring(0, 2).toUpperCase()}
</div>
)}
<div>
<div className="font-medium text-theme-text">{entity.name}</div>
<div className="text-xs text-theme-muted truncate">{entity.description}</div>
</div>
</div>
</div>
<button
onClick={(e) => { e.stopPropagation(); handleDelete(entity.id); }}
@@ -651,25 +674,46 @@ const WorldBuilder: React.FC<WorldBuilderProps> = ({ entities, onCreate, onUpdat
</div>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-theme-text mb-1">{t('wb.name')}</label>
<input
type="text"
value={tempEntity.name}
onChange={e => setTempEntity({ ...tempEntity, name: e.target.value })}
className="w-full p-2 bg-theme-bg border border-theme-border rounded focus:ring-2 focus:ring-blue-500 outline-none font-serif text-lg"
placeholder={t('wb.name_ph')}
/>
</div>
<div className="flex flex-col md:flex-row gap-6">
<div className="flex flex-col items-center gap-2">
<label className="relative group cursor-pointer">
<input type="file" accept="image/*" onChange={handleAvatarUpload} className="hidden" />
{tempEntity.avatar ? (
<img src={tempEntity.avatar} alt="Avatar" className="w-24 h-24 rounded-2xl object-cover shadow-md border-2 border-theme-border group-hover:border-indigo-400 transition-all" />
) : (
<div className="w-24 h-24 rounded-2xl bg-slate-100 flex items-center justify-center border-2 border-dashed border-slate-300 group-hover:border-indigo-400 group-hover:bg-indigo-50 transition-all text-slate-400">
<Camera size={32} />
</div>
)}
<div className="absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 rounded-2xl flex items-center justify-center transition-opacity">
<Camera size={24} className="text-white" />
</div>
</label>
<span className="text-xs font-semibold text-theme-muted text-center">Avatar / Plan</span>
</div>
<div>
<label className="block text-sm font-medium text-theme-text mb-1">{t('wb.short_desc')}</label>
<textarea
value={tempEntity.description}
onChange={e => setTempEntity({ ...tempEntity, description: e.target.value })}
className="w-full p-2 bg-theme-bg border border-theme-border rounded focus:ring-2 focus:ring-blue-500 outline-none text-sm h-20"
placeholder={t('wb.short_desc_ph')}
/>
<div className="flex-1 space-y-4">
<div>
<label className="block text-sm font-medium text-theme-text mb-1">{t('wb.name')}</label>
<input
type="text"
value={tempEntity.name}
onChange={e => setTempEntity({ ...tempEntity, name: e.target.value })}
className="w-full p-2 bg-theme-bg border border-theme-border rounded focus:ring-2 focus:ring-blue-500 outline-none font-serif text-lg"
placeholder={t('wb.name_ph')}
/>
</div>
<div>
<label className="block text-sm font-medium text-theme-text mb-1">{t('wb.short_desc')}</label>
<textarea
value={tempEntity.description}
onChange={e => setTempEntity({ ...tempEntity, description: e.target.value })}
className="w-full p-2 bg-theme-bg border border-theme-border rounded focus:ring-2 focus:ring-blue-500 outline-none text-sm h-20"
placeholder={t('wb.short_desc_ph')}
/>
</div>
</div>
</div>
{tempEntity.type === EntityType.CHARACTER && renderCharacterEditor()}

View File

@@ -89,6 +89,7 @@ export const useProjects = (user: UserProfile | null) => {
id: e.id,
type: e.type,
name: e.name,
avatar: e.avatar,
description: e.description,
details: e.details,
storyContext: e.storyContext,
@@ -264,6 +265,7 @@ export const useProjects = (user: UserProfile | null) => {
projectId,
type,
name: initialData?.name || `Nouveau ${type}`,
avatar: initialData?.avatar || undefined,
description: initialData?.description || '',
details: initialData?.details || '',
attributes: initialData?.attributes || undefined,
@@ -278,6 +280,7 @@ export const useProjects = (user: UserProfile | null) => {
id: newEntity.id,
type: newEntity.type,
name: newEntity.name,
avatar: newEntity.avatar,
description: newEntity.description,
details: newEntity.details,
attributes: newEntity.attributes,

View File

@@ -124,7 +124,7 @@ const api = {
// --- ENTITIES ---
entities: {
async create(data: { projectId: string; type: string; name?: string; description?: string; details?: string; attributes?: any; customValues?: any }) {
async create(data: { projectId: string; type: string; name?: string; avatar?: string; description?: string; details?: string; attributes?: any; customValues?: any }) {
return api.request<any>('/entities', {
method: 'POST',
body: JSON.stringify(data),

View File

@@ -164,6 +164,8 @@ export const translations = {
'auth.signup_link': "S'inscrire",
'auth.signin_link': 'Se connecter',
'auth.back_to_site': '← Revenir au site',
'auth.accept_cgu': "J'ai lu et j'accepte les ",
'auth.cgu_link': "Conditions Générales d'Utilisation",
'auth.hero_title_part1': "L'endroit où vos",
'auth.hero_title_part2': "histoires",
'auth.hero_title_part3': "prennent vie.",
@@ -331,6 +333,106 @@ export const translations = {
'pov_options.multi-points_de_vue_(alterné)': 'Multi-points de vue (Alterné)',
'tense_options.passé_(passé_simple_/_imparfait)': 'Passé (Passé simple / Imparfait)',
'tense_options.présent_de_narration': 'Présent de narration',
// CGU Page
'cgu.title': "Conditions Générales d'Utilisation (CGU)",
'cgu.version': "Version en vigueur au : 06/03/2026",
'cgu.preamble_title': "Préambule",
'cgu.preamble_text1': "La présente plateforme (ci-après \"la Plateforme\"), accessible à l'adresse https://pluu.me, est éditée par Pluume. La Plateforme propose un service de génération de contenus textuels assisté par l'intelligence artificielle (technologie Google Gemini) destiné à la création d'ebooks.",
'cgu.preamble_text2': "Les présentes Conditions Générales d'Utilisation (CGU) ont pour objet de définir les règles d'accès et d'utilisation du service. Tout accès ou utilisation de la Plateforme suppose l'acceptation sans réserve de l'intégralité des présentes conditions.",
'cgu.art1_title': "Article 1 : Définitions",
'cgu.art1_user': "Utilisateur : Toute personne physique ou morale accédant à la Plateforme.",
'cgu.art1_service': "Service : Ensemble des outils de génération de texte, de structuration et d'exportation d'ebooks mis à disposition.",
'cgu.art1_content': "Contenu Généré : Textes, plans ou documents produits par l'IA suite aux instructions de l'Utilisateur.",
'cgu.art1_prompt': "Prompt : Instructions textuelles saisies par l'Utilisateur pour diriger l'IA.",
'cgu.art2_title': "Article 2 : Accès et Inscription",
'cgu.art2_text': "L'accès à la Plateforme est réservé aux personnes majeures. Pour utiliser les services de génération, l'Utilisateur doit créer un compte. Il est responsable de la confidentialité de ses identifiants.",
'cgu.art3_title': "Article 3 : Conditions Financières (Abonnements et Crédits)",
'cgu.art3_1_title': "3.1 Tarifs",
'cgu.art3_1_text': "L'accès aux fonctionnalités de génération est soumis à une tarification consultable sur la page Tarifs. Les prix sont exprimés en Euros TTC.",
'cgu.art3_1_sub1': "Abonnements : Prélèvement périodique automatique.",
'cgu.art3_1_sub2': "Packs de crédits : Achat ponctuel pour un nombre défini de générations.",
'cgu.art3_2_title': "3.2 Paiement et Sécurité",
'cgu.art3_2_text': "Les paiements sont traités par un prestataire de paiement sécurisé. La Plateforme ne stocke aucune coordonnée bancaire.",
'cgu.art4_title': "Article 4 : Obligations de l'Utilisateur et Sécurité (Clause Stricte)",
'cgu.art4_text': "L'Utilisateur s'engage à utiliser le Service de manière licite.",
'cgu.art4_1_title': "4.1 Contenus Interdits",
'cgu.art4_1_text': "Il est formellement interdit d'utiliser la Plateforme pour générer des contenus :",
'cgu.art4_1_item1': "Pédopornographiques (CSAM) : Toute tentative de génération, requête ou diffusion de contenu lié à l'exploitation sexuelle des mineurs fera l'objet d'un bannissement immédiat sans préavis ni remboursement. Conformément à la loi, la Plateforme procédera à un signalement systématique auprès des autorités compétentes.",
'cgu.art4_1_item2': "Haineux et Discriminatoires : Incitation à la violence, au racisme, à l'antisémitisme, à l'homophobie ou toute forme de discrimination.",
'cgu.art4_1_item3': "Illégaux : Apologie de crimes, terrorisme, ou violation de droits de propriété intellectuelle tiers.",
'cgu.art4_2_title': "4.2 Surveillance et Modération",
'cgu.art4_2_text': "La Plateforme utilise des algorithmes de filtrage en temps réel. En cas de violation répétée ou grave de cet article, la Plateforme se réserve le droit de suspendre le compte de l'Utilisateur de plein droit.",
'cgu.art5_title': "Article 5 : Propriété Intellectuelle",
'cgu.art5_1_title': "5.1 Droits de l'Utilisateur",
'cgu.art5_1_text': "La Plateforme concède à l'Utilisateur, sous réserve du paiement des frais éventuels, la pleine propriété des droits d'exploitation sur les ebooks générés.",
'cgu.art5_1_note': "Note sur l'IA : L'Utilisateur est informé que la protection par le droit d'auteur des œuvres générées par IA peut varier selon les législations nationales et nécessite souvent une intervention créative humaine significative de la part de l'Utilisateur.",
'cgu.art5_2_title': "5.2 Droits de la Plateforme",
'cgu.art5_2_text': "L'interface, les algorithmes de connexion à l'API Gemini et l'identité visuelle du site restent la propriété exclusive de l'Éditeur.",
'cgu.art6_title': "Article 6 : Politique de \"Zéro Stockage\" et Confidentialité",
'cgu.art6_1_title': "6.1 Traitement Éphémère",
'cgu.art6_1_text1': "La Plateforme applique une politique stricte de confidentialité. Aucune donnée de création (prompts, textes générés, ebooks en cours) n'est sauvegardée sur nos serveurs de manière permanente.",
'cgu.art6_1_text2': "Les données ne sont conservées que le temps de la session de travail pour permettre l'affichage et l'exportation.",
'cgu.art6_1_text3': "Quoi qu'il arrive, si l'Utilisateur décide de supprimer ses informations, son projet ou ferme sa session, toutes les données associées sont immédiatement et irréversiblement supprimées de nos bases de données.",
'cgu.art6_2_title': "6.2 Responsabilité de Sauvegarde",
'cgu.art6_2_text': "En raison de cette politique de non-conservation, il appartient exclusivement à l'Utilisateur de télécharger et de sauvegarder ses travaux (format PDF, EPUB, etc.) avant la clôture de sa session. La Plateforme ne pourra être tenue responsable d'une perte de données consécutive à une déconnexion ou une suppression volontaire.",
'cgu.art7_title': "Article 7 : Limitation de Responsabilité",
'cgu.art7_1_title': "7.1 Qualité du Contenu",
'cgu.art7_1_text': "Le Service utilise la technologie Google Gemini. L'Utilisateur accepte que l'IA puisse produire des informations inexactes, incomplètes ou biaisées (\"hallucinations\"). La Plateforme ne saurait être tenue responsable du contenu des ebooks ou de l'utilisation qui en est faite.",
'cgu.art7_2_title': "7.2 Disponibilité Technique",
'cgu.art7_2_text': "La Plateforme ne peut garantir une disponibilité ininterrompue du service, celle-ci dépendant de prestataires tiers (hébergement et API Google).",
'cgu.art8_title': "Article 8 : Protection des Données Personnelles (RGPD)",
'cgu.art8_text': "Les seules données conservées sont celles strictement nécessaires à la gestion du compte (email, facturation). L'Utilisateur dispose d'un droit d'accès, de rectification et de suppression totale de ses données personnelles.",
'cgu.art9_title': "Article 9 : Modification et Résiliation",
'cgu.art9_1_title': "9.1 Modification des CGU",
'cgu.art9_1_text': "La Plateforme se réserve le droit de modifier les présentes CGU à tout moment. L'Utilisateur sera informé de toute modification substantielle.",
'cgu.art9_2_title': "9.2 Résiliation",
'cgu.art9_2_text': "L'Utilisateur peut supprimer son compte à tout moment. Cette action entraîne la suppression immédiate de toutes ses données, conformément à l'Article 6.",
'cgu.art10_title': "Article 10 : Loi Applicable et Juridiction",
'cgu.art10_text': "Les présentes CGU sont soumises au droit français. Tout litige relatif à leur interprétation ou exécution relève de la compétence exclusive des tribunaux compétents.",
// CGV Page
'cgv.title': "Conditions Générales de Vente (CGV)",
'cgv.version': "Version en vigueur au : 06/03/2026",
'cgv.art1_title': "Article 1 : Objet et Champ d'Application",
'cgv.art1_text1': "Les présentes Conditions Générales de Vente (CGV) s'appliquent, sans restriction ni réserve, à tout achat des services de génération d'ebooks (ci-après \"les Services\") proposés par Pluume sur la plateforme https://pluu.me.",
'cgv.art1_text2': "Ces CGV encadrent les modalités de commande, de paiement, de livraison des crédits/abonnements et de gestion des éventuels litiges entre le Vendeur et l'Utilisateur (ci-après \"le Client\").",
'cgv.art2_title': "Article 2 : Caractéristiques des Services",
'cgv.art2_text': "Le Vendeur propose des solutions de création d'ebooks assistées par l'intelligence artificielle Google Gemini. Les offres sont divisées en deux catégories :",
'cgv.art2_item1': "Packs de Crédits : Achat ponctuel d'un volume défini de générations de contenu.",
'cgv.art2_item2': "Abonnements : Accès illimité ou plafonné aux services pour une durée déterminée (mensuelle ou annuelle) avec renouvellement automatique.",
'cgv.art3_title': "Article 3 : Commande et Validation",
'cgv.art3_text': "Le Client sélectionne l'offre de son choix sur la Plateforme. La commande est considérée comme définitive dès la validation du paiement par le prestataire tiers. Un e-mail de confirmation est envoyé au Client à l'adresse renseignée lors de la création du compte.",
'cgv.art4_title': "Article 4 : Conditions Financières",
'cgv.art4_1_title': "4.1 Tarifs",
'cgv.art4_1_text': "Les prix sont indiqués en Euros et s'entendent toutes taxes comprises (TTC). Le Vendeur se réserve le droit de modifier ses tarifs à tout moment, mais les services seront facturés sur la base des tarifs en vigueur au moment de l'enregistrement de la commande.",
'cgv.art4_2_title': "4.2 Modalités de paiement (Stripe)",
'cgv.art4_2_text': "Le paiement s'effectue exclusivement par carte bancaire via le système de paiement sécurisé Stripe.",
'cgv.art4_2_item1': "Le Vendeur n'a jamais accès aux coordonnées bancaires du Client.",
'cgv.art4_2_item2': "Stripe garantit la confidentialité et la sécurité des transactions grâce au protocole SSL et à la conformité PCI-DSS.",
'cgv.art5_title': "Article 5 : Gestion des Abonnements",
'cgv.art5_1_title': "5.1 Renouvellement automatique",
'cgv.art5_1_text': "Tout abonnement souscrit est à tacite reconduction. Le Client autorise Stripe à prélever le montant de l'abonnement à chaque échéance (mois ou année) sur la carte bancaire utilisée lors de l'achat initial.",
'cgv.art5_2_title': "5.2 Résiliation",
'cgv.art5_2_text': "Le Client peut résilier son abonnement à tout moment et sans frais depuis son espace personnel.",
'cgv.art5_2_item1': "La résiliation interrompt le renouvellement automatique mais laisse l'accès aux services actif jusqu'à la fin de la période en cours.",
'cgv.art5_2_item2': "Aucun remboursement pro rata n'est effectué pour la période restant à courir entre la date de résiliation et la fin de l'échéance.",
'cgv.art6_title': "Article 6 : Droit de Rétractation (Produits Numériques)",
'cgv.art6_text': "Conformément à l'article L221-28 du Code de la consommation, le droit de rétractation de 14 jours ne s'applique pas aux contrats de fourniture de contenu numérique non fourni sur un support matériel dont l'exécution a commencé après accord préalable exprès du consommateur.",
'cgv.art6_warning': "En procédant au paiement et en utilisant son premier crédit de génération, le Client accepte expressément l'exécution immédiate du service et renonce à son droit de rétractation. Aucun remboursement ne sera accordé une fois le service consommé.",
'cgv.art7_title': "Article 7 : Livraison et Accès",
'cgv.art7_text': "Les Services sont réputés \"livrés\" dès que les crédits sont ajoutés au compte du Client ou que l'accès premium est débloqué. En cas de problème technique empêchant l'accès immédiat, le Client doit contacter le support.",
'cgv.art8_title': "Article 8 : Garanties et Responsabilité Commerciale",
'cgv.art8_1_title': "8.1 Qualité de l'IA",
'cgv.art8_1_text': "Le Vendeur vend un accès à un outil de génération. Il ne garantit pas la qualité littéraire, l'exactitude des faits ou l'originalité absolue du texte produit par l'IA. Par conséquent, aucune demande de remboursement ne pourra être motivée par une \"déception\" quant au style de l'IA.",
'cgv.art8_2_title': "8.2 Continuité de service",
'cgv.art8_2_text': "Le Vendeur s'engage à faire ses meilleurs efforts pour maintenir l'accès au service, mais ne pourra être tenu responsable des pannes dues aux prestataires tiers (Stripe, API Google Gemini).",
'cgv.art9_title': "Article 9 : Politique de Suppression des Données",
'cgv.art9_text': "Comme stipulé dans les CGU, le Vendeur applique une politique de zéro stockage.",
'cgv.art9_item1': "Si le Client supprime ses informations ou son compte, toutes les données de facturation sont archivées uniquement pour la durée légale fiscale, mais les contenus créés sont supprimés irréversiblement.",
'cgv.art9_item2': "Le Client ne pourra prétendre à un dédommagement en cas de perte de données suite à une suppression volontaire.",
'cgv.art10_title': "Article 10 : Litiges et Loi Applicable",
'cgv.art10_text': "Les présentes CGV sont soumises au droit français. En cas de litige, une solution amiable sera recherchée avant toute action judiciaire. À défaut d'accord, compétence exclusive est attribuée aux tribunaux compétents.",
},
en: {
// General Navigation
@@ -495,6 +597,8 @@ export const translations = {
'auth.signup_link': 'Sign up',
'auth.signin_link': 'Log in',
'auth.back_to_site': '← Back to site',
'auth.accept_cgu': "I have read and agree to the ",
'auth.cgu_link': "Terms of Service",
'auth.hero_title_part1': "The place where your",
'auth.hero_title_part2': "stories",
'auth.hero_title_part3': "come to life.",
@@ -662,6 +766,104 @@ export const translations = {
'pov_options.multi-points_de_vue_(alterné)': 'Multi-POV (Alternating)',
'tense_options.passé_(passé_simple_/_imparfait)': 'Past Tense',
'tense_options.présent_de_narration': 'Present Tense',
// CGU Page
'cgu.title': "Terms of Service (ToS)",
'cgu.version': "Effective as of: 2026-03-06",
'cgu.preamble_title': "Preamble",
'cgu.preamble_text1': "This platform (hereinafter \"the Platform\"), accessible at https://pluu.me, is published by Pluume. The Platform offers a text generation service assisted by artificial intelligence (Google Gemini technology) intended for creating ebooks.",
'cgu.preamble_text2': "These Terms of Service (ToS) aim to define the rules for access and use of the service. Any access or use of the Platform implies unreserved acceptance of all these conditions.",
'cgu.art1_title': "Article 1: Definitions",
'cgu.art1_user': "User: Any natural or legal person accessing the Platform.",
'cgu.art1_service': "Service: All tools for text generation, structuring, and ebook exportation provided.",
'cgu.art1_content': "Generated Content: Texts, plans, or documents produced by the AI following User instructions.",
'cgu.art1_prompt': "Prompt: Textual instructions entered by the User to direct the AI.",
'cgu.art2_title': "Article 2: Access and Registration",
'cgu.art2_text': "Access to the Platform is reserved for adults. To use the generation services, the User must create an account. They are responsible for the confidentiality of their credentials.",
'cgu.art3_title': "Article 3: Financial Conditions (Subscriptions and Credits)",
'cgu.art3_1_title': "3.1 Rates",
'cgu.art3_1_text': "Access to generation features is subject to pricing available on the Pricing page. Prices are expressed in Euros including all taxes.",
'cgu.art3_1_sub1': "Subscriptions: Automatic periodic withdrawal.",
'cgu.art3_1_sub2': "Credit Packs: One-time purchase for a defined number of generations.",
'cgu.art3_2_title': "3.2 Payment and Security",
'cgu.art3_2_text': "Payments are processed by a secure payment provider. The Platform does not store any bank details.",
'cgu.art4_title': "Article 4: User Obligations and Security (Strict Clause)",
'cgu.art4_text': "The User agrees to use the Service lawfully.",
'cgu.art4_1_title': "4.1 Prohibited Content",
'cgu.art4_1_text': "It is strictly forbidden to use the Platform to generate content that is:",
'cgu.art4_1_item1': "Child Sexual Abuse Material (CSAM): Any attempt to generate, request, or disseminate content related to the sexual exploitation of minors will result in an immediate ban without notice or refund. In accordance with the law, the Platform will proceed with a systematic report to the competent authorities.",
'cgu.art4_1_item2': "Hateful and Discriminatory: Incitement to violence, racism, anti-Semitism, homophobia, or any form of discrimination.",
'cgu.art4_1_item3': "Illegal: Apology for crimes, terrorism, or violation of third-party intellectual property rights.",
'cgu.art4_2_title': "4.2 Monitoring and Moderation",
'cgu.art4_2_text': "The Platform uses real-time filtering algorithms. In case of repeated or serious violation of this article, the Platform reserves the right to suspend the User's account as of right.",
'cgu.art5_title': "Article 5: Intellectual Property",
'cgu.art5_1_title': "5.1 User Rights",
'cgu.art5_1_text': "The Platform grants the User, subject to payment of any fees, full ownership of the exploitation rights to the generated ebooks.",
'cgu.art5_1_note': "Note on AI: The User is informed that copyright protection for AI-generated works may vary by national legislation and often requires significant creative human intervention from the User.",
'cgu.art5_2_title': "5.2 Platform Rights",
'cgu.art5_2_text': "The interface, connection algorithms to the Gemini API, and the visual identity of the site remain the exclusive property of the Publisher.",
'cgu.art6_title': "Article 6: \"Zero Storage\" Policy and Confidentiality",
'cgu.art6_1_title': "6.1 Ephemeral Processing",
'cgu.art6_1_text1': "The Platform applies a strict privacy policy. No creation data (prompts, generated texts, ebooks in progress) is saved on our servers permanently.",
'cgu.art6_1_text2': "Data is only kept for the duration of the work session to allow for display and export.",
'cgu.art6_1_text3': "Whatever happens, if the User decides to delete their information, their project, or closes their session, all associated data is immediately and irreversibly deleted from our databases.",
'cgu.art6_2_title': "6.2 Backup Responsibility",
'cgu.art6_2_text': "Due to this non-retention policy, it is the User's exclusive responsibility to download and save their work (PDF, EPUB, etc.) before the end of their session. The Platform cannot be held responsible for data loss following a disconnection or voluntary deletion.",
'cgu.art7_title': "Article 7: Limitation of Liability",
'cgu.art7_1_title': "7.1 Content Quality",
'cgu.art7_1_text': "The Service uses Google Gemini technology. The User accepts that the AI may produce inaccurate, incomplete, or biased information (\"hallucinations\"). The Platform cannot be held responsible for the content of the ebooks or the use made of them.",
'cgu.art7_2_title': "7.2 Technical Availability",
'cgu.art7_2_text': "The Platform cannot guarantee uninterrupted availability of the service, as it depends on third-party providers (hosting and Google API).",
'cgu.art8_title': "Article 8: Personal Data Protection (GDPR)",
'cgu.art8_text': "The only data kept are those strictly necessary for account management (email, billing). The User has a right of access, rectification, and total deletion of their personal data.",
'cgu.art9_title': "Article 9: Modification and Termination",
'cgu.art9_1_title': "9.1 ToS Modification",
'cgu.art9_1_text': "The Platform reserves the right to modify these ToS at any time. The User will be informed of any substantial modification.",
'cgu.art9_2_title': "9.2 Termination",
'cgu.art9_2_text': "The User can delete their account at any time. This action results in the immediate deletion of all their data, in accordance with Article 6.",
'cgu.art10_title': "Article 10: Applicable Law and Jurisdiction",
'cgu.art10_text': "These ToS are subject to French law. Any dispute relating to their interpretation or execution falls under the exclusive competence of the competent courts.",
'cgv.title': "General Terms of Sale (ToS)",
'cgv.version': "Effective as of: 2026-03-06",
'cgv.art1_title': "Article 1: Purpose and Scope",
'cgv.art1_text1': "These General Terms of Sale (ToS) apply, without restriction or reservation, to any purchase of ebook generation services (hereinafter \"the Services\") offered by Pluume on the platform https://pluu.me.",
'cgv.art1_text2': "These ToS frame the terms of ordering, payment, delivery of credits/subscriptions, and management of possible disputes between the Seller and the User (hereinafter \"the Client\").",
'cgv.art2_title': "Article 2: Characteristics of the Services",
'cgv.art2_text': "The Seller offers ebook creation solutions assisted by Google Gemini artificial intelligence. The offers are divided into two categories:",
'cgv.art2_item1': "Credit Packs: One-time purchase of a defined volume of content generations.",
'cgv.art2_item2': "Subscriptions: Unlimited or capped access to services for a determined duration (monthly or annual) with automatic renewal.",
'cgv.art3_title': "Article 3: Order and Validation",
'cgv.art3_text': "The Client selects the offer of their choice on the Platform. The order is considered final upon validation of the payment by the third-party provider. A confirmation email is sent to the Client at the address provided during account creation.",
'cgv.art4_title': "Article 4: Financial Conditions",
'cgv.art4_1_title': "4.1 Rates",
'cgv.art4_1_text': "Prices are indicated in Euros and are inclusive of all taxes (TTC). The Seller reserves the right to modify its rates at any time, but the services will be invoiced based on the rates in effect at the time of order registration.",
'cgv.art4_2_title': "4.2 Payment Terms (Stripe)",
'cgv.art4_2_text': "Payment is made exclusively by credit card via the Stripe secure payment system.",
'cgv.art4_2_item1': "The Seller never has access to the Client's bank details.",
'cgv.art4_2_item2': "Stripe guarantees the confidentiality and security of transactions thanks to the SSL protocol and PCI-DSS compliance.",
'cgv.art5_title': "Article 5: Subscription Management",
'cgv.art5_1_title': "5.1 Automatic Renewal",
'cgv.art5_1_text': "Any subscription taken out is with tacit renewal. The Client authorizes Stripe to withdraw the subscription amount at each due date (month or year) from the credit card used during the initial purchase.",
'cgv.art5_2_title': "5.2 Termination",
'cgv.art5_2_text': "The Client can terminate their subscription at any time and free of charge from their personal space.",
'cgv.art5_2_item1': "Termination interrupts automatic renewal but leaves access to services active until the end of the current period.",
'cgv.art5_2_item2': "No pro rata refund is made for the period remaining between the termination date and the end of the period.",
'cgv.art6_title': "Article 6: Right of Withdrawal (Digital Products)",
'cgv.art6_text': "In accordance with article L221-28 of the Consumer Code, the 14-day right of withdrawal does not apply to contracts for the supply of digital content not provided on a tangible medium whose execution has begun after the consumer's express prior agreement.",
'cgv.art6_warning': "By proceeding with payment and using their first generation credit, the Client expressly accepts the immediate execution of the service and waives their right of withdrawal. No refund will be granted once the service is consumed.",
'cgv.art7_title': "Article 7: Delivery and Access",
'cgv.art7_text': "The Services are deemed \"delivered\" as soon as the credits are added to the Client's account or premium access is unlocked. In case of a technical problem preventing immediate access, the Client should contact support.",
'cgv.art8_title': "Article 8: Guarantees and Commercial Liability",
'cgv.art8_1_title': "8.1 AI Quality",
'cgv.art8_1_text': "The Seller sells access to a generation tool. It does not guarantee literary quality, factual accuracy, or absolute originality of the text produced by the AI. Therefore, no refund request can be motivated by a \"disappointment\" regarding the AI's style.",
'cgv.art8_2_title': "8.2 Continuity of Service",
'cgv.art8_2_text': "The Seller agrees to make its best efforts to maintain access to the service but cannot be held responsible for failures due to third-party providers (Stripe, Google Gemini API).",
'cgv.art9_title': "Article 9: Data Deletion Policy",
'cgv.art9_text': "As stipulated in the ToS, the Seller applies a zero storage policy.",
'cgv.art9_item1': "If the Client deletes their info or account, all billing data is archived only for the legal fiscal duration, but created content is irreversibly deleted.",
'cgv.art9_item2': "The Client cannot claim compensation for data loss following a voluntary deletion.",
'cgv.art10_title': "Article 10: Disputes and Applicable Law",
'cgv.art10_text': "These ToS are subject to French law. In case of dispute, an amicable solution will be sought before any legal action. Failing agreement, exclusive competence is attributed to the competent courts.",
},
es: {
// General Navigation
@@ -826,6 +1028,8 @@ export const translations = {
'auth.signup_link': 'Regístrate',
'auth.signin_link': 'Iniciar sesión',
'auth.back_to_site': '← Volver al sitio',
'auth.accept_cgu': "He leído y acepto los ",
'auth.cgu_link': "Términos de Servicio",
'auth.hero_title_part1': "El lugar donde tus",
'auth.hero_title_part2': "historias",
'auth.hero_title_part3': "cobran vida.",
@@ -993,6 +1197,106 @@ export const translations = {
'pov_options.multi-points_de_vue_(alterné)': 'Múltiples POV (Alternando)',
'tense_options.passé_(passé_simple_/_imparfait)': 'Tiempo Pasado',
'tense_options.présent_de_narration': 'Tiempo Presente',
// CGU Page
'cgu.title': "Condiciones Generales de Uso (CGU)",
'cgu.version': "Versión vigente al: 06/03/2026",
'cgu.preamble_title': "Preámbulo",
'cgu.preamble_text1': "La presente plataforma (en adelante \"la Plataforma\"), accesible en https://pluu.me, es editada por Pluume. La Plataforma ofrece un servicio de generación de contenidos textuales asistido por inteligencia artificial (tecnología Google Gemini) destinado a la creación de ebooks.",
'cgu.preamble_text2': "Las presentes Condiciones Generales de Uso (CGU) tienen por objeto definir las reglas de acceso y uso del servicio. Todo acceso o uso de la Plataforma supone la aceptación sin reserva de la totalidad de las presentes condiciones.",
'cgu.art1_title': "Artículo 1: Definiciones",
'cgu.art1_user': "Usuario: Cualquier persona física o jurídica que acceda a la Plataforma.",
'cgu.art1_service': "Servicio: Conjunto de herramientas de generación de texto, estructuración y exportación de ebooks puestas a disposición.",
'cgu.art1_content': "Contenido Generado: Textos, esquemas o documentos producidos por la IA siguiendo las instrucciones del Usuario.",
'cgu.art1_prompt': "Prompt: Instrucciones textuales introducidas por el Usuario para dirigir a la IA.",
'cgu.art2_title': "Artículo 2: Acceso y Registro",
'cgu.art2_text': "El acceso a la Plataforma está reservado a personas mayores de edad. Para utilizar los servicios de generación, el Usuario debe crear una cuenta. Es responsable de la confidencialidad de sus credenciales.",
'cgu.art3_title': "Artículo 3: Condiciones Financieras (Suscripciones y Créditos)",
'cgu.art3_1_title': "3.1 Tarifas",
'cgu.art3_1_text': "El acceso a las funcionalidades de generación está sujeto a una tarificación consultable en la página de Tarifas. Los precios se expresan en Euros con todos los impuestos incluidos.",
'cgu.art3_1_sub1': "Suscripciones: Cargo periódico automático.",
'cgu.art3_1_sub2': "Packs de créditos: Compra puntual de un volumen definido de generaciones.",
'cgu.art3_2_title': "3.2 Pago y Seguridad",
'cgu.art3_2_text': "Los pagos son procesados por un proveedor de pagos seguro. La Plataforma no almacena ningún dato bancario.",
'cgu.art4_title': "Artículo 4: Obligaciones del Usuario y Seguridad (Cláusula Estricta)",
'cgu.art4_text': "El Usuario se compromete a utilizar el Servicio de manera lícita.",
'cgu.art4_1_title': "4.1 Contenidos Prohibidos",
'cgu.art4_1_text': "Está formalmente prohibido utilizar la Plataforma para generar contenidos:",
'cgu.art4_1_item1': "Pornografía Infantil (CSAM): Cualquier intento de generación, solicitud o difusión de contenido relacionado con la explotación sexual de menores resultará en una expulsión inmediata sin previo aviso ni reembolso. De acuerdo con la ley, la Plataforma procederá a informar sistemáticamente a las autoridades competentes.",
'cgu.art4_1_item2': "Odiosos y Discriminatorios: Incitación a la violencia, al racismo, al antisemitismo, al homophobia o cualquier forma de discriminación.",
'cgu.art4_1_item3': "Ilegales: Apología de crímenes, terrorismo o violación de derechos de propiedad intelectual de terceros.",
'cgu.art4_2_title': "4.2 Vigilancia y Moderación",
'cgu.art4_2_text': "La Plataforma utiliza algoritmos de filtrado en tiempo real. En caso de violación repetida o grave de este artículo, la Plataforma se reserva el derecho de suspender la cuenta del Usuario por pleno derecho.",
'cgu.art5_title': "Artículo 5: Propiedad Intelectual",
'cgu.art5_1_title': "5.1 Derechos del Usuario",
'cgu.art5_1_text': "La Plataforma concede al Usuario, sujeto al pago de las tasas correspondientes, la plena propiedad de los derechos de explotación sobre los ebooks generados.",
'cgu.art5_1_note': "Nota sobre la IA: Se informa al Usuario que la protección por derechos de autor de las obras generadas por IA puede variar según las legislaciones nacionales y a menudo requiere una intervención creativa humana significativa por parte del Usuario.",
'cgu.art5_2_title': "5.2 Derechos de la Plataforma",
'cgu.art5_2_text': "La interfaz, los algoritmos de conexión a la API Gemini y la identidad visual del sitio siguen siendo propiedad exclusiva del Editor.",
'cgu.art6_title': "Artículo 6: Política de \"Cero Almacenamiento\" y Confidencialidad",
'cgu.art6_1_title': "6.1 Tratamiento Efímero",
'cgu.art6_1_text1': "La Plataforma aplica una política estricta de confidencialidad. Ningún dato de creación (prompts, textos generados, ebooks en curso) se guarda en nuestros servidores de manera permanente.",
'cgu.art6_1_text2': "Los datos solo se conservan durante el tiempo de la sesión de trabajo para permitir la visualización y exportación.",
'cgu.art6_1_text3': "Pase lo que pase, si el Usuario decide eliminar su información, su proyecto o cierra su sesión, todos los datos asociados se eliminan inmediata e irreversiblemente de nuestras bases de datos.",
'cgu.art6_2_title': "6.2 Responsabilidad de Copia de Seguridad",
'cgu.art6_2_text': "Debido a esta política de no retención, es responsabilidad exclusiva del Usuario descargar y guardar sus trabajos (formato PDF, EPUB, etc.) antes del cierre de su sesión. La Plataforma no podrá ser considerada responsable de una pérdida de datos tras una desconexión o eliminación voluntaria.",
'cgu.art7_title': "Artículo 7: Limitación de Responsabilidad",
'cgu.art7_1_title': "7.1 Calidad del Contenido",
'cgu.art7_1_text': "El Servicio utiliza la tecnología Google Gemini. El Usuario acepta que la IA pueda producir informaciones inexactas, incompletas o sesgadas (\"alucinaciones\"). La Plataforma no será responsable del contenido de los ebooks ni del uso que se haga de ellos.",
'cgu.art7_2_title': "7.2 Disponibilidad Técnica",
'cgu.art7_2_text': "La Plataforma no puede garantizar la disponibilidad ininterrumpida del servicio, ya que depende de terceros proveedores (alojamiento y API Google).",
'cgu.art8_title': "Artículo 8: Protección de Datos Personales (RGPD)",
'cgu.art8_text': "Los únicos datos conservados son los estrictamente necesarios para la gestión de la cuenta (email, facturación). El Usuario tiene derecho de acceso, rectificación y eliminación total de sus datos personales.",
'cgu.art9_title': "Artículo 9: Modificación y Rescisión",
'cgu.art9_1_title': "9.1 Modificación de las CGU",
'cgu.art9_1_text': "La Plataforma se reserva el derecho de modificar las presentes CGU en cualquier momento. El Usuario será informado de cualquier modificación sustancial.",
'cgu.art9_2_title': "9.2 Rescisión",
'cgu.art9_2_text': "El Usuario puede eliminar su cuenta en cualquier momento. Esta acción conlleva la eliminación inmediata de todos sus datos, de conformidad con el Artículo 6.",
'cgu.art10_title': "Artículo 10: Ley Aplicable y Jurisdicción",
'cgu.art10_text': "Las presentes CGU están sujetas a la ley francesa. Cualquier disputa relativa a su interpretación o ejecución corresponde a la competencia exclusiva de los tribunales competentes.",
// CGV Page
'cgv.title': "Condiciones Generales de Venta (CGV)",
'cgv.version': "Versión vigente al: 06/03/2026",
'cgv.art1_title': "Artículo 1: Objeto y Ámbito de Aplicación",
'cgv.art1_text1': "Las presentes Condiciones Generales de Venta (CGV) se aplican, sin restricción ni reserva, a cualquier compra de servicios de generación de ebooks (en adelante \"los Servicios\") ofrecidos por Pluume en la plataforma https://pluu.me.",
'cgv.art1_text2': "Estas CGV regulan las modalidades de pedido, pago, entrega de créditos/suscripciones y gestión de posibles disputas entre el Vendedor y el Usuario (en adelante \"el Cliente\").",
'cgv.art2_title': "Artículo 2: Características de los Servicios",
'cgv.art2_text': "El Vendeur ofrece soluciones de creación de ebooks asistidas por inteligencia artificial Google Gemini. Las ofertas se dividen en dos categorías:",
'cgv.art2_item1': "Packs de Créditos: Compra puntual de un volumen definido de generaciones de contenido.",
'cgv.art2_item2': "Suscripciones: Acceso ilimitado o limitado a los servicios por una duración determinada (mensual o anual) con renovación automática.",
'cgv.art3_title': "Artículo 3: Pedido y Validación",
'cgv.art3_text': "El Cliente selecciona la oferta de su elección en la Plataforma. El pedido se considera definitivo tras la validación del pago por el proveedor tercero. Se envía un correo de confirmación al Cliente a la dirección proporcionada durante la creación de la cuenta.",
'cgv.art4_title': "Artículo 4: Condiciones Financieras",
'cgv.art4_1_title': "4.1 Tarifas",
'cgv.art4_1_text': "Los precios se indican en Euros y se entienden con todos los impuestos incluidos (TTC). El Vendedor se reserva el derecho de modificar sus tarifas en cualquier momento, pero los servicios se facturarán según las tarifas vigentes en el momento del registro del pedido.",
'cgv.art4_2_title': "4.2 Modalidades de pago (Stripe)",
'cgv.art4_2_text': "El pago se realiza exclusivamente por tarjeta bancaria a través del sistema de pago seguro Stripe.",
'cgv.art4_2_item1': "El Vendedor nunca tiene acceso a los datos bancarios del Cliente.",
'cgv.art4_2_item2': "Stripe garantiza la confidencialidad y la seguridad de las transacciones gracias al protocolo SSL y al cumplimiento de PCI-DSS.",
'cgv.art5_title': "Artículo 5: Gestión de Suscripciones",
'cgv.art5_1_title': "5.1 Renovación Automática",
'cgv.art5_1_text': "Cualquier suscripción contratada es con renovación automática. El Cliente autoriza a Stripe a retirar el importe de la suscripción en cada vencimiento (mes o año) de la tarjeta bancaria utilizada durante la compra inicial.",
'cgv.art5_2_title': "5.2 Rescisión",
'cgv.art5_2_text': "El Cliente puede rescindir su suscripción en cualquier momento y sin cargo desde su espacio personal.",
'cgv.art5_2_item1': "La rescisión interrumpe la renovación automática pero deja el acceso a los servicios activo hasta el final del periodo en curso.",
'cgv.art5_2_item2': "No se realiza ningún reembolso prorrata por el periodo restante entre la fecha de rescisión y el final del periodo.",
'cgv.art6_title': "Artículo 6: Derecho de Desistimiento (Productos Digitales)",
'cgv.art6_text': "De conformidad con el artículo L221-28 del Código del Consumidor, el derecho de desistimiento de 14 días no se aplica a los contratos de suministro de contenido digital no proporcionado en un soporte material cuya ejecución haya comenzado tras el acuerdo previo expreso del consumidor.",
'cgv.art6_warning': "Al proceder al pago y utilizar su primer crédito de generación, el Cliente acepta expresamente la ejecución inmediata del servicio y renuncia a su derecho de desistimiento. No se concederá ningún reembolso una vez consumido el servicio.",
'cgv.art7_title': "Artículo 7: Entrega y Acceso",
'cgv.art7_text': "Los Servicios se consideran \"entregados\" tan pronto como los créditos se añaden a la cuenta del Cliente o se desbloquea el acceso premium. En caso de problema técnico que impida el acceso inmediato, el Cliente debe contactar con soporte.",
'cgv.art8_title': "Artículo 8: Garantías y Responsabilidad Comercial",
'cgv.art8_1_title': "8.1 Calidad de la IA",
'cgv.art8_1_text': "El Vendedor vende acceso a una herramienta de generación. No garantiza la calidad literaria, la exactitud de los hechos ni la originalidad absoluta del texto producido por la IA. Por lo tanto, ninguna solicitud de reembolso podrá motivarse por una \"decepción\" respecto al estilo de la IA.",
'cgv.art8_2_title': "8.2 Continuidad del Servicio",
'cgv.art8_2_text': "El Vendedor se compromete a hacer sus mejores esfuerzos para mantener el acceso al servicio, pero no será responsable de los fallos debidos a proveedores terceros (Stripe, API Google Gemini).",
'cgv.art9_title': "Artículo 9: Política de Eliminación de Datos",
'cgv.art9_text': "Como se estipula en las CGU, el Vendedor aplica una política de cero almacenamiento.",
'cgv.art9_item1': "Si el Cliente elimina su información o su cuenta, todos los datos de facturación se archivan únicamente durante la duración legal fiscal, pero los contenidos creados se eliminan irreversiblemente.",
'cgv.art9_item2': "El Cliente no podrá reclamar una indemnización por pérdida de datos tras una eliminación voluntaria.",
'cgv.art10_title': "Artículo 10: Litigios y Ley Aplicable",
'cgv.art10_text': "Las presentes CGV están sujetas a la ley francesa. En caso de disputa, se buscará una solución amistosa antes de cualquier acción legal. A falta de acuerdo, la competencia exclusiva se atribuye a los tribunales competentes.",
},
de: {
// General Navigation
@@ -1157,6 +1461,8 @@ export const translations = {
'auth.signup_link': 'Registrieren',
'auth.signin_link': 'Anmelden',
'auth.back_to_site': '← Zurück zur Website',
'auth.accept_cgu': "Ich akzeptiere die ",
'auth.cgu_link': "Nutzungsbedingungen",
'auth.hero_title_part1': "Der Ort, an dem deine",
'auth.hero_title_part2': "Geschichten",
'auth.hero_title_part3': "zum Leben erwachen.",
@@ -1324,7 +1630,107 @@ export const translations = {
'pov_options.multi-points_de_vue_(alterné)': 'Mehrere Perspektiven',
'tense_options.passé_(passé_simple_/_imparfait)': 'Vergangenheit (Präteritum)',
'tense_options.présent_de_narration': 'Gegenwart (Präsens)',
}
// CGU Page
'cgu.title': "Allgemeine Nutzungsbedingungen (ANB)",
'cgu.version': "Gültig ab: 06.03.2026",
'cgu.preamble_title': "Präambel",
'cgu.preamble_text1': "Diese Plattform (nachfolgend \"die Plattform\"), erreichbar unter https://pluu.me, wird von Pluume herausgegeben. Die Plattform bietet einen durch künstliche Intelligenz (Google Gemini Technologie) unterstützten Textgenerierungsdienst zur Erstellung von E-Books an.",
'cgu.preamble_text2': "Diese Allgemeinen Nutzungsbedingungen (ANB) dienen der Festlegung der Zugangs- und Nutzungsregeln für den Dienst. Jeder Zugang oder jede Nutzung der Plattform setzt die vorbehaltlose Annahme der Gesamtheit dieser Bedingungen voraus.",
'cgu.art1_title': "Artikel 1: Definitionen",
'cgu.art1_user': "Nutzer: Jede natürliche oder juristische Person, die auf die Plattform zugreift.",
'cgu.art1_service': "Dienst: Gesamtheit der zur Verfügung gestellten Werkzeuge zur Textgenerierung, Strukturierung und zum Export von E-Books.",
'cgu.art1_content': "Generierter Inhalt: Texte, Pläne oder Dokumente, die von der KI aufgrund der Anweisungen des Nutzers erstellt wurden.",
'cgu.art1_prompt': "Prompt: Textanweisungen, die der Nutzer eingibt, um die KI zu steuern.",
'cgu.art2_title': "Artikel 2: Zugang und Anmeldung",
'cgu.art2_text': "Der Zugang zur Plattform ist volljährigen Personen vorbehalten. Um die Generierungsdienste zu nutzen, muss der Nutzer ein Konto erstellen. Er ist für die Vertraulichkeit seiner Zugangsdaten verantwortlich.",
'cgu.art3_title': "Artikel 3: Finanzielle Bedingungen (Abonnements und Credits)",
'cgu.art3_1_title': "3.1 Tarife",
'cgu.art3_1_text': "Der Zugang zu den Generierungsfunktionen unterliegt einer Preisgestaltung, die auf der Seite Preise einsehbar ist. Die Preise verstehen sich in Euro inkl. MwSt.",
'cgu.art3_1_sub1': "Abonnements: Automatischer periodischer Einzug.",
'cgu.art3_1_sub2': "Credit-Packs: Einmaliger Kauf eines definierten Volumens an Generierungen.",
'cgu.art3_2_title': "3.2 Zahlung und Sicherheit",
'cgu.art3_2_text': "Zahlungen werden über einen sicheren Zahlungsanbieter abgewickelt. Die Plattform speichert keine Bankdaten.",
'cgu.art4_title': "Artikel 4: Pflichten des Nutzers und Sicherheit (Strenge Klausel)",
'cgu.art4_text': "Der Nutzer verpflichtet sich, den Dienst rechtmäßig zu nutzen.",
'cgu.art4_1_title': "4.1 Verbotene Inhalte",
'cgu.art4_1_text': "Es ist ausdrücklich untersagt, die Plattform zur Generierung folgender Inhalte zu nutzen:",
'cgu.art4_1_item1': "Kinderpornographie (CSAM): Jeder Versuch der Generierung, Abfrage oder Verbreitung von Inhalten im Zusammenhang mit der sexuellen Ausbeutung Minderjähriger führt zum sofortigen Ausschluss ohne Vorankündigung oder Rückerstattung. Gesetzgemäß erfolgt eine systematische Meldung an die zuständigen Behörden.",
'cgu.art4_1_item2': "Hasserfüllt und diskriminierend: Aufstachelung zu Gewalt, Rassismus, Antisemitismus, Homophobie oder jeder Form von Diskriminierung.",
'cgu.art4_1_item3': "Illegal: Verherrlichung von Verbrechen, Terrorismus oder Verletzung von geistigem Eigentum Dritter.",
'cgu.art4_2_title': "4.2 Überwachung und Moderation",
'cgu.art4_2_text': "Die Plattform verwendet Echtzeit-Filteralgorithmen. Bei wiederholtem oder schwerem Verstoß gegen diesen Artikel behält sich die Plattform das Recht vor, das Konto des Nutzers von Rechts wegen zu sperren.",
'cgu.art5_title': "Artikel 5: Geistiges Eigentum",
'cgu.art5_1_title': "5.1 Rechte des Nutzers",
'cgu.art5_1_text': "Die Plattform gewährt dem Nutzer, unter Vorbehalt der Zahlung etwaiger Gebühren, das volle Eigentum an den Verwertungsrechten an den generierten E-Books.",
'cgu.art5_1_note': "Hinweis zur KI: Der Nutzer wird hiermit informiert, dass der urheberrechtliche Schutz von KI-generierten Werken je nach nationaler Gesetzgebung variieren kann und oft eine erhebliche menschliche kreative Leistung des Nutzers erfordert.",
'cgu.art5_2_title': "5.2 Rechte der Plattform",
'cgu.art5_2_text': "Die Benutzeroberfläche, die Verbindungsalgorithmen zur Gemini-API und die visuelle Identität der Website bleiben das ausschließliche Eigentum des Herausgebers.",
'cgu.art6_title': "Artikel 6: Politik der \"Null-Speicherung\" und Vertraulichkeit",
'cgu.art6_1_title': "6.1 Flüchtige Verarbeitung",
'cgu.art6_1_text1': "Die Plattform wendet eine strenge Vertraulichkeitspolitik an. Es werden keine Erstellungsdaten (Prompts, generierte Texte, laufende E-Books) dauerhaft auf unseren Servern gespeichert.",
'cgu.art6_1_text2': "Die Daten werden nur für die Dauer der Arbeitssitzung aufbewahrt, um die Anzeige und den Export zu ermöglichen.",
'cgu.art6_1_text3': "In jedem Fall werden bei Löschung der Informationen, des Projekts oder beim Schließen der Sitzung durch den Nutzer alle damit verbundenen Daten sofort und unwiderruflich aus unseren Datenbanken gelöscht.",
'cgu.art6_2_title': "6.2 Verantwortung für die Sicherung",
'cgu.art6_2_text': "Aufgrund dieser Nicht-Aufbewahrungspolitik liegt es in der ausschließlichen Verantwortung des Nutzers, seine Arbeiten vor Beendigung der Sitzung herunterzuladen und zu sichern (Format PDF, EPUB usw.). Die Plattform kann nicht für Datenverlust infolge einer Trennung oder freiwilligen Löschung haftbar gemacht werden.",
'cgu.art7_title': "Artikel 7: Haftungsbeschränkung",
'cgu.art7_1_title': "7.1 Qualität des Inhalts",
'cgu.art7_1_text': "Der Dienst nutzt die Google Gemini Technologie. Der Nutzer akzeptiert, dass die KI ungenaue, unvollständige oder voreingenommene Informationen (\"Halluzinationen\") produzieren kann. Die Plattform haftet nicht für den Inhalt der E-Books oder deren Verwendung.",
'cgu.art7_2_title': "7.2 Technische Verfügbarkeit",
'cgu.art7_2_text': "Die Plattform kann keine ununterbrochene Verfügbarkeit des Dienstes garantieren, da diese von Drittanbietern (Hosting und Google-API) abhängt.",
'cgu.art8_title': "Artikel 8: Schutz personenbezogener Daten (DSGVO)",
'cgu.art8_text': "Es werden nur die Daten aufbewahrt, die für die Kontoverwaltung (E-Mail, Abrechnung) unbedingt erforderlich sind. Der Nutzer hat ein Recht auf Auskunft, Berichtigung und vollständige Löschung seiner personenbezogenen Daten.",
'cgu.art9_title': "Artikel 9: Änderung und Beendigung",
'cgu.art9_1_title': "9.1 Änderung der ANB",
'cgu.art9_1_text': "Die Plattform behält sich das Recht vor, diese ANB jederzeit zu ändern. Der Nutzer wird über jede wesentliche Änderung informiert.",
'cgu.art9_2_title': "9.2 Beendigung",
'cgu.art9_2_text': "Der Nutzer kann sein Konto jederzeit löschen. Diese Aktion führt zur sofortigen Löschung all seiner Daten gemäß Artikel 6.",
'cgu.art10_title': "Artikel 10: Anwendbares Recht und Gerichtsstand",
'cgu.art10_text': "Diese ANB unterliegen französischem Recht. Jeder Streitfall im Zusammenhang mit ihrer Auslegung oder Ausführung fällt in die ausschließliche Zuständigkeit der zuständigen Gerichte.",
// CGV Page
'cgv.title': "Allgemeine Verkaufsbedingungen (AVB)",
'cgv.version': "Gültig ab: 06.03.2026",
'cgv.art1_title': "Artikel 1: Gegenstand und Anwendungsbereich",
'cgv.art1_text1': "Diese Allgemeinen Verkaufsbedingungen (AVB) gelten ohne Einschränkung oder Vorbehalt für jeden Kauf von E-Book-Generierungsdiensten (nachfolgend \"die Dienste\"), die von Pluume auf der Plattform https://pluu.me angeboten werden.",
'cgv.art1_text2': "Diese AVB regeln die Modalitäten von Bestellung, Zahlung, Lieferung von Credits/Abonnements und die Abwicklung etwaiger Streitigkeiten zwischen dem Verkäufer und dem Nutzer (nachfolgend \"der Kunde\").",
'cgv.art2_title': "Artikel 2: Merkmale der Dienste",
'cgv.art2_text': "Der Verkäufer bietet Lösungen zur Erstellung von E-Books an, die durch die künstliche Intelligenz Google Gemini unterstützt werden. Die Angebote sind in zwei Kategorien unterteilt:",
'cgv.art2_item1': "Credit-Packs: Einmaliger Kauf eines definierten Volumens an Inhaltsgenerierungen.",
'cgv.art2_item2': "Abonnements: Unbegrenzter oder gedrosselter Zugang zu den Diensten für eine bestimmte Dauer (monatlich oder jährlich) mit automatischer Verlängerung.",
'cgv.art3_title': "Artikel 3: Bestellung und Validierung",
'cgv.art3_text': "Der Kunde wählt das Angebot seiner Wahl auf der Plattform aus. Die Bestellung gilt als endgültig nach Bestätigung der Zahlung durch den Drittanbieter. Eine Bestätigungs-E-Mail wird an den Kunden an die bei der Kontoerstellung angegebene Adresse gesendet.",
'cgv.art4_title': "Artikel 4: Finanzielle Bedingungen",
'cgv.art4_1_title': "4.1 Tarife",
'cgv.art4_1_text': "Die Preise sind in Euro angegeben und verstehen sich inklusive aller Steuern (MwSt.). Der Verkäufer behält sich das Recht vor, seine Preise jederzeit zu ändern, aber die Dienste werden auf der Grundlage der zum Zeitpunkt der Registrierung der Bestellung gültigen Tarife berechnet.",
'cgv.art4_2_title': "4.2 Zahlungsmodalitäten (Stripe)",
'cgv.art4_2_text': "Die Zahlung erfolgt ausschließlich per Kreditkarte über das sichere Zahlungssystem Stripe.",
'cgv.art4_2_item1': "Der Verkäufer hat niemals Zugang zu den Bankdaten des Kunden.",
'cgv.art4_2_item2': "Stripe garantiert die Vertraulichkeit und Sicherheit der Transaktionen dank des SSL-Protokolls und der PCI-DSS-Konformität.",
'cgv.art5_title': "Artikel 5: Verwaltung von Abonnements",
'cgv.art5_1_title': "5.1 Automatische Verlängerung",
'cgv.art5_1_text': "Jedes abgeschlossene Abonnement verlängert sich stillschweigend. Der Kunde ermächtigt Stripe, den Abonnementbetrag bei jeder Fälligkeit (Monat oder Jahr) von der beim ursprünglichen Kauf verwendeten Kreditkarte einzuziehen.",
'cgv.art5_2_title': "5.2 Kündigung",
'cgv.art5_2_text': "Der Kunde kann sein Abonnement jederzeit und kostenlos über seinen persönlichen Bereich kündigen.",
'cgv.art5_2_item1': "Die Kündigung unterbricht die automatische Verlängerung, lässt den Zugang zu den Diensten jedoch bis zum Ende des laufenden Zeitraums aktiv.",
'cgv.art5_2_item2': "Es erfolgt keine anteilige Rückerstattung für den Zeitraum zwischen dem Kündigungsdatum und dem Ende der Laufzeit.",
'cgv.art6_title': "Artikel 6: Widerrufsrecht (Digitale Produkte)",
'cgv.art6_text': "Gemäß Artikel L221-28 des Verbrauchergesetzbuches gilt das 14-tägige Widerrufsrecht nicht für Verträge über die Lieferung von digitalen Inhalten, die nicht auf einem körperlichen Datenträger geliefert werden und deren Ausführung nach ausdrücklicher vorheriger Zustimmung des Verbrauchers begonnen hat.",
'cgv.art6_warning': "Mit der Durchführung der Zahlung und der Nutzung seines ersten Generierungs-Credits akzeptiert der Kunde ausdrücklich die sofortige Ausführung des Dienstes und verzichtet auf sein Widerrufsrecht. Nach Inanspruchnahme des Dienstes wird keine Rückerstattung gewährt.",
'cgv.art7_title': "Artikel 7: Lieferung und Zugang",
'cgv.art7_text': "Die Dienste gelten als \"geliefert\", sobald die Credits dem Konto des Kunden gutgeschrieben wurden oder der Premium-Zugang freigeschaltet wurde. Bei technischen Problemen sollte der Kunde den Support kontaktieren.",
'cgv.art8_title': "Artikel 8: Garantien und kommerzielle Haftung",
'cgv.art8_1_title': "8.1 Qualität der KI",
'cgv.art8_1_text': "Der Verkäufer verkauft den Zugang zu einem Generierungswerkzeug. Er garantiert nicht die literarische Qualität, die sachliche Richtigkeit oder die absolute Originalität des von der KI erzeugten Textes. Daher kann kein Rückerstattungsantrag mit einer \"Enttäuschung\" über den Stil der KI begründet werden.",
'cgv.art8_2_title': "8.2 Kontinuität des Dienstes",
'cgv.art8_2_text': "Der Verkäufer verpflichtet sich, sein Bestes zu tun, um den Zugang zum Dienst aufrechtzuerhalten, kann jedoch nicht für Ausfälle aufgrund von Drittanbietern (Stripe, Google Gemini API) verantwortlich gemacht werden.",
'cgv.art9_title': "Artikel 9: Politik zur Datenlöschung",
'cgv.art9_text': "Wie in den ANB festgelegt, wendet der Verkäufer eine Null-Speicherungspolitik an.",
'cgv.art9_item1': "Wenn der Kunde seine Informationen oder sein Konto löscht, werden alle Abrechnungsdaten nur für die gesetzliche steuerliche Dauer archiviert, die erstellten Inhalte jedoch unwiderruflich gelöscht.",
'cgv.art9_item2': "Der Kunde kann keinen Anspruch auf Entschädigung für Datenverlust infolge einer freiwilligen Löschung geltend machen.",
'cgv.art10_title': "Artikel 10: Streitigkeiten und anwendbares Recht",
'cgv.art10_text': "Diese AVB unterliegen französischem Recht. Im Streitfall wird vor gerichtlichen Schritten eine gütliche Einigung angestrebt. Kommt keine Einigung zustande, wird die ausschließliche Zuständigkeit den zuständigen Gerichten zugewiesen.",
},
};
export type TranslationKey = keyof typeof translations.fr;

View File

@@ -41,6 +41,7 @@ export interface Entity {
id: string;
type: EntityType;
name: string;
avatar?: string;
description: string;
details: string;
storyContext?: string;