Compare commits
10 Commits
4b46621cac
...
178d1259b5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
178d1259b5 | ||
|
|
f1da6096bc | ||
|
|
b52f17f2f2 | ||
|
|
5232f6ef70 | ||
|
|
698f7cbe86 | ||
|
|
8bd5968f77 | ||
|
|
c529273c0f | ||
|
|
f8e15f765c | ||
|
|
7af3144470 | ||
|
|
02036f7ef4 |
@ -41,7 +41,6 @@ import {
|
|||||||
CloudUpload,
|
CloudUpload,
|
||||||
Cloud,
|
Cloud,
|
||||||
HardDrive,
|
HardDrive,
|
||||||
Brain,
|
|
||||||
FileQuestion,
|
FileQuestion,
|
||||||
Timer,
|
Timer,
|
||||||
ShieldCheck,
|
ShieldCheck,
|
||||||
@ -707,46 +706,6 @@ export default function FoldersPage() {
|
|||||||
setShowAuthModal(false);
|
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
|
// Fonction pour envoyer une notification dans le chat du dossier
|
||||||
const sendFolderChatNotification = (folderId: string, message: string, actionType: string) => {
|
const sendFolderChatNotification = (folderId: string, message: string, actionType: string) => {
|
||||||
@ -1507,13 +1466,6 @@ export default function FoldersPage() {
|
|||||||
Déconnexion 4NK
|
Déconnexion 4NK
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{/* Debug PairingId */}
|
|
||||||
{!userPairingId && (
|
|
||||||
<Button variant="outline" size="sm" onClick={handleForceGetPairingId}>
|
|
||||||
<Brain className="h-4 w-4 mr-2" />
|
|
||||||
Debug PairingId
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Button variant="outline" size="sm" onClick={handleLogin}>
|
<Button variant="outline" size="sm" onClick={handleLogin}>
|
||||||
|
|||||||
@ -26,7 +26,6 @@ import {
|
|||||||
HardDrive,
|
HardDrive,
|
||||||
X,
|
X,
|
||||||
FolderPlus,
|
FolderPlus,
|
||||||
Brain,
|
|
||||||
XCircle,
|
XCircle,
|
||||||
Info,
|
Info,
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
@ -347,46 +346,6 @@ export default function DashboardPage() {
|
|||||||
|
|
||||||
// 4NK handlers
|
// 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 (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
@ -418,15 +377,6 @@ export default function DashboardPage() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -2,25 +2,21 @@ import Link from "next/link"
|
|||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||||
import { Badge } from "@/components/ui/badge"
|
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() {
|
export default function FormationPage() {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-900 text-gray-100">
|
<div className="min-h-screen bg-gray-900 text-gray-100">
|
||||||
{/* Header */}
|
<Header
|
||||||
<header className="border-b border-gray-700 bg-gray-800/80 backdrop-blur-sm">
|
variant="dark"
|
||||||
<div className="container mx-auto px-4 py-4 flex justify-between items-center">
|
showAuth={false}
|
||||||
<Link href="/" className="flex items-center space-x-2">
|
showBackButton={true}
|
||||||
<Shield className="h-8 w-8 text-blue-400" />
|
backHref="/"
|
||||||
<span className="text-2xl font-bold text-gray-100">DocV</span>
|
backText="Retour à l'accueil"
|
||||||
<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>
|
|
||||||
|
|
||||||
{/* Hero Section */}
|
{/* Hero Section */}
|
||||||
<section className="py-16 px-4">
|
<section className="py-16 px-4">
|
||||||
@ -66,149 +62,38 @@ export default function FormationPage() {
|
|||||||
{/* Formations Section */}
|
{/* Formations Section */}
|
||||||
<section className="py-16 px-4">
|
<section className="py-16 px-4">
|
||||||
<div className="container mx-auto grid lg:grid-cols-3 gap-8">
|
<div className="container mx-auto grid lg:grid-cols-3 gap-8">
|
||||||
{/* Cybersécurité */}
|
<FormationCard
|
||||||
<Card className="border-2 border-gray-700 hover:border-red-600 bg-gray-800 hover:shadow-xl transition-all duration-300">
|
icon={Shield}
|
||||||
<CardHeader className="text-center">
|
title={FORMATIONS.cybersecurity.title}
|
||||||
<Shield className="h-16 w-16 text-red-400 mx-auto mb-4" />
|
description={FORMATIONS.cybersecurity.description}
|
||||||
<CardTitle className="text-2xl text-red-300">Cybersécurité</CardTitle>
|
program={FORMATIONS.cybersecurity.program}
|
||||||
<CardDescription className="text-lg text-gray-300">
|
specialization={FORMATIONS.cybersecurity.specialization}
|
||||||
Maîtrisez les fondamentaux de la sécurité informatique
|
duration={FORMATIONS.cybersecurity.duration}
|
||||||
</CardDescription>
|
maxParticipants={FORMATIONS.cybersecurity.maxParticipants}
|
||||||
</CardHeader>
|
color={FORMATIONS.cybersecurity.color}
|
||||||
<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>
|
|
||||||
|
|
||||||
{/* Hygiène Numérique */}
|
<FormationCard
|
||||||
<Card className="border-2 border-gray-700 hover:border-green-600 bg-gray-800 hover:shadow-xl transition-all duration-300">
|
icon={Monitor}
|
||||||
<CardHeader className="text-center">
|
title={FORMATIONS.digitalHygiene.title}
|
||||||
<Monitor className="h-16 w-16 text-green-400 mx-auto mb-4" />
|
description={FORMATIONS.digitalHygiene.description}
|
||||||
<CardTitle className="text-2xl text-green-300">Hygiène Numérique</CardTitle>
|
program={FORMATIONS.digitalHygiene.program}
|
||||||
<CardDescription className="text-lg text-gray-300">
|
specialization={FORMATIONS.digitalHygiene.specialization}
|
||||||
Adoptez les bonnes pratiques pour un environnement numérique sain
|
duration={FORMATIONS.digitalHygiene.duration}
|
||||||
</CardDescription>
|
maxParticipants={FORMATIONS.digitalHygiene.maxParticipants}
|
||||||
</CardHeader>
|
color={FORMATIONS.digitalHygiene.color}
|
||||||
<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>
|
|
||||||
|
|
||||||
{/* Développement Souverain */}
|
<FormationCard
|
||||||
<Card className="border-2 border-gray-700 hover:border-blue-600 bg-gray-800 hover:shadow-xl transition-all duration-300">
|
icon={Code}
|
||||||
<CardHeader className="text-center">
|
title={FORMATIONS.sovereignDevelopment.title}
|
||||||
<Code className="h-16 w-16 text-blue-400 mx-auto mb-4" />
|
description={FORMATIONS.sovereignDevelopment.description}
|
||||||
<CardTitle className="text-2xl text-blue-300">Développement Souverain</CardTitle>
|
program={FORMATIONS.sovereignDevelopment.program}
|
||||||
<CardDescription className="text-lg text-gray-300">
|
specialization={FORMATIONS.sovereignDevelopment.specialization}
|
||||||
Créez des applications indépendantes et sécurisées
|
duration={FORMATIONS.sovereignDevelopment.duration}
|
||||||
</CardDescription>
|
maxParticipants={FORMATIONS.sovereignDevelopment.maxParticipants}
|
||||||
</CardHeader>
|
color={FORMATIONS.sovereignDevelopment.color}
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@ -300,19 +185,7 @@ export default function FormationPage() {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Footer */}
|
<Footer variant="dark" showNavigation={false} />
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import type { Metadata } from 'next'
|
import type { Metadata } from 'next'
|
||||||
import { Inter } from 'next/font/google'
|
import { Inter } from 'next/font/google'
|
||||||
import '../styles/globals.css'
|
import './globals.css'
|
||||||
|
|
||||||
const inter = Inter({ subsets: ['latin'] })
|
const inter = Inter({ subsets: ['latin'] })
|
||||||
|
|
||||||
|
|||||||
140
app/page.tsx
140
app/page.tsx
@ -5,11 +5,14 @@ import { useRouter } from "next/navigation"
|
|||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { Badge } from "@/components/ui/badge"
|
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 { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||||
import AuthModal from "@/components/4nk/AuthModal"
|
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() {
|
export default function HomePage() {
|
||||||
const [showAuthModal, setShowAuthModal] = useState(false)
|
const [showAuthModal, setShowAuthModal] = useState(false)
|
||||||
@ -28,25 +31,7 @@ export default function HomePage() {
|
|||||||
|
|
||||||
return (
|
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">
|
<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 onAuthClick={() => setShowAuthModal(true)} />
|
||||||
<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>
|
|
||||||
|
|
||||||
{/* Hero Section */}
|
{/* 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">
|
<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>
|
<span className="text-blue-600 dark:text-blue-400">GED simple et souveraine</span>
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8 max-w-3xl mx-auto">
|
<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>
|
</p>
|
||||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||||
<Link href="">
|
<Link href="">
|
||||||
@ -89,43 +74,34 @@ export default function HomePage() {
|
|||||||
<div className="container mx-auto">
|
<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>
|
<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">
|
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8 mb-16">
|
||||||
{/* Card 1 */}
|
<ProductCard
|
||||||
<Card className="border-2 border-gray-200 dark:border-gray-700 hover:border-blue-200 dark:hover:border-blue-400 transition-colors duration-300">
|
icon={Key}
|
||||||
<CardHeader>
|
title="Login cryptographique ultra-simplifié"
|
||||||
<Key className="h-12 w-12 text-blue-600 dark:text-blue-400 mb-4" />
|
description={[
|
||||||
<CardTitle className="dark:text-gray-100">Login cryptographique ultra-simplifié</CardTitle>
|
"Aucun mot de passe, aucun OTP, aucun mail, aucun code, aucune application.",
|
||||||
</CardHeader>
|
"Notifications transverses et temps réel sur l'avancement des traitements."
|
||||||
<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>
|
<ProductCard
|
||||||
</Card>
|
icon={Zap}
|
||||||
|
title="IA embarquée"
|
||||||
{/* Card 2 */}
|
description={[
|
||||||
<Card className="border-2 border-gray-200 dark:border-gray-700 hover:border-blue-200 dark:hover:border-blue-400 transition-colors duration-300">
|
"OCR, classification et extraction avec IA locale.",
|
||||||
<CardHeader>
|
"L'IA, ses données et ses traitements restent locaux.",
|
||||||
<Zap className="h-12 w-12 text-blue-600 dark:text-blue-400 mb-4" />
|
"Interface conversationnelle pour suivre les dossiers."
|
||||||
<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>
|
<ProductCard
|
||||||
<p className="text-gray-600 dark:text-gray-300">L'IA, ses données et ses traitements restent locaux.</p>
|
icon={Users}
|
||||||
<p className="text-gray-600 dark:text-gray-300 mt-2">Interface conversationnelle pour suivre les dossiers.</p>
|
title="Facilite l'usage quotidien"
|
||||||
</CardContent>
|
description={[
|
||||||
</Card>
|
"• Réduction massive des emails",
|
||||||
|
"• Protection des identités et accès",
|
||||||
{/* Card 3 */}
|
"• Traçabilité sur blockchain"
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Additional Features */}
|
{/* Additional Features */}
|
||||||
@ -488,51 +464,7 @@ export default function HomePage() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
{/* Footer */}
|
<Footer onAuthClick={() => setShowAuthModal(true)} />
|
||||||
<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>© 2025 4NK. Tous droits réservés.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
86
components/layout/Footer.tsx
Normal file
86
components/layout/Footer.tsx
Normal 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>© 2025 4NK. Tous droits réservés.</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
)
|
||||||
|
}
|
||||||
103
components/layout/Header.tsx
Normal file
103
components/layout/Header.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
2
components/layout/index.ts
Normal file
2
components/layout/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { default as Header } from './Header'
|
||||||
|
export { default as Footer } from './Footer'
|
||||||
116
components/ui/FormationCard.tsx
Normal file
116
components/ui/FormationCard.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
129
components/ui/PricingCard.tsx
Normal file
129
components/ui/PricingCard.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
41
components/ui/ProductCard.tsx
Normal file
41
components/ui/ProductCard.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -61,8 +61,19 @@ export default class MessageBus {
|
|||||||
}
|
}
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
this.destroyMessageListener();
|
this.destroyMessageListener();
|
||||||
|
|
||||||
|
// Connect with tokens first
|
||||||
UserStore.getInstance().connect(accessToken, refreshToken);
|
UserStore.getInstance().connect(accessToken, refreshToken);
|
||||||
resolve();
|
|
||||||
|
// 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) => {
|
const unsubscribeError = EventBus.getInstance().on('ERROR_LINK_ACCEPTED', (responseId: string, error: string) => {
|
||||||
|
|||||||
137
lib/constants.ts
Normal file
137
lib/constants.ts
Normal 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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user