Compare commits

...

10 Commits

Author SHA1 Message Date
Omar Oughriss
178d1259b5 Delete pairingId debug feature 2025-10-20 11:43:29 +02:00
Omar Oughriss
f1da6096bc Set pairingId while requesting link 2025-10-20 11:42:32 +02:00
Omar Oughriss
b52f17f2f2 Refactor formation page 2025-10-20 11:41:41 +02:00
Omar Oughriss
5232f6ef70 Refactor home page 2025-10-20 11:41:13 +02:00
Omar Oughriss
698f7cbe86 Create constant file to refactor 2025-10-20 11:39:55 +02:00
Omar Oughriss
8bd5968f77 Create card components to refactor 2025-10-20 11:38:33 +02:00
Omar Oughriss
c529273c0f Create an index for layout components 2025-10-20 11:37:57 +02:00
Omar Oughriss
f8e15f765c Create Footer component to refactor 2025-10-20 11:37:25 +02:00
Omar Oughriss
7af3144470 Create Header component to refactor 2025-10-20 11:37:02 +02:00
Omar Oughriss
02036f7ef4 Use the correct css file 2025-10-20 11:33:36 +02:00
13 changed files with 705 additions and 373 deletions

View File

@ -41,7 +41,6 @@ import {
CloudUpload,
Cloud,
HardDrive,
Brain,
FileQuestion,
Timer,
ShieldCheck,
@ -707,46 +706,6 @@ export default function FoldersPage() {
setShowAuthModal(false);
}, []);
// Debug function pour forcer la récupération du userPairingId
const handleForceGetPairingId = useCallback(() => {
console.log('Force récupération userPairingId - État actuel:', {
isConnected,
userPairingId,
userStoreConnected: UserStore.getInstance().isConnected(),
userStorePairingId: UserStore.getInstance().getUserPairingId()
});
// D'abord essayer de synchroniser depuis UserStore
const userStorePairingId = UserStore.getInstance().getUserPairingId();
if (userStorePairingId) {
console.log('Force - Synchronisation depuis UserStore:', userStorePairingId);
setUserPairingId(userStorePairingId);
showNotification("success", `UserPairingId synchronisé: ${userStorePairingId.substring(0, 8)}...`);
return;
}
// Sinon récupérer depuis MessageBus
if (isConnected) {
const messageBus = MessageBus.getInstance(iframeUrl);
messageBus.isReady().then(() => {
console.log('Force - MessageBus prêt');
messageBus.getUserPairingId().then((retrievedPairingId: string) => {
console.log('Force - UserPairingId récupéré:', retrievedPairingId);
UserStore.getInstance().pair(retrievedPairingId);
setUserPairingId(retrievedPairingId);
showNotification("success", `UserPairingId récupéré: ${retrievedPairingId.substring(0, 8)}...`);
}).catch((error) => {
console.error('Force - Erreur récupération userPairingId:', error);
showNotification("error", "Erreur lors de la récupération du userPairingId");
});
}).catch((error) => {
console.error('Force - Erreur MessageBus isReady:', error);
showNotification("error", "MessageBus non prêt");
});
} else {
showNotification("error", "Vous devez être connecté à 4NK");
}
}, [isConnected, userPairingId, iframeUrl]);
// Fonction pour envoyer une notification dans le chat du dossier
const sendFolderChatNotification = (folderId: string, message: string, actionType: string) => {
@ -1507,13 +1466,6 @@ export default function FoldersPage() {
Déconnexion 4NK
</Button>
{/* Debug PairingId */}
{!userPairingId && (
<Button variant="outline" size="sm" onClick={handleForceGetPairingId}>
<Brain className="h-4 w-4 mr-2" />
Debug PairingId
</Button>
)}
</div>
) : (
<Button variant="outline" size="sm" onClick={handleLogin}>

View File

@ -26,7 +26,6 @@ import {
HardDrive,
X,
FolderPlus,
Brain,
XCircle,
Info,
} from "lucide-react"
@ -347,46 +346,6 @@ export default function DashboardPage() {
// 4NK handlers
// Debug function pour forcer la récupération du userPairingId
const handleForceGetPairingId = useCallback(() => {
console.log('Force récupération userPairingId - État actuel:', {
isConnected,
userPairingId,
userStoreConnected: UserStore.getInstance().isConnected(),
userStorePairingId: UserStore.getInstance().getUserPairingId()
});
// D'abord essayer de synchroniser depuis UserStore
const userStorePairingId = UserStore.getInstance().getUserPairingId();
if (userStorePairingId) {
console.log('Force - Synchronisation depuis UserStore:', userStorePairingId);
setUserPairingId(userStorePairingId);
showNotification("success", `UserPairingId synchronisé: ${userStorePairingId.substring(0, 8)}...`);
return;
}
// Sinon récupérer depuis MessageBus
if (isConnected) {
const messageBus = MessageBus.getInstance(iframeUrl);
messageBus.isReady().then(() => {
console.log('Force - MessageBus prêt');
messageBus.getUserPairingId().then((retrievedPairingId: string) => {
console.log('Force - UserPairingId récupéré:', retrievedPairingId);
UserStore.getInstance().pair(retrievedPairingId);
setUserPairingId(retrievedPairingId);
showNotification("success", `UserPairingId récupéré: ${retrievedPairingId.substring(0, 8)}...`);
}).catch((error) => {
console.error('Force - Erreur récupération userPairingId:', error);
showNotification("error", "Erreur lors de la récupération du userPairingId");
});
}).catch((error) => {
console.error('Force - Erreur MessageBus isReady:', error);
showNotification("error", "MessageBus non prêt");
});
} else {
showNotification("error", "Vous devez être connecté à 4NK");
}
}, [isConnected, userPairingId, iframeUrl]);
return (
<div className="space-y-6">
@ -418,15 +377,6 @@ export default function DashboardPage() {
</p>
</div>
<div className="flex gap-2">
{/* Debug PairingId */}
{!userPairingId && (
<Button variant="outline" size="sm" onClick={handleForceGetPairingId}>
<Brain className="h-4 w-4 mr-2" />
Debug PairingId
</Button>
)}
</div>
</div>

View File

@ -2,25 +2,21 @@ import Link from "next/link"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { Shield, Monitor, Code, ArrowLeft, Clock, Users, Award, BookOpen } from 'lucide-react'
import { Shield, Monitor, Code, Clock, Users, Award, BookOpen } from 'lucide-react'
import { Header, Footer } from "@/components/layout"
import FormationCard from "@/components/ui/FormationCard"
import { FORMATIONS } from "@/lib/constants"
export default function FormationPage() {
return (
<div className="min-h-screen bg-gray-900 text-gray-100">
{/* Header */}
<header className="border-b border-gray-700 bg-gray-800/80 backdrop-blur-sm">
<div className="container mx-auto px-4 py-4 flex justify-between items-center">
<Link href="/" className="flex items-center space-x-2">
<Shield className="h-8 w-8 text-blue-400" />
<span className="text-2xl font-bold text-gray-100">DocV</span>
<Badge variant="secondary" className="ml-2 bg-gray-700 text-gray-200">By 4NK</Badge>
</Link>
<Link href="/" className="flex items-center text-blue-400 hover:text-blue-500">
<ArrowLeft className="h-4 w-4 mr-2" />
Retour à l'accueil
</Link>
</div>
</header>
<Header
variant="dark"
showAuth={false}
showBackButton={true}
backHref="/"
backText="Retour à l'accueil"
/>
{/* Hero Section */}
<section className="py-16 px-4">
@ -66,149 +62,38 @@ export default function FormationPage() {
{/* Formations Section */}
<section className="py-16 px-4">
<div className="container mx-auto grid lg:grid-cols-3 gap-8">
{/* Cybersécurité */}
<Card className="border-2 border-gray-700 hover:border-red-600 bg-gray-800 hover:shadow-xl transition-all duration-300">
<CardHeader className="text-center">
<Shield className="h-16 w-16 text-red-400 mx-auto mb-4" />
<CardTitle className="text-2xl text-red-300">Cybersécurité</CardTitle>
<CardDescription className="text-lg text-gray-300">
Maîtrisez les fondamentaux de la sécurité informatique
</CardDescription>
</CardHeader>
<CardContent className="space-y-6 text-gray-300">
<div>
<h4 className="font-semibold mb-3">Programme de formation :</h4>
<ul className="space-y-2">
<li> Analyse des menaces et vulnérabilités</li>
<li> Cryptographie appliquée et PKI</li>
<li> Sécurisation des infrastructures</li>
<li> Gestion des incidents de sécurité</li>
<li> Audit et conformité (ISO 27001, RGPD)</li>
<li> Tests d'intrusion et pentest</li>
</ul>
</div>
<div className="bg-red-900 p-4 rounded-lg">
<h5 className="font-semibold text-red-200 mb-2">Spécialisation DocV :</h5>
<ul className="text-sm text-red-300 space-y-1">
<li> Authentification sans mot de passe</li>
<li> Chiffrement de bout en bout</li>
<li> Blockchain et preuves cryptographiques</li>
<li> Architecture zero-trust</li>
</ul>
</div>
<div className="flex items-center justify-between text-sm text-gray-400">
<div className="flex items-center">
<Clock className="h-4 w-4 mr-1" />
5 jours
</div>
<div className="flex items-center">
<Users className="h-4 w-4 mr-1" />
Max 12 pers.
</div>
</div>
<Link href="/formation/devis">
<Button className="w-full bg-red-600 hover:bg-red-700 text-white">
S'inscrire à la formation
</Button>
</Link>
</CardContent>
</Card>
<FormationCard
icon={Shield}
title={FORMATIONS.cybersecurity.title}
description={FORMATIONS.cybersecurity.description}
program={FORMATIONS.cybersecurity.program}
specialization={FORMATIONS.cybersecurity.specialization}
duration={FORMATIONS.cybersecurity.duration}
maxParticipants={FORMATIONS.cybersecurity.maxParticipants}
color={FORMATIONS.cybersecurity.color}
/>
{/* Hygiène Numérique */}
<Card className="border-2 border-gray-700 hover:border-green-600 bg-gray-800 hover:shadow-xl transition-all duration-300">
<CardHeader className="text-center">
<Monitor className="h-16 w-16 text-green-400 mx-auto mb-4" />
<CardTitle className="text-2xl text-green-300">Hygiène Numérique</CardTitle>
<CardDescription className="text-lg text-gray-300">
Adoptez les bonnes pratiques pour un environnement numérique sain
</CardDescription>
</CardHeader>
<CardContent className="space-y-6 text-gray-300">
<div>
<h4 className="font-semibold mb-3">Programme de formation :</h4>
<ul className="space-y-2">
<li> Gestion sécurisée des mots de passe</li>
<li> Protection de la vie privée en ligne</li>
<li> Sécurisation des communications</li>
<li> Sauvegarde et archivage sécurisé</li>
<li> Sensibilisation aux risques numériques</li>
<li> RGPD et protection des données</li>
</ul>
</div>
<div className="bg-green-900 p-4 rounded-lg">
<h5 className="font-semibold text-green-200 mb-2">Approche DocV :</h5>
<ul className="text-sm text-green-300 space-y-1">
<li> Identité numérique souveraine</li>
<li> Gestion documentaire sécurisée</li>
<li> Réduction de l'empreinte numérique</li>
<li> Autonomie technologique</li>
</ul>
</div>
<div className="flex items-center justify-between text-sm text-gray-400">
<div className="flex items-center">
<Clock className="h-4 w-4 mr-1" />
3 jours
</div>
<div className="flex items-center">
<Users className="h-4 w-4 mr-1" />
Max 15 pers.
</div>
</div>
<Link href="/formation/devis">
<Button className="w-full bg-green-600 hover:bg-green-700 text-white">
S'inscrire à la formation
</Button>
</Link>
</CardContent>
</Card>
<FormationCard
icon={Monitor}
title={FORMATIONS.digitalHygiene.title}
description={FORMATIONS.digitalHygiene.description}
program={FORMATIONS.digitalHygiene.program}
specialization={FORMATIONS.digitalHygiene.specialization}
duration={FORMATIONS.digitalHygiene.duration}
maxParticipants={FORMATIONS.digitalHygiene.maxParticipants}
color={FORMATIONS.digitalHygiene.color}
/>
{/* Développement Souverain */}
<Card className="border-2 border-gray-700 hover:border-blue-600 bg-gray-800 hover:shadow-xl transition-all duration-300">
<CardHeader className="text-center">
<Code className="h-16 w-16 text-blue-400 mx-auto mb-4" />
<CardTitle className="text-2xl text-blue-300">Développement Souverain</CardTitle>
<CardDescription className="text-lg text-gray-300">
Créez des applications indépendantes et sécurisées
</CardDescription>
</CardHeader>
<CardContent className="space-y-6 text-gray-300">
<div>
<h4 className="font-semibold mb-3">Programme de formation :</h4>
<ul className="space-y-2">
<li> Architecture décentralisée</li>
<li> Développement sans dépendances cloud</li>
<li> Intégration blockchain et cryptographie</li>
<li> APIs souveraines et sécurisées</li>
<li> Déploiement on-premise</li>
<li> Maintenance et évolutivité</li>
</ul>
</div>
<div className="bg-blue-900 p-4 rounded-lg">
<h5 className="font-semibold text-blue-200 mb-2">Technologies DocV :</h5>
<ul className="text-sm text-blue-300 space-y-1">
<li> Stack technologique souveraine</li>
<li> Intégration IA locale</li>
<li> Gestion d'identité décentralisée</li>
<li> Protocoles de communication sécurisés</li>
</ul>
</div>
<div className="flex items-center justify-between text-sm text-gray-400">
<div className="flex items-center">
<Clock className="h-4 w-4 mr-1" />
7 jours
</div>
<div className="flex items-center">
<Users className="h-4 w-4 mr-1" />
Max 8 pers.
</div>
</div>
<Link href="/formation/devis">
<Button className="w-full bg-blue-600 hover:bg-blue-700 text-white">
S'inscrire à la formation
</Button>
</Link>
</CardContent>
</Card>
<FormationCard
icon={Code}
title={FORMATIONS.sovereignDevelopment.title}
description={FORMATIONS.sovereignDevelopment.description}
program={FORMATIONS.sovereignDevelopment.program}
specialization={FORMATIONS.sovereignDevelopment.specialization}
duration={FORMATIONS.sovereignDevelopment.duration}
maxParticipants={FORMATIONS.sovereignDevelopment.maxParticipants}
color={FORMATIONS.sovereignDevelopment.color}
/>
</div>
</section>
@ -300,19 +185,7 @@ export default function FormationPage() {
</div>
</section>
{/* Footer */}
<footer className="bg-gray-900 text-gray-300 py-8 px-4">
<div className="container mx-auto text-center">
<div className="flex items-center justify-center space-x-2 mb-4">
<Shield className="h-6 w-6 text-blue-400" />
<span className="text-xl font-bold text-gray-100">DocV</span>
<Badge variant="secondary" className="bg-gray-700 text-gray-200">By 4NK</Badge>
</div>
<p className="text-gray-400">
4NK, pionnier du Web 5.0 - Solutions de souveraineté numérique
</p>
</div>
</footer>
<Footer variant="dark" showNavigation={false} />
</div>
)
}

View File

@ -1,6 +1,6 @@
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import '../styles/globals.css'
import './globals.css'
const inter = Inter({ subsets: ['latin'] })

View File

@ -5,11 +5,14 @@ import { useRouter } from "next/navigation"
import Link from "next/link"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Shield, ArrowRight, Key, Zap, Users, Globe, Database, Code, CheckCircle } from "lucide-react"
import { ArrowRight, Key, Zap, Users, Globe, Database, Code, CheckCircle, Shield } from "lucide-react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import AuthModal from "@/components/4nk/AuthModal"
import { Header, Footer } from "@/components/layout"
import ProductCard from "@/components/ui/ProductCard"
import { EXTERNAL_URLS, COMPANY_INFO } from "@/lib/constants"
export const iframeUrl = 'https://dev3.4nkweb.com'
export const iframeUrl = EXTERNAL_URLS.iframe
export default function HomePage() {
const [showAuthModal, setShowAuthModal] = useState(false)
@ -28,25 +31,7 @@ export default function HomePage() {
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-blue-50 dark:from-gray-900 dark:to-gray-800 transition-colors duration-300">
{/* Header */}
<header className="border-b bg-white/80 dark:bg-gray-900/80 backdrop-blur-sm sticky top-0 z-50 transition-colors duration-300">
<div className="container mx-auto px-4 py-4 flex justify-between items-center">
<div className="flex items-center space-x-2">
<Shield className="h-8 w-8 text-blue-600 dark:text-blue-400" />
<span className="text-2xl font-bold text-gray-900 dark:text-gray-100">DocV</span>
<Badge variant="secondary" className="ml-2">By 4NK</Badge>
</div>
<nav className="hidden md:flex items-center space-x-6">
<Link href="#produit" className="text-gray-600 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors duration-300">Le produit</Link>
<Link href="#securite" className="text-gray-600 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors duration-300">Sécurité</Link>
<Link href="#tarifs" className="text-gray-600 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors duration-300">Tarifs</Link>
<Link href="/formation">
<Button variant="outline" className="dark:border-gray-700 dark:text-gray-200 dark:hover:bg-gray-800 transition-colors duration-300">Formation</Button>
</Link>
<Button onClick={() => setShowAuthModal(true)} className="dark:bg-blue-700 dark:hover:bg-blue-600 transition-colors duration-300">Connexion</Button>
</nav>
</div>
</header>
<Header onAuthClick={() => setShowAuthModal(true)} />
{/* Hero Section */}
<section className="py-20 px-4 bg-gradient-to-br from-slate-50 to-blue-50 dark:from-gray-900 dark:to-gray-800 transition-colors duration-300">
@ -56,7 +41,7 @@ export default function HomePage() {
<span className="text-blue-600 dark:text-blue-400">GED simple et souveraine</span>
</h1>
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8 max-w-3xl mx-auto">
DocV propose une approche révolutionnaire de la gestion d'identité, garantissant sécurité, souveraineté et conformité dans la gestion de vos documents et processus métier.
{COMPANY_INFO.description}
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<Link href="">
@ -89,43 +74,34 @@ export default function HomePage() {
<div className="container mx-auto">
<h2 className="text-4xl font-bold text-center mb-12 text-gray-900 dark:text-gray-100">Le produit</h2>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8 mb-16">
{/* Card 1 */}
<Card className="border-2 border-gray-200 dark:border-gray-700 hover:border-blue-200 dark:hover:border-blue-400 transition-colors duration-300">
<CardHeader>
<Key className="h-12 w-12 text-blue-600 dark:text-blue-400 mb-4" />
<CardTitle className="dark:text-gray-100">Login cryptographique ultra-simplifié</CardTitle>
</CardHeader>
<CardContent>
<p className="text-gray-600 dark:text-gray-300 mb-4">Aucun mot de passe, aucun OTP, aucun mail, aucun code, aucune application.</p>
<p className="text-gray-600 dark:text-gray-300">Notifications transverses et temps réel sur l'avancement des traitements.</p>
</CardContent>
</Card>
<ProductCard
icon={Key}
title="Login cryptographique ultra-simplifié"
description={[
"Aucun mot de passe, aucun OTP, aucun mail, aucun code, aucune application.",
"Notifications transverses et temps réel sur l'avancement des traitements."
]}
/>
{/* Card 2 */}
<Card className="border-2 border-gray-200 dark:border-gray-700 hover:border-blue-200 dark:hover:border-blue-400 transition-colors duration-300">
<CardHeader>
<Zap className="h-12 w-12 text-blue-600 dark:text-blue-400 mb-4" />
<CardTitle className="dark:text-gray-100">IA embarquée</CardTitle>
</CardHeader>
<CardContent>
<p className="text-gray-600 dark:text-gray-300 mb-4">OCR, classification et extraction avec IA locale.</p>
<p className="text-gray-600 dark:text-gray-300">L'IA, ses données et ses traitements restent locaux.</p>
<p className="text-gray-600 dark:text-gray-300 mt-2">Interface conversationnelle pour suivre les dossiers.</p>
</CardContent>
</Card>
<ProductCard
icon={Zap}
title="IA embarquée"
description={[
"OCR, classification et extraction avec IA locale.",
"L'IA, ses données et ses traitements restent locaux.",
"Interface conversationnelle pour suivre les dossiers."
]}
/>
{/* Card 3 */}
<Card className="border-2 border-gray-200 dark:border-gray-700 hover:border-blue-200 dark:hover:border-blue-400 transition-colors duration-300">
<CardHeader>
<Users className="h-12 w-12 text-blue-600 dark:text-blue-400 mb-4" />
<CardTitle className="dark:text-gray-100">Facilite l'usage quotidien</CardTitle>
</CardHeader>
<CardContent>
<p className="text-gray-600 dark:text-gray-300 mb-2"> Réduction massive des emails</p>
<p className="text-gray-600 dark:text-gray-300 mb-2"> Protection des identités et accès</p>
<p className="text-gray-600 dark:text-gray-300"> Traçabilité sur blockchain</p>
</CardContent>
</Card>
<ProductCard
icon={Users}
title="Facilite l'usage quotidien"
description={[
"• Réduction massive des emails",
"• Protection des identités et accès",
"• Traçabilité sur blockchain"
]}
/>
</div>
{/* Additional Features */}
@ -488,51 +464,7 @@ export default function HomePage() {
</section>
{/* Footer */}
<footer className="bg-gray-900 dark:bg-gray-900 text-white py-12 px-4 transition-colors">
<div className="container mx-auto">
<div className="grid md:grid-cols-2 gap-8">
<div>
<div className="flex items-center space-x-2 mb-4">
<Shield className="h-8 w-8 text-blue-400" />
<span className="text-2xl font-bold">DocV</span>
<Badge variant="secondary" className="ml-2">
By 4NK
</Badge>
</div>
<p className="text-gray-400 dark:text-gray-300 mb-4">
4NK, pionnier du Web 5.0. Conçoit et développe des solutions de souveraineté.
</p>
<p className="text-gray-400 dark:text-gray-300">contact@docv.fr</p>
</div>
<div>
<h3 className="text-lg font-semibold mb-4 text-white dark:text-white">Navigation</h3>
<div className="space-y-2">
<Link href="#produit" className="block text-gray-400 dark:text-gray-300 hover:text-white transition-colors">
Le produit
</Link>
<Link href="#securite" className="block text-gray-400 dark:text-gray-300 hover:text-white transition-colors">
Sécurité
</Link>
<Link href="#tarifs" className="block text-gray-400 dark:text-gray-300 hover:text-white transition-colors">
Tarifs
</Link>
<Link href="/formation" className="block text-gray-400 dark:text-gray-300 hover:text-white transition-colors">
Formation
</Link>
<Link href="" onClick={() => setShowAuthModal(true)} className="block text-gray-400 dark:text-gray-300 hover:text-white transition-colors">
Connexion
</Link>
</div>
</div>
</div>
<div className="border-t border-gray-800 dark:border-gray-700 mt-8 pt-8 text-center text-gray-400 dark:text-gray-300">
<p>&copy; 2025 4NK. Tous droits réservés.</p>
</div>
</div>
</footer>
<Footer onAuthClick={() => setShowAuthModal(true)} />
</div>
)
}

View File

@ -0,0 +1,86 @@
import Link from "next/link"
import { Badge } from "@/components/ui/badge"
import { Shield } from "lucide-react"
interface FooterProps {
variant?: 'default' | 'dark'
showNavigation?: boolean
onAuthClick?: () => void
}
export default function Footer({
variant = 'default',
showNavigation = true,
onAuthClick
}: FooterProps) {
const getFooterStyles = () => {
switch (variant) {
case 'dark':
return "bg-gray-900 text-gray-300 py-8 px-4"
default:
return "bg-gray-900 dark:bg-gray-900 text-white py-12 px-4 transition-colors"
}
}
return (
<footer className={getFooterStyles()}>
<div className="container mx-auto">
{showNavigation ? (
<div className="grid md:grid-cols-2 gap-8">
<div>
<div className="flex items-center space-x-2 mb-4">
<Shield className="h-8 w-8 text-blue-400" />
<span className="text-2xl font-bold">DocV</span>
<Badge variant="secondary" className="ml-2">
By 4NK
</Badge>
</div>
<p className="text-gray-400 dark:text-gray-300 mb-4">
4NK, pionnier du Web 5.0. Conçoit et développe des solutions de souveraineté.
</p>
<p className="text-gray-400 dark:text-gray-300">contact@docv.fr</p>
</div>
<div>
<h3 className="text-lg font-semibold mb-4 text-white dark:text-white">Navigation</h3>
<div className="space-y-2">
<Link href="#produit" className="block text-gray-400 dark:text-gray-300 hover:text-white transition-colors">
Le produit
</Link>
<Link href="#securite" className="block text-gray-400 dark:text-gray-300 hover:text-white transition-colors">
Sécurité
</Link>
<Link href="#tarifs" className="block text-gray-400 dark:text-gray-300 hover:text-white transition-colors">
Tarifs
</Link>
<Link href="/formation" className="block text-gray-400 dark:text-gray-300 hover:text-white transition-colors">
Formation
</Link>
<Link href="" onClick={onAuthClick} className="block text-gray-400 dark:text-gray-300 hover:text-white transition-colors">
Connexion
</Link>
</div>
</div>
</div>
) : (
<div className="text-center">
<div className="flex items-center justify-center space-x-2 mb-4">
<Shield className="h-6 w-6 text-blue-400" />
<span className="text-xl font-bold text-gray-100">DocV</span>
<Badge variant="secondary" className="bg-gray-700 text-gray-200">By 4NK</Badge>
</div>
<p className="text-gray-400">
4NK, pionnier du Web 5.0 - Solutions de souveraineté numérique
</p>
</div>
)}
{showNavigation && (
<div className="border-t border-gray-800 dark:border-gray-700 mt-8 pt-8 text-center text-gray-400 dark:text-gray-300">
<p>&copy; 2025 4NK. Tous droits réservés.</p>
</div>
)}
</div>
</footer>
)
}

View File

@ -0,0 +1,103 @@
import Link from "next/link"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Shield, ArrowLeft } from "lucide-react"
interface HeaderProps {
variant?: 'default' | 'dark' | 'dashboard'
showAuth?: boolean
showBackButton?: boolean
backHref?: string
backText?: string
onAuthClick?: () => void
}
export default function Header({
variant = 'default',
showAuth = true,
showBackButton = false,
backHref = "/",
backText = "Retour à l'accueil",
onAuthClick
}: HeaderProps) {
const getHeaderStyles = () => {
switch (variant) {
case 'dark':
return "border-b border-gray-700 bg-gray-800/80 backdrop-blur-sm"
case 'dashboard':
return "border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900"
default:
return "border-b bg-white/80 dark:bg-gray-900/80 backdrop-blur-sm sticky top-0 z-50 transition-colors duration-300"
}
}
const getLogoStyles = () => {
switch (variant) {
case 'dark':
return {
shield: "h-8 w-8 text-blue-400",
text: "text-2xl font-bold text-gray-100",
badge: "ml-2 bg-gray-700 text-gray-200"
}
case 'dashboard':
return {
shield: "h-8 w-8 text-blue-600 dark:text-blue-400",
text: "text-xl font-bold text-gray-900 dark:text-gray-100",
badge: "ml-2"
}
default:
return {
shield: "h-8 w-8 text-blue-600 dark:text-blue-400",
text: "text-2xl font-bold text-gray-900 dark:text-gray-100",
badge: "ml-2"
}
}
}
const logoStyles = getLogoStyles()
return (
<header className={getHeaderStyles()}>
<div className="container mx-auto px-4 py-4 flex justify-between items-center">
<Link href="/" className="flex items-center space-x-2">
<Shield className={logoStyles.shield} />
<span className={logoStyles.text}>DocV</span>
<Badge variant="secondary" className={logoStyles.badge}>
By 4NK
</Badge>
</Link>
<div className="flex items-center space-x-4">
{showBackButton && (
<Link href={backHref} className="flex items-center text-blue-400 hover:text-blue-500">
<ArrowLeft className="h-4 w-4 mr-2" />
{backText}
</Link>
)}
{showAuth && variant === 'default' && (
<nav className="hidden md:flex items-center space-x-6">
<Link href="#produit" className="text-gray-600 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors duration-300">
Le produit
</Link>
<Link href="#securite" className="text-gray-600 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors duration-300">
Sécurité
</Link>
<Link href="#tarifs" className="text-gray-600 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors duration-300">
Tarifs
</Link>
<Link href="/formation">
<Button variant="outline" className="dark:border-gray-700 dark:text-gray-200 dark:hover:bg-gray-800 transition-colors duration-300">
Formation
</Button>
</Link>
<Button onClick={onAuthClick} className="dark:bg-blue-700 dark:hover:bg-blue-600 transition-colors duration-300">
Connexion
</Button>
</nav>
)}
</div>
</div>
</header>
)
}

View File

@ -0,0 +1,2 @@
export { default as Header } from './Header'
export { default as Footer } from './Footer'

View File

@ -0,0 +1,116 @@
import Link from "next/link"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Clock, Users, LucideIcon } from "lucide-react"
interface FormationCardProps {
icon: LucideIcon
title: string
description: string
program: string[]
specialization: {
title: string
items: string[]
}
duration: string
maxParticipants: string
color: 'red' | 'green' | 'blue'
href?: string
}
export default function FormationCard({
icon: Icon,
title,
description,
program,
specialization,
duration,
maxParticipants,
color,
href = "/formation/devis"
}: FormationCardProps) {
const getColorClasses = () => {
switch (color) {
case 'red':
return {
card: "border-2 border-gray-700 hover:border-red-600 bg-gray-800 hover:shadow-xl transition-all duration-300",
icon: "h-16 w-16 text-red-400 mx-auto mb-4",
title: "text-2xl text-red-300",
specialization: "bg-red-900",
specializationTitle: "font-semibold text-red-200 mb-2",
specializationItems: "text-sm text-red-300 space-y-1",
button: "w-full bg-red-600 hover:bg-red-700 text-white"
}
case 'green':
return {
card: "border-2 border-gray-700 hover:border-green-600 bg-gray-800 hover:shadow-xl transition-all duration-300",
icon: "h-16 w-16 text-green-400 mx-auto mb-4",
title: "text-2xl text-green-300",
specialization: "bg-green-900",
specializationTitle: "font-semibold text-green-200 mb-2",
specializationItems: "text-sm text-green-300 space-y-1",
button: "w-full bg-green-600 hover:bg-green-700 text-white"
}
case 'blue':
return {
card: "border-2 border-gray-700 hover:border-blue-600 bg-gray-800 hover:shadow-xl transition-all duration-300",
icon: "h-16 w-16 text-blue-400 mx-auto mb-4",
title: "text-2xl text-blue-300",
specialization: "bg-blue-900",
specializationTitle: "font-semibold text-blue-200 mb-2",
specializationItems: "text-sm text-blue-300 space-y-1",
button: "w-full bg-blue-600 hover:bg-blue-700 text-white"
}
}
}
const colorClasses = getColorClasses()
return (
<Card className={colorClasses.card}>
<CardHeader className="text-center">
<Icon className={colorClasses.icon} />
<CardTitle className={colorClasses.title}>{title}</CardTitle>
<CardDescription className="text-lg text-gray-300">
{description}
</CardDescription>
</CardHeader>
<CardContent className="space-y-6 text-gray-300">
<div>
<h4 className="font-semibold mb-3">Programme de formation :</h4>
<ul className="space-y-2">
{program.map((item, index) => (
<li key={index}> {item}</li>
))}
</ul>
</div>
<div className={`${colorClasses.specialization} p-4 rounded-lg`}>
<h5 className={colorClasses.specializationTitle}>{specialization.title}</h5>
<ul className={colorClasses.specializationItems}>
{specialization.items.map((item, index) => (
<li key={index}> {item}</li>
))}
</ul>
</div>
<div className="flex items-center justify-between text-sm text-gray-400">
<div className="flex items-center">
<Clock className="h-4 w-4 mr-1" />
{duration}
</div>
<div className="flex items-center">
<Users className="h-4 w-4 mr-1" />
{maxParticipants}
</div>
</div>
<Link href={href}>
<Button className={colorClasses.button}>
S'inscrire à la formation
</Button>
</Link>
</CardContent>
</Card>
)
}

View File

@ -0,0 +1,129 @@
import Link from "next/link"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { CheckCircle, Database, Zap, Users, LucideIcon } from "lucide-react"
interface TokenBreakdown {
icon: LucideIcon
title: string
value: string
description: string
color: 'blue' | 'green' | 'purple'
}
interface PricingFeature {
text: string
}
interface PricingCardProps {
title: string
price: string
tokensIncluded: string
tokenBreakdown: TokenBreakdown[]
features: PricingFeature[]
ctaText: string
ctaHref: string
variant?: 'default' | 'featured'
}
export default function PricingCard({
title,
price,
tokensIncluded,
tokenBreakdown,
features,
ctaText,
ctaHref,
variant = 'default'
}: PricingCardProps) {
const getColorClasses = (color: 'blue' | 'green' | 'purple') => {
switch (color) {
case 'blue':
return {
bg: "bg-blue-50 dark:bg-blue-800",
icon: "text-blue-600 dark:text-blue-300",
title: "text-blue-800 dark:text-blue-200",
value: "text-blue-600 dark:text-blue-300",
description: "text-blue-700 dark:text-blue-200"
}
case 'green':
return {
bg: "bg-green-50 dark:bg-green-800",
icon: "text-green-600 dark:text-green-300",
title: "text-green-800 dark:text-green-200",
value: "text-green-600 dark:text-green-300",
description: "text-green-700 dark:text-green-200"
}
case 'purple':
return {
bg: "bg-purple-50 dark:bg-purple-800",
icon: "text-purple-600 dark:text-purple-300",
title: "text-purple-800 dark:text-purple-200",
value: "text-purple-600 dark:text-purple-300",
description: "text-purple-700 dark:text-purple-200"
}
}
}
return (
<Card className="border-2 border-blue-200 bg-blue-50 dark:bg-blue-900 dark:border-blue-700 transition-colors">
<CardHeader className="text-center">
<CardTitle className="text-3xl font-bold text-blue-700 dark:text-blue-400">
{title}
</CardTitle>
<CardDescription className="text-2xl font-semibold text-blue-600 dark:text-blue-300">
{price}
</CardDescription>
<Badge className="bg-green-600 text-white text-lg px-4 py-2 mt-2 dark:bg-green-500">
{tokensIncluded}
</Badge>
</CardHeader>
<CardContent>
{/* Token Breakdown */}
<div className="bg-white dark:bg-gray-800 p-6 rounded-lg mb-6 transition-colors">
<h3 className="text-xl font-bold text-gray-900 dark:text-white mb-4 text-center">
🎯 Que comprennent {tokensIncluded} ?
</h3>
<div className="grid md:grid-cols-3 gap-6">
{tokenBreakdown.map((item, index) => {
const colorClasses = getColorClasses(item.color)
const Icon = item.icon
return (
<div key={index} className={`text-center p-4 ${colorClasses.bg} rounded-lg transition-colors`}>
<Icon className={`h-8 w-8 mx-auto ${colorClasses.icon} mb-2`} />
<h4 className={`font-semibold ${colorClasses.title}`}>{item.title}</h4>
<p className={`text-2xl font-bold ${colorClasses.value}`}>{item.value}</p>
<p className={`text-sm ${colorClasses.description}`}>{item.description}</p>
</div>
)
})}
</div>
</div>
{/* Features */}
<div className="space-y-3 mb-6 text-gray-900 dark:text-gray-200">
{features.map((feature, index) => (
<div key={index} className="flex items-center">
<CheckCircle className="h-5 w-5 text-green-600 dark:text-green-300 mr-3" />
<span>{feature.text}</span>
</div>
))}
</div>
{/* CTA */}
<div className="text-center">
<p className="text-lg font-semibold text-blue-700 dark:text-blue-400 mb-4">
Tarification à la consommation + setup personnalisé
</p>
<Link href={ctaHref}>
<Button size="lg" className="w-full">
{ctaText}
</Button>
</Link>
</div>
</CardContent>
</Card>
)
}

View File

@ -0,0 +1,41 @@
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { LucideIcon } from "lucide-react"
interface ProductCardProps {
icon: LucideIcon
title: string
description: string[]
variant?: 'default' | 'gradient'
}
export default function ProductCard({
icon: Icon,
title,
description,
variant = 'default'
}: ProductCardProps) {
const getCardStyles = () => {
switch (variant) {
case 'gradient':
return "border-2 border-gray-200 dark:border-gray-700 hover:border-blue-200 dark:hover:border-blue-400 transition-colors duration-300 bg-gradient-to-br from-white to-blue-50 dark:from-gray-800 dark:to-blue-900"
default:
return "border-2 border-gray-200 dark:border-gray-700 hover:border-blue-200 dark:hover:border-blue-400 transition-colors duration-300"
}
}
return (
<Card className={getCardStyles()}>
<CardHeader>
<Icon className="h-12 w-12 text-blue-600 dark:text-blue-400 mb-4" />
<CardTitle className="dark:text-gray-100">{title}</CardTitle>
</CardHeader>
<CardContent>
{description.map((text, index) => (
<p key={index} className="text-gray-600 dark:text-gray-300 mb-4 last:mb-0">
{text}
</p>
))}
</CardContent>
</Card>
)
}

View File

@ -61,8 +61,19 @@ export default class MessageBus {
}
unsubscribe();
this.destroyMessageListener();
// Connect with tokens first
UserStore.getInstance().connect(accessToken, refreshToken);
// Then get and set the pairing ID
this.getUserPairingId().then((pairingId: string) => {
UserStore.getInstance().pair(pairingId);
resolve();
}).catch((error: string) => {
console.error('Failed to get pairing ID after authentication:', error);
// Still resolve since the main authentication succeeded
resolve();
});
});
const unsubscribeError = EventBus.getInstance().on('ERROR_LINK_ACCEPTED', (responseId: string, error: string) => {

137
lib/constants.ts Normal file
View File

@ -0,0 +1,137 @@
// Company Information
export const COMPANY_INFO = {
name: "DocV",
tagline: "By 4NK",
email: "contact@docv.fr",
description: "DocV propose une approche révolutionnaire de la gestion d'identité, garantissant sécurité, souveraineté et conformité dans la gestion de vos documents et processus métier.",
fullDescription: "4NK, pionnier du Web 5.0. Conçoit et développe des solutions de souveraineté.",
copyright: "© 2025 4NK. Tous droits réservés."
}
// External URLs
export const EXTERNAL_URLS = {
iframe: 'https://dev3.4nkweb.com',
gitRepository: 'https://git.4nkweb.com',
lecoffre: 'https://lecoffre.io'
}
// Navigation Links
export const NAV_LINKS = {
product: "#produit",
security: "#securite",
pricing: "#tarifs",
formation: "/formation",
contact: "/contact",
dashboard: "/dashboard"
}
// Pricing Information
export const PRICING = {
discoveryOffer: {
title: "Offre Découverte",
price: "2990 € HT / mois",
tokensIncluded: "1000 jetons inclus",
tokenBreakdown: [
{
title: "Stockage permanent",
value: "1 To",
description: "Documents chiffrés et sécurisés",
color: "blue" as const
},
{
title: "Stockage temporaire",
value: "100 Go",
description: "Traitement IA et OCR",
color: "green" as const
},
{
title: "Nouveaux dossiers",
value: "75",
description: "Par mois maximum",
color: "purple" as const
}
],
features: [
"Pas de coût par utilisateur",
"Pas de surcoût pour l'IA embarquée",
"Pas de frais de licence à la signature ou au document",
"Pas de facturation par API ou par traitement"
]
}
}
// Formation Data
export const FORMATIONS = {
cybersecurity: {
title: "Cybersécurité",
description: "Maîtrisez les fondamentaux de la sécurité informatique",
duration: "5 jours",
maxParticipants: "Max 12 pers.",
color: "red" as const,
program: [
"Analyse des menaces et vulnérabilités",
"Cryptographie appliquée et PKI",
"Sécurisation des infrastructures",
"Gestion des incidents de sécurité",
"Audit et conformité (ISO 27001, RGPD)",
"Tests d'intrusion et pentest"
],
specialization: {
title: "Spécialisation DocV :",
items: [
"Authentification sans mot de passe",
"Chiffrement de bout en bout",
"Blockchain et preuves cryptographiques",
"Architecture zero-trust"
]
}
},
digitalHygiene: {
title: "Hygiène Numérique",
description: "Adoptez les bonnes pratiques pour un environnement numérique sain",
duration: "3 jours",
maxParticipants: "Max 15 pers.",
color: "green" as const,
program: [
"Gestion sécurisée des mots de passe",
"Protection de la vie privée en ligne",
"Sécurisation des communications",
"Sauvegarde et archivage sécurisé",
"Sensibilisation aux risques numériques",
"RGPD et protection des données"
],
specialization: {
title: "Approche DocV :",
items: [
"Identité numérique souveraine",
"Gestion documentaire sécurisée",
"Réduction de l'empreinte numérique",
"Autonomie technologique"
]
}
},
sovereignDevelopment: {
title: "Développement Souverain",
description: "Créez des applications indépendantes et sécurisées",
duration: "7 jours",
maxParticipants: "Max 8 pers.",
color: "blue" as const,
program: [
"Architecture décentralisée",
"Développement sans dépendances cloud",
"Intégration blockchain et cryptographie",
"APIs souveraines et sécurisées",
"Déploiement on-premise",
"Maintenance et évolutivité"
],
specialization: {
title: "Technologies DocV :",
items: [
"Stack technologique souveraine",
"Intégration IA locale",
"Gestion d'identité décentralisée",
"Protocoles de communication sécurisés"
]
}
}
}