fix it
This commit is contained in:
parent
25d99b4edc
commit
8de2eed463
@ -1 +1,299 @@
|
|||||||
<p>About</p>
|
<div class="about-container">
|
||||||
|
<!-- Hero Section -->
|
||||||
|
<header class="hero-section">
|
||||||
|
<div class="hero-content">
|
||||||
|
<a routerLink="/documentation" class="back-link">
|
||||||
|
<ng-icon name="lucideArrowLeft" size="18"></ng-icon>
|
||||||
|
Retour à la documentation
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="hero-badge">
|
||||||
|
<ng-icon name="lucideZap" size="16"></ng-icon>
|
||||||
|
<span>À propos de nous</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 class="hero-title">
|
||||||
|
Simplifier les paiements mobiles en
|
||||||
|
<span class="gradient-text">Afrique</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p class="hero-subtitle">
|
||||||
|
DCB Hub est la plateforme d'agrégation de paiements Direct Carrier Billing qui connecte
|
||||||
|
les marchands aux opérateurs télécoms à travers le continent africain.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="hero-actions">
|
||||||
|
<a routerLink="/documentation" class="btn btn-primary">
|
||||||
|
<ng-icon name="lucideBookOpen" size="18"></ng-icon>
|
||||||
|
Explorer la documentation
|
||||||
|
</a>
|
||||||
|
<a href="mailto:contact@dcb-hub.com" class="btn btn-secondary">
|
||||||
|
<ng-icon name="lucideMail" size="18"></ng-icon>
|
||||||
|
Nous contacter
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hero-visual">
|
||||||
|
<div class="visual-card">
|
||||||
|
<div class="visual-icon">
|
||||||
|
<ng-icon name="lucideZap" size="48"></ng-icon>
|
||||||
|
</div>
|
||||||
|
<div class="visual-rings">
|
||||||
|
<div class="ring ring-1"></div>
|
||||||
|
<div class="ring ring-2"></div>
|
||||||
|
<div class="ring ring-3"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Stats Section -->
|
||||||
|
<section class="stats-section">
|
||||||
|
<div class="stats-grid">
|
||||||
|
@for (stat of stats; track stat.label) {
|
||||||
|
<div class="stat-card" [class]="'stat-' + stat.color">
|
||||||
|
<div class="stat-icon" [class]="'icon-' + stat.color">
|
||||||
|
<ng-icon [name]="stat.icon" size="24"></ng-icon>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value">{{ stat.value }}</div>
|
||||||
|
<div class="stat-label">{{ stat.label }}</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Mission Section -->
|
||||||
|
<section class="mission-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<span class="section-badge">Notre Mission</span>
|
||||||
|
<h2 class="section-title">Démocratiser l'accès aux paiements numériques</h2>
|
||||||
|
<p class="section-subtitle">
|
||||||
|
En Afrique, plus de 50% de la population n'a pas accès aux services bancaires traditionnels,
|
||||||
|
mais la pénétration mobile dépasse 80%. Notre mission est de transformer chaque téléphone
|
||||||
|
mobile en un moyen de paiement sécurisé et accessible.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mission-grid">
|
||||||
|
<div class="mission-card">
|
||||||
|
<div class="mission-icon icon-indigo">
|
||||||
|
<ng-icon name="lucideTarget" size="28"></ng-icon>
|
||||||
|
</div>
|
||||||
|
<h3>Vision</h3>
|
||||||
|
<p>
|
||||||
|
Devenir la référence des paiements Direct Carrier Billing en Afrique,
|
||||||
|
en offrant une plateforme fiable, sécurisée et facile à intégrer pour
|
||||||
|
tous les acteurs de l'écosystème digital.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mission-card">
|
||||||
|
<div class="mission-icon icon-green">
|
||||||
|
<ng-icon name="lucideHeart" size="28"></ng-icon>
|
||||||
|
</div>
|
||||||
|
<h3>Impact</h3>
|
||||||
|
<p>
|
||||||
|
Permettre à des millions d'utilisateurs d'accéder à des services numériques
|
||||||
|
premium (streaming, gaming, éducation) grâce à la facturation sur leur
|
||||||
|
crédit mobile, sans carte bancaire.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Values Section -->
|
||||||
|
<section class="values-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<span class="section-badge">Nos Valeurs</span>
|
||||||
|
<h2 class="section-title">Ce qui nous guide au quotidien</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="values-grid">
|
||||||
|
@for (value of values; track value.title) {
|
||||||
|
<div class="value-card" [class]="'value-' + value.color">
|
||||||
|
<div class="value-icon" [class]="'icon-' + value.color">
|
||||||
|
<ng-icon [name]="value.icon" size="24"></ng-icon>
|
||||||
|
</div>
|
||||||
|
<h3 class="value-title">{{ value.title }}</h3>
|
||||||
|
<p class="value-description">{{ value.description }}</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Features Section -->
|
||||||
|
<section class="features-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<span class="section-badge">Notre Plateforme</span>
|
||||||
|
<h2 class="section-title">Une solution complète pour vos paiements</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="features-grid">
|
||||||
|
@for (feature of features; track feature.title) {
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<ng-icon [name]="feature.icon" size="24"></ng-icon>
|
||||||
|
</div>
|
||||||
|
<div class="feature-content">
|
||||||
|
<h3>{{ feature.title }}</h3>
|
||||||
|
<p>{{ feature.description }}</p>
|
||||||
|
</div>
|
||||||
|
<ng-icon name="lucideArrowRight" size="18" class="feature-arrow"></ng-icon>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Timeline Section -->
|
||||||
|
<section class="timeline-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<span class="section-badge">Notre Parcours</span>
|
||||||
|
<h2 class="section-title">Les étapes clés de notre histoire</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="timeline">
|
||||||
|
@for (milestone of milestones; track milestone.year; let i = $index; let last = $last) {
|
||||||
|
<div class="timeline-item" [class.last]="last">
|
||||||
|
<div class="timeline-marker">
|
||||||
|
<div class="marker-icon">
|
||||||
|
<ng-icon [name]="milestone.icon" size="20"></ng-icon>
|
||||||
|
</div>
|
||||||
|
@if (!last) {
|
||||||
|
<div class="marker-line"></div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="timeline-content">
|
||||||
|
<span class="timeline-year">{{ milestone.year }}</span>
|
||||||
|
<h3 class="timeline-title">{{ milestone.title }}</h3>
|
||||||
|
<p class="timeline-description">{{ milestone.description }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Partners Section -->
|
||||||
|
<section class="partners-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<span class="section-badge">Nos Partenaires</span>
|
||||||
|
<h2 class="section-title">Un écosystème de confiance</h2>
|
||||||
|
<p class="section-subtitle">
|
||||||
|
Nous collaborons avec les principaux opérateurs télécoms et régulateurs
|
||||||
|
pour offrir une couverture optimale à travers l'Afrique.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="partners-grid">
|
||||||
|
@for (partner of partners; track partner.name) {
|
||||||
|
<div class="partner-card">
|
||||||
|
<span class="partner-logo">{{ partner.logo }}</span>
|
||||||
|
<span class="partner-name">{{ partner.name }}</span>
|
||||||
|
<span class="partner-type">{{ partner.type }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Team Section -->
|
||||||
|
<section class="team-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<span class="section-badge">Notre Équipe</span>
|
||||||
|
<h2 class="section-title">Les visages derrière DCB Hub</h2>
|
||||||
|
<p class="section-subtitle">
|
||||||
|
Une équipe passionnée par la fintech et déterminée à transformer
|
||||||
|
les paiements mobiles en Afrique.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="team-grid">
|
||||||
|
@for (member of teamMembers; track member.name) {
|
||||||
|
<div class="team-card">
|
||||||
|
<div class="team-avatar">
|
||||||
|
<span>{{ member.image }}</span>
|
||||||
|
</div>
|
||||||
|
<h3 class="team-name">{{ member.name }}</h3>
|
||||||
|
<p class="team-role">{{ member.role }}</p>
|
||||||
|
@if (member.linkedin) {
|
||||||
|
<a [href]="member.linkedin" class="team-linkedin" target="_blank">
|
||||||
|
<ng-icon name="lucideLinkedin" size="18"></ng-icon>
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Contact Section -->
|
||||||
|
<section class="contact-section">
|
||||||
|
<div class="contact-card">
|
||||||
|
<div class="contact-content">
|
||||||
|
<h2>Prêt à démarrer ?</h2>
|
||||||
|
<p>
|
||||||
|
Contactez notre équipe pour discuter de votre projet et découvrir
|
||||||
|
comment DCB Hub peut vous aider à atteindre vos objectifs.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="contact-info">
|
||||||
|
<div class="contact-item">
|
||||||
|
<ng-icon name="lucideMail" size="20"></ng-icon>
|
||||||
|
<div>
|
||||||
|
<span class="contact-label">Email</span>
|
||||||
|
<a href="mailto:contact@dcb-hub.com">contact@dcb-hub.com</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="contact-item">
|
||||||
|
<ng-icon name="lucideMapPin" size="20"></ng-icon>
|
||||||
|
<div>
|
||||||
|
<span class="contact-label">Adresse</span>
|
||||||
|
<span>Dakar, Sénégal</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="contact-actions">
|
||||||
|
<a href="mailto:integration@dcb-hub.com" class="btn btn-primary btn-large">
|
||||||
|
<ng-icon name="lucideMail" size="20"></ng-icon>
|
||||||
|
Contactez-nous
|
||||||
|
</a>
|
||||||
|
<a routerLink="/documentation" class="btn btn-secondary btn-large">
|
||||||
|
<ng-icon name="lucideBookOpen" size="20"></ng-icon>
|
||||||
|
Documentation
|
||||||
|
</a>
|
||||||
|
<a routerLink="/faq" class="btn btn-secondary btn-large">
|
||||||
|
<ng-icon name="lucideHelpCircle" size="20"></ng-icon>
|
||||||
|
FAQ
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="about-footer">
|
||||||
|
<div class="footer-content">
|
||||||
|
<div class="footer-brand">
|
||||||
|
<ng-icon name="lucideZap" size="20"></ng-icon>
|
||||||
|
<span class="brand-name">DCB Hub</span>
|
||||||
|
<span class="brand-divider">|</span>
|
||||||
|
<span class="brand-tagline">Paiements simplifiés</span>
|
||||||
|
</div>
|
||||||
|
<div class="footer-links">
|
||||||
|
<a routerLink="/documentation">Documentation</a>
|
||||||
|
<a routerLink="/faq">FAQ</a>
|
||||||
|
<a href="mailto:support@dcb-hub.com">Support</a>
|
||||||
|
</div>
|
||||||
|
<div class="footer-social">
|
||||||
|
<a href="#" class="social-link" aria-label="LinkedIn">
|
||||||
|
<ng-icon name="lucideLinkedin" size="20"></ng-icon>
|
||||||
|
</a>
|
||||||
|
<a href="#" class="social-link" aria-label="Twitter">
|
||||||
|
<ng-icon name="lucideTwitter" size="20"></ng-icon>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="footer-bottom">
|
||||||
|
<p>© 2025 DCB Hub. Tous droits réservés.</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|||||||
953
src/app/modules/about/about.scss
Normal file
953
src/app/modules/about/about.scss
Normal file
@ -0,0 +1,953 @@
|
|||||||
|
// Variables - Light Theme
|
||||||
|
$bg-primary: #ffffff;
|
||||||
|
$bg-secondary: #f8fafc;
|
||||||
|
$bg-card: #ffffff;
|
||||||
|
$bg-hover: #f1f5f9;
|
||||||
|
$border-color: #e2e8f0;
|
||||||
|
$text-primary: #1e293b;
|
||||||
|
$text-secondary: #475569;
|
||||||
|
$text-muted: #94a3b8;
|
||||||
|
|
||||||
|
$indigo: #6366f1;
|
||||||
|
$green: #22c55e;
|
||||||
|
$blue: #3b82f6;
|
||||||
|
$purple: #a855f7;
|
||||||
|
$orange: #f97316;
|
||||||
|
$pink: #ec4899;
|
||||||
|
$amber: #f59e0b;
|
||||||
|
$red: #ef4444;
|
||||||
|
|
||||||
|
// Animations
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0%, 100% {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.05);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes float {
|
||||||
|
0%, 100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container
|
||||||
|
.about-container {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: $bg-secondary;
|
||||||
|
color: $text-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hero Section
|
||||||
|
.hero-section {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 3rem;
|
||||||
|
align-items: center;
|
||||||
|
padding: 2rem 1.5rem 4rem;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
padding: 3rem 2rem 5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-content {
|
||||||
|
animation: fadeIn 0.6s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-link {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: $text-secondary;
|
||||||
|
text-decoration: none;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $indigo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 9999px;
|
||||||
|
background: rgba($indigo, 0.1);
|
||||||
|
border: 1px solid rgba($indigo, 0.2);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
|
||||||
|
ng-icon {
|
||||||
|
color: $indigo;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: $indigo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-title {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.2;
|
||||||
|
color: $text-primary;
|
||||||
|
margin: 0 0 1.5rem;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
font-size: 3.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.gradient-text {
|
||||||
|
background: linear-gradient(135deg, $indigo, $purple);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-subtitle {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
color: $text-secondary;
|
||||||
|
line-height: 1.7;
|
||||||
|
margin: 0 0 2rem;
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.875rem 1.5rem;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
font-size: 0.9375rem;
|
||||||
|
font-weight: 500;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.btn-primary {
|
||||||
|
background: linear-gradient(135deg, $indigo, $purple);
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 4px 15px rgba($indigo, 0.3);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 20px rgba($indigo, 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-secondary {
|
||||||
|
background: $bg-card;
|
||||||
|
color: $text-primary;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $bg-hover;
|
||||||
|
border-color: darken($border-color, 5%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-large {
|
||||||
|
padding: 1rem 1.75rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hero Visual
|
||||||
|
.hero-visual {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
@media (max-width: 1023px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.visual-card {
|
||||||
|
position: relative;
|
||||||
|
width: 300px;
|
||||||
|
height: 300px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visual-icon {
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
border-radius: 2rem;
|
||||||
|
background: linear-gradient(135deg, $indigo, $purple);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 20px 50px rgba($indigo, 0.3);
|
||||||
|
animation: float 3s ease-in-out infinite;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visual-rings {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ring {
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px solid rgba($indigo, 0.1);
|
||||||
|
|
||||||
|
&.ring-1 {
|
||||||
|
inset: 20%;
|
||||||
|
animation: pulse 3s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ring-2 {
|
||||||
|
inset: 10%;
|
||||||
|
animation: pulse 3s ease-in-out infinite 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ring-3 {
|
||||||
|
inset: 0;
|
||||||
|
animation: pulse 3s ease-in-out infinite 1s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stats Section
|
||||||
|
.stats-section {
|
||||||
|
padding: 3rem 1.5rem;
|
||||||
|
background: $bg-card;
|
||||||
|
border-top: 1px solid $border-color;
|
||||||
|
border-bottom: 1px solid $border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 1.5rem;
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
text-align: center;
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 1rem;
|
||||||
|
background: $bg-secondary;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $color, $value in (indigo: $indigo, green: $green, blue: $blue, purple: $purple) {
|
||||||
|
&.stat-#{$color}:hover {
|
||||||
|
border-color: rgba($value, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon {
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0 auto 1rem;
|
||||||
|
|
||||||
|
&.icon-indigo {
|
||||||
|
background: rgba($indigo, 0.1);
|
||||||
|
color: $indigo;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-green {
|
||||||
|
background: rgba($green, 0.1);
|
||||||
|
color: $green;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-blue {
|
||||||
|
background: rgba($blue, 0.1);
|
||||||
|
color: $blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-purple {
|
||||||
|
background: rgba($purple, 0.1);
|
||||||
|
color: $purple;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $text-primary;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: $text-secondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section Common Styles
|
||||||
|
.section-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-badge {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.375rem 0.875rem;
|
||||||
|
border-radius: 9999px;
|
||||||
|
background: rgba($indigo, 0.1);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $indigo;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $text-primary;
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-subtitle {
|
||||||
|
font-size: 1.0625rem;
|
||||||
|
color: $text-secondary;
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mission Section
|
||||||
|
.mission-section {
|
||||||
|
padding: 5rem 1.5rem;
|
||||||
|
max-width: 1000px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mission-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 1.5rem;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mission-card {
|
||||||
|
padding: 2rem;
|
||||||
|
background: $bg-card;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
border-radius: 1rem;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text-primary;
|
||||||
|
margin: 0 0 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 0.9375rem;
|
||||||
|
color: $text-secondary;
|
||||||
|
line-height: 1.7;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mission-icon {
|
||||||
|
width: 3.5rem;
|
||||||
|
height: 3.5rem;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
|
||||||
|
&.icon-indigo {
|
||||||
|
background: rgba($indigo, 0.1);
|
||||||
|
color: $indigo;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-green {
|
||||||
|
background: rgba($green, 0.1);
|
||||||
|
color: $green;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values Section
|
||||||
|
.values-section {
|
||||||
|
padding: 5rem 1.5rem;
|
||||||
|
background: $bg-card;
|
||||||
|
}
|
||||||
|
|
||||||
|
.values-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 1.5rem;
|
||||||
|
max-width: 1000px;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
@media (min-width: 640px) {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-card {
|
||||||
|
padding: 1.5rem;
|
||||||
|
background: $bg-secondary;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
border-radius: 1rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $color, $value in (indigo: $indigo, green: $green, amber: $amber, purple: $purple) {
|
||||||
|
&.value-#{$color}:hover {
|
||||||
|
border-color: rgba($value, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-icon {
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
|
||||||
|
&.icon-indigo {
|
||||||
|
background: rgba($indigo, 0.1);
|
||||||
|
color: $indigo;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-green {
|
||||||
|
background: rgba($green, 0.1);
|
||||||
|
color: $green;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-amber {
|
||||||
|
background: rgba($amber, 0.1);
|
||||||
|
color: $amber;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-purple {
|
||||||
|
background: rgba($purple, 0.1);
|
||||||
|
color: $purple;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-title {
|
||||||
|
font-size: 1.0625rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text-primary;
|
||||||
|
margin: 0 0 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-description {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: $text-secondary;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Features Section
|
||||||
|
.features-section {
|
||||||
|
padding: 5rem 1.5rem;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.features-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: 1.25rem 1.5rem;
|
||||||
|
background: $bg-card;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $bg-hover;
|
||||||
|
border-color: rgba($indigo, 0.3);
|
||||||
|
|
||||||
|
.feature-arrow {
|
||||||
|
transform: translateX(4px);
|
||||||
|
color: $indigo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-icon {
|
||||||
|
width: 2.75rem;
|
||||||
|
height: 2.75rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background: rgba($indigo, 0.1);
|
||||||
|
color: $indigo;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-content {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text-primary;
|
||||||
|
margin: 0 0 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: $text-secondary;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-arrow {
|
||||||
|
color: $text-muted;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeline Section
|
||||||
|
.timeline-section {
|
||||||
|
padding: 5rem 1.5rem;
|
||||||
|
background: $bg-card;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-item {
|
||||||
|
display: flex;
|
||||||
|
gap: 1.5rem;
|
||||||
|
padding-bottom: 2rem;
|
||||||
|
|
||||||
|
&.last {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-marker {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.marker-icon {
|
||||||
|
width: 2.75rem;
|
||||||
|
height: 2.75rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: linear-gradient(135deg, $indigo, $purple);
|
||||||
|
color: white;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
box-shadow: 0 4px 15px rgba($indigo, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.marker-line {
|
||||||
|
flex: 1;
|
||||||
|
width: 2px;
|
||||||
|
background: linear-gradient(to bottom, $indigo, rgba($indigo, 0.1));
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-content {
|
||||||
|
flex: 1;
|
||||||
|
padding-top: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-year {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.25rem 0.625rem;
|
||||||
|
background: rgba($indigo, 0.1);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $indigo;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-title {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text-primary;
|
||||||
|
margin: 0 0 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-description {
|
||||||
|
font-size: 0.9375rem;
|
||||||
|
color: $text-secondary;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Partners Section
|
||||||
|
.partners-section {
|
||||||
|
padding: 5rem 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partners-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 1rem;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 1.5rem 2rem;
|
||||||
|
background: $bg-card;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
border-radius: 1rem;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
min-width: 140px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-logo {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-name {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-type {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: $text-muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Team Section
|
||||||
|
.team-section {
|
||||||
|
padding: 5rem 1.5rem;
|
||||||
|
background: $bg-card;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 1.5rem;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-card {
|
||||||
|
text-align: center;
|
||||||
|
padding: 1.5rem;
|
||||||
|
background: $bg-secondary;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
border-radius: 1rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-avatar {
|
||||||
|
width: 4.5rem;
|
||||||
|
height: 4.5rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: linear-gradient(135deg, rgba($indigo, 0.1), rgba($purple, 0.1));
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0 auto 1rem;
|
||||||
|
font-size: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-name {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text-primary;
|
||||||
|
margin: 0 0 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-role {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
color: $text-secondary;
|
||||||
|
margin: 0 0 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-linkedin {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background: rgba($blue, 0.1);
|
||||||
|
color: $blue;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $blue;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contact Section
|
||||||
|
.contact-section {
|
||||||
|
padding: 5rem 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-card {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 2rem;
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 2.5rem;
|
||||||
|
background: linear-gradient(135deg, rgba($indigo, 0.05), rgba($purple, 0.05));
|
||||||
|
border: 1px solid rgba($indigo, 0.15);
|
||||||
|
border-radius: 1.5rem;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-content {
|
||||||
|
h2 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $text-primary;
|
||||||
|
margin: 0 0 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: $text-secondary;
|
||||||
|
margin: 0 0 1.5rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.75rem;
|
||||||
|
|
||||||
|
ng-icon {
|
||||||
|
color: $indigo;
|
||||||
|
margin-top: 0.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: $text-muted;
|
||||||
|
margin-bottom: 0.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a, span {
|
||||||
|
font-size: 0.9375rem;
|
||||||
|
color: $text-primary;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: $indigo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Footer
|
||||||
|
.about-footer {
|
||||||
|
background: $bg-card;
|
||||||
|
border-top: 1px solid $border-color;
|
||||||
|
padding: 2rem 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1.5rem;
|
||||||
|
max-width: 1000px;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-brand {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
color: $text-secondary;
|
||||||
|
|
||||||
|
ng-icon {
|
||||||
|
color: $indigo;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-name {
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-divider {
|
||||||
|
color: $border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-tagline {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-links {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1.5rem;
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: $text-secondary;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $indigo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-social {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-link {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 2.25rem;
|
||||||
|
height: 2.25rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background: $bg-secondary;
|
||||||
|
color: $text-secondary;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $indigo;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-bottom {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
padding-top: 1.5rem;
|
||||||
|
border-top: 1px solid $border-color;
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
color: $text-muted;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,244 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { RouterLink } from '@angular/router';
|
||||||
|
import { NgIcon, provideIcons } from '@ng-icons/core';
|
||||||
|
import {
|
||||||
|
lucideZap,
|
||||||
|
lucideTarget,
|
||||||
|
lucideUsers,
|
||||||
|
lucideGlobe,
|
||||||
|
lucideShield,
|
||||||
|
lucideTrendingUp,
|
||||||
|
lucideCheckCircle,
|
||||||
|
lucideArrowRight,
|
||||||
|
lucideExternalLink,
|
||||||
|
lucideBuilding,
|
||||||
|
lucideMapPin,
|
||||||
|
lucideMail,
|
||||||
|
lucidePhone,
|
||||||
|
lucideLinkedin,
|
||||||
|
lucideTwitter,
|
||||||
|
lucideHeart,
|
||||||
|
lucideClock,
|
||||||
|
lucideAward,
|
||||||
|
lucideRocket,
|
||||||
|
lucideLayers,
|
||||||
|
lucideRefreshCw,
|
||||||
|
lucideCreditCard,
|
||||||
|
lucideSmartphone,
|
||||||
|
lucideArrowLeft,
|
||||||
|
lucideHelpCircle,
|
||||||
|
lucideBookOpen,
|
||||||
|
} from '@ng-icons/lucide';
|
||||||
|
|
||||||
|
interface TeamMember {
|
||||||
|
name: string;
|
||||||
|
role: string;
|
||||||
|
image: string;
|
||||||
|
linkedin?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Partner {
|
||||||
|
name: string;
|
||||||
|
logo: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Milestone {
|
||||||
|
year: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
icon: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Stat {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
icon: string;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Value {
|
||||||
|
icon: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-about',
|
selector: 'app-about',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule, NgIcon, RouterLink],
|
||||||
|
viewProviders: [
|
||||||
|
provideIcons({
|
||||||
|
lucideZap,
|
||||||
|
lucideTarget,
|
||||||
|
lucideUsers,
|
||||||
|
lucideGlobe,
|
||||||
|
lucideShield,
|
||||||
|
lucideTrendingUp,
|
||||||
|
lucideCheckCircle,
|
||||||
|
lucideArrowRight,
|
||||||
|
lucideExternalLink,
|
||||||
|
lucideBuilding,
|
||||||
|
lucideMapPin,
|
||||||
|
lucideMail,
|
||||||
|
lucidePhone,
|
||||||
|
lucideLinkedin,
|
||||||
|
lucideTwitter,
|
||||||
|
lucideHeart,
|
||||||
|
lucideClock,
|
||||||
|
lucideAward,
|
||||||
|
lucideRocket,
|
||||||
|
lucideLayers,
|
||||||
|
lucideRefreshCw,
|
||||||
|
lucideCreditCard,
|
||||||
|
lucideSmartphone,
|
||||||
|
lucideArrowLeft,
|
||||||
|
lucideHelpCircle,
|
||||||
|
lucideBookOpen,
|
||||||
|
}),
|
||||||
|
],
|
||||||
templateUrl: './about.html',
|
templateUrl: './about.html',
|
||||||
|
styleUrl: './about.scss',
|
||||||
})
|
})
|
||||||
export class About {}
|
export class About {
|
||||||
|
stats: Stat[] = [
|
||||||
|
{
|
||||||
|
value: '5+',
|
||||||
|
label: 'Pays couverts',
|
||||||
|
icon: 'lucideGlobe',
|
||||||
|
color: 'indigo',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '10+',
|
||||||
|
label: 'Opérateurs intégrés',
|
||||||
|
icon: 'lucideSmartphone',
|
||||||
|
color: 'green',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '99.9%',
|
||||||
|
label: 'Disponibilité API',
|
||||||
|
icon: 'lucideTrendingUp',
|
||||||
|
color: 'blue',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '50M+',
|
||||||
|
label: 'Transactions traitées',
|
||||||
|
icon: 'lucideCreditCard',
|
||||||
|
color: 'purple',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
values: Value[] = [
|
||||||
|
{
|
||||||
|
icon: 'lucideShield',
|
||||||
|
title: 'Sécurité',
|
||||||
|
description: "Protection des données et des transactions au plus haut niveau. Conformité PCI-DSS et chiffrement de bout en bout.",
|
||||||
|
color: 'indigo',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'lucideZap',
|
||||||
|
title: 'Performance',
|
||||||
|
description: "Infrastructure optimisée pour des temps de réponse minimaux. 99.9% de disponibilité garantie.",
|
||||||
|
color: 'amber',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'lucideUsers',
|
||||||
|
title: 'Partenariat',
|
||||||
|
description: "Relation de confiance avec nos marchands et opérateurs. Support dédié et accompagnement personnalisé.",
|
||||||
|
color: 'green',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'lucideRocket',
|
||||||
|
title: 'Innovation',
|
||||||
|
description: "Amélioration continue de notre plateforme. Nouvelles fonctionnalités et intégrations régulières.",
|
||||||
|
color: 'purple',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
milestones: Milestone[] = [
|
||||||
|
{
|
||||||
|
year: '2022',
|
||||||
|
title: 'Création',
|
||||||
|
description: "Lancement du projet DCB Hub avec l'ambition de simplifier les paiements mobiles en Afrique.",
|
||||||
|
icon: 'lucideRocket',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
year: '2023',
|
||||||
|
title: 'Première intégration',
|
||||||
|
description: "Intégration réussie avec Orange Money en Côte d'Ivoire. Premiers marchands connectés.",
|
||||||
|
icon: 'lucideCheckCircle',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
year: '2024',
|
||||||
|
title: 'Expansion régionale',
|
||||||
|
description: "Extension au Sénégal, Cameroun et RDC. Intégration MTN et Airtel.",
|
||||||
|
icon: 'lucideGlobe',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
year: '2025',
|
||||||
|
title: 'Plateforme unifiée',
|
||||||
|
description: "Lancement de l'API v1.0 unifiée. Dashboard backoffice et outils de monitoring avancés.",
|
||||||
|
icon: 'lucideAward',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
partners: Partner[] = [
|
||||||
|
{ name: 'Orange', logo: '🟠', type: 'Opérateur' },
|
||||||
|
{ name: 'MTN', logo: '🟡', type: 'Opérateur' },
|
||||||
|
{ name: 'Airtel', logo: '🔴', type: 'Opérateur' },
|
||||||
|
{ name: 'BCEAO', logo: '🏛️', type: 'Régulateur' },
|
||||||
|
{ name: 'BEAC', logo: '🏛️', type: 'Régulateur' },
|
||||||
|
];
|
||||||
|
|
||||||
|
teamMembers: TeamMember[] = [
|
||||||
|
{
|
||||||
|
name: 'Amadou Diallo',
|
||||||
|
role: 'CEO & Co-fondateur',
|
||||||
|
image: '👨🏾💼',
|
||||||
|
linkedin: '#',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Fatou Sow',
|
||||||
|
role: 'CTO & Co-fondatrice',
|
||||||
|
image: '👩🏾💻',
|
||||||
|
linkedin: '#',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Jean-Marc Kouadio',
|
||||||
|
role: 'Head of Partnerships',
|
||||||
|
image: '👨🏾🤝👨🏾',
|
||||||
|
linkedin: '#',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Marie Ndour',
|
||||||
|
role: 'Head of Engineering',
|
||||||
|
image: '👩🏾🔧',
|
||||||
|
linkedin: '#',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
features = [
|
||||||
|
{
|
||||||
|
icon: 'lucideLayers',
|
||||||
|
title: 'API Unifiée',
|
||||||
|
description: "Une seule intégration pour tous les opérateurs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'lucideRefreshCw',
|
||||||
|
title: 'Abonnements',
|
||||||
|
description: "Gestion automatique des paiements récurrents",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'lucideShield',
|
||||||
|
title: 'Sécurité OTP',
|
||||||
|
description: "Authentification forte par SMS",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'lucideTrendingUp',
|
||||||
|
title: 'Analytics',
|
||||||
|
description: "Tableaux de bord et rapports détaillés",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|||||||
@ -1 +1,150 @@
|
|||||||
<p>Help</p>
|
<div class="faq-container">
|
||||||
|
<!-- Header -->
|
||||||
|
<header class="faq-header">
|
||||||
|
<div class="header-content">
|
||||||
|
<a routerLink="/documentation" class="back-link">
|
||||||
|
<ng-icon name="lucideArrowLeft" size="18"></ng-icon>
|
||||||
|
Retour à la documentation
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="header-title-section">
|
||||||
|
<div class="header-badge">
|
||||||
|
<ng-icon name="lucideHelpCircle" size="16"></ng-icon>
|
||||||
|
<span>Centre d'aide</span>
|
||||||
|
</div>
|
||||||
|
<h1 class="header-title">Questions Fréquentes</h1>
|
||||||
|
<p class="header-subtitle">
|
||||||
|
Trouvez rapidement des réponses à vos questions sur l'intégration et l'utilisation du DCB Hub
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Search -->
|
||||||
|
<div class="search-container">
|
||||||
|
<div class="search-box">
|
||||||
|
<ng-icon name="lucideSearch" size="20"></ng-icon>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Rechercher une question..."
|
||||||
|
[value]="searchQuery()"
|
||||||
|
(input)="onSearch($event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p class="search-hint">{{ getTotalQuestions() }} questions disponibles</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="faq-main">
|
||||||
|
<div class="faq-content">
|
||||||
|
<!-- Quick Links -->
|
||||||
|
<nav class="quick-links">
|
||||||
|
<h3>Catégories</h3>
|
||||||
|
<div class="quick-links-grid">
|
||||||
|
@for (category of faqCategories; track category.id) {
|
||||||
|
<a
|
||||||
|
class="quick-link"
|
||||||
|
[class]="'quick-link-' + category.color"
|
||||||
|
(click)="toggleCategory(category)"
|
||||||
|
>
|
||||||
|
<div class="quick-link-icon" [class]="'icon-' + category.color">
|
||||||
|
<ng-icon [name]="category.icon" size="18"></ng-icon>
|
||||||
|
</div>
|
||||||
|
<span>{{ category.title }}</span>
|
||||||
|
<span class="quick-link-count">{{ category.questions.length }}</span>
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- FAQ Categories -->
|
||||||
|
<div class="faq-categories">
|
||||||
|
@for (category of getFilteredCategories(); track category.id) {
|
||||||
|
<div class="faq-category" [class]="'faq-category-' + category.color" [id]="category.id">
|
||||||
|
<button class="faq-category-header" (click)="toggleCategory(category)">
|
||||||
|
<div class="category-icon" [class]="'icon-' + category.color">
|
||||||
|
<ng-icon [name]="category.icon" size="22"></ng-icon>
|
||||||
|
</div>
|
||||||
|
<div class="category-info">
|
||||||
|
<h2 class="category-title">{{ category.title }}</h2>
|
||||||
|
<span class="category-count">{{ category.questions.length }} questions</span>
|
||||||
|
</div>
|
||||||
|
<div class="category-chevron" [class.open]="category.isOpen">
|
||||||
|
<ng-icon name="lucideChevronDown" size="22"></ng-icon>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
@if (category.isOpen) {
|
||||||
|
<div class="faq-questions">
|
||||||
|
@for (question of category.questions; track question.question; let i = $index) {
|
||||||
|
<div class="faq-item" [class.open]="question.isOpen">
|
||||||
|
<button class="faq-question" (click)="toggleQuestion(question)">
|
||||||
|
<span class="question-number">{{ i + 1 }}</span>
|
||||||
|
<span class="question-text">{{ question.question }}</span>
|
||||||
|
<div class="question-chevron" [class.open]="question.isOpen">
|
||||||
|
<ng-icon name="lucideChevronRight" size="18"></ng-icon>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
@if (question.isOpen) {
|
||||||
|
<div class="faq-answer">
|
||||||
|
<p>{{ question.answer }}</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (getFilteredCategories().length === 0) {
|
||||||
|
<div class="no-results">
|
||||||
|
<ng-icon name="lucideSearch" size="48"></ng-icon>
|
||||||
|
<h3>Aucun résultat trouvé</h3>
|
||||||
|
<p>Essayez avec d'autres mots-clés ou parcourez les catégories</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Support Section -->
|
||||||
|
<section class="support-section">
|
||||||
|
<div class="support-card">
|
||||||
|
<div class="support-icon">
|
||||||
|
<ng-icon name="lucideMessageSquare" size="32"></ng-icon>
|
||||||
|
</div>
|
||||||
|
<div class="support-content">
|
||||||
|
<h3>Vous ne trouvez pas votre réponse ?</h3>
|
||||||
|
<p>Notre équipe technique est disponible pour vous aider avec votre intégration.</p>
|
||||||
|
</div>
|
||||||
|
<div class="support-actions">
|
||||||
|
<a href="mailto:integration@pixpay.sn" class="btn btn-primary">
|
||||||
|
<ng-icon name="lucideMessageSquare" size="16"></ng-icon>
|
||||||
|
Contacter le support
|
||||||
|
</a>
|
||||||
|
<a routerLink="/documentation" class="btn btn-secondary">
|
||||||
|
<ng-icon name="lucideExternalLink" size="16"></ng-icon>
|
||||||
|
Documentation API
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="faq-footer">
|
||||||
|
<div class="footer-content">
|
||||||
|
<div class="footer-brand">
|
||||||
|
<ng-icon name="lucideZap" size="20"></ng-icon>
|
||||||
|
<span class="brand-name">DCB Hub</span>
|
||||||
|
<span class="brand-divider">|</span>
|
||||||
|
<span class="brand-version">FAQ v1.0</span>
|
||||||
|
</div>
|
||||||
|
<div class="footer-links">
|
||||||
|
<a routerLink="/documentation">Documentation</a>
|
||||||
|
<a href="#">API Status</a>
|
||||||
|
<a href="mailto:support@dcb-hub.com">Support</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|||||||
641
src/app/modules/help/help.scss
Normal file
641
src/app/modules/help/help.scss
Normal file
@ -0,0 +1,641 @@
|
|||||||
|
// Variables - Light Theme
|
||||||
|
$bg-primary: #ffffff;
|
||||||
|
$bg-secondary: #f8fafc;
|
||||||
|
$bg-card: #ffffff;
|
||||||
|
$bg-hover: #f1f5f9;
|
||||||
|
$border-color: #e2e8f0;
|
||||||
|
$text-primary: #1e293b;
|
||||||
|
$text-secondary: #475569;
|
||||||
|
$text-muted: #94a3b8;
|
||||||
|
|
||||||
|
$indigo: #6366f1;
|
||||||
|
$green: #22c55e;
|
||||||
|
$blue: #3b82f6;
|
||||||
|
$purple: #a855f7;
|
||||||
|
$orange: #f97316;
|
||||||
|
$pink: #ec4899;
|
||||||
|
$amber: #f59e0b;
|
||||||
|
$red: #ef4444;
|
||||||
|
|
||||||
|
// Animations
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideDown {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
max-height: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
max-height: 500px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container
|
||||||
|
.faq-container {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: $bg-secondary;
|
||||||
|
color: $text-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header
|
||||||
|
.faq-header {
|
||||||
|
background: linear-gradient(135deg, $bg-primary 0%, $bg-secondary 100%);
|
||||||
|
border-bottom: 1px solid $border-color;
|
||||||
|
padding: 1.5rem 0 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-link {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: $text-secondary;
|
||||||
|
text-decoration: none;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $indigo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title-section {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 9999px;
|
||||||
|
background: rgba($indigo, 0.1);
|
||||||
|
border: 1px solid rgba($indigo, 0.2);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
|
||||||
|
ng-icon {
|
||||||
|
color: $indigo;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: $indigo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $text-primary;
|
||||||
|
margin: 0 0 0.75rem;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
font-size: 3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-subtitle {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
color: $text-secondary;
|
||||||
|
margin: 0;
|
||||||
|
max-width: 500px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search
|
||||||
|
.search-container {
|
||||||
|
max-width: 500px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 1rem 1.25rem;
|
||||||
|
background: $bg-card;
|
||||||
|
border: 2px solid $border-color;
|
||||||
|
border-radius: 1rem;
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:focus-within {
|
||||||
|
border-color: $indigo;
|
||||||
|
box-shadow: 0 4px 20px rgba($indigo, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
ng-icon {
|
||||||
|
color: $text-muted;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
flex: 1;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: $text-primary;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: $text-muted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-hint {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: $text-muted;
|
||||||
|
margin: 0.75rem 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main Content
|
||||||
|
.faq-main {
|
||||||
|
padding: 2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-content {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quick Links
|
||||||
|
.quick-links {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
color: $text-muted;
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-links-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 0.75rem;
|
||||||
|
|
||||||
|
@media (min-width: 640px) {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
grid-template-columns: repeat(6, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-link {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 1rem;
|
||||||
|
background: $bg-card;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: $text-secondary;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-link-count {
|
||||||
|
font-size: 0.625rem;
|
||||||
|
padding: 0.125rem 0.5rem;
|
||||||
|
background: $bg-secondary;
|
||||||
|
border-radius: 9999px;
|
||||||
|
color: $text-muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $color, $value in (indigo: $indigo, blue: $blue, green: $green, purple: $purple, orange: $orange, red: $red) {
|
||||||
|
&.quick-link-#{$color}:hover {
|
||||||
|
border-color: rgba($value, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-link-icon {
|
||||||
|
width: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&.icon-indigo {
|
||||||
|
background: rgba($indigo, 0.1);
|
||||||
|
color: $indigo;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-blue {
|
||||||
|
background: rgba($blue, 0.1);
|
||||||
|
color: $blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-green {
|
||||||
|
background: rgba($green, 0.1);
|
||||||
|
color: $green;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-purple {
|
||||||
|
background: rgba($purple, 0.1);
|
||||||
|
color: $purple;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-orange {
|
||||||
|
background: rgba($orange, 0.1);
|
||||||
|
color: $orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-red {
|
||||||
|
background: rgba($red, 0.1);
|
||||||
|
color: $red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FAQ Categories
|
||||||
|
.faq-categories {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-category {
|
||||||
|
background: $bg-card;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
border-radius: 1rem;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
@each $color, $value in (indigo: $indigo, blue: $blue, green: $green, purple: $purple, orange: $orange, red: $red) {
|
||||||
|
&.faq-category-#{$color} {
|
||||||
|
.faq-category-header:hover {
|
||||||
|
background: rgba($value, 0.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-icon.icon-#{$color} {
|
||||||
|
background: rgba($value, 0.1);
|
||||||
|
color: $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-category-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
width: 100%;
|
||||||
|
padding: 1.25rem 1.5rem;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-icon {
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-info {
|
||||||
|
flex: 1;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-title {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text-primary;
|
||||||
|
margin: 0 0 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-count {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: $text-muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-chevron {
|
||||||
|
color: $text-muted;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
|
||||||
|
&.open {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FAQ Questions
|
||||||
|
.faq-questions {
|
||||||
|
border-top: 1px solid $border-color;
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-item {
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.open {
|
||||||
|
background: $bg-secondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover:not(.open) {
|
||||||
|
background: $bg-hover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-question {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
width: 100%;
|
||||||
|
padding: 1rem 1.25rem;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
.question-number {
|
||||||
|
width: 1.75rem;
|
||||||
|
height: 1.75rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: $bg-secondary;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text-muted;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.open & {
|
||||||
|
background: $indigo;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-text {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 0.9375rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: $text-primary;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-chevron {
|
||||||
|
color: $text-muted;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&.open {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
color: $indigo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-answer {
|
||||||
|
padding: 0 1.25rem 1.25rem 4rem;
|
||||||
|
animation: fadeIn 0.3s ease;
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 0.9375rem;
|
||||||
|
color: $text-secondary;
|
||||||
|
line-height: 1.8;
|
||||||
|
margin: 0;
|
||||||
|
padding: 1rem;
|
||||||
|
background: $bg-card;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
border-left: 3px solid $indigo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No Results
|
||||||
|
.no-results {
|
||||||
|
text-align: center;
|
||||||
|
padding: 4rem 2rem;
|
||||||
|
color: $text-muted;
|
||||||
|
|
||||||
|
ng-icon {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text-primary;
|
||||||
|
margin: 0 0 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support Section
|
||||||
|
.support-section {
|
||||||
|
padding: 2rem 1.5rem 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.support-card {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1.5rem;
|
||||||
|
padding: 2rem;
|
||||||
|
background: linear-gradient(135deg, rgba($indigo, 0.05), rgba($purple, 0.05));
|
||||||
|
border: 1px solid rgba($indigo, 0.15);
|
||||||
|
border-radius: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
flex-direction: row;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.support-icon {
|
||||||
|
width: 4rem;
|
||||||
|
height: 4rem;
|
||||||
|
border-radius: 1rem;
|
||||||
|
background: linear-gradient(135deg, $indigo, $purple);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
flex-shrink: 0;
|
||||||
|
box-shadow: 0 4px 15px rgba($indigo, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.support-content {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text-primary;
|
||||||
|
margin: 0 0 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: $text-secondary;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.support-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
|
||||||
|
@media (min-width: 480px) {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.75rem 1.25rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
white-space: nowrap;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.btn-primary {
|
||||||
|
background: linear-gradient(135deg, $indigo, $purple);
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 2px 8px rgba($indigo, 0.3);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 4px 12px rgba($indigo, 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-secondary {
|
||||||
|
background: $bg-card;
|
||||||
|
color: $text-primary;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $bg-hover;
|
||||||
|
border-color: darken($border-color, 5%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Footer
|
||||||
|
.faq-footer {
|
||||||
|
background: $bg-card;
|
||||||
|
border-top: 1px solid $border-color;
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-content {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-brand {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
color: $text-secondary;
|
||||||
|
|
||||||
|
ng-icon {
|
||||||
|
color: $indigo;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-name {
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-divider {
|
||||||
|
color: $border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-version {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-links {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1.5rem;
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: $text-secondary;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $indigo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,280 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, signal } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { RouterLink } from '@angular/router';
|
||||||
|
import { NgIcon, provideIcons } from '@ng-icons/core';
|
||||||
|
import {
|
||||||
|
lucideHelpCircle,
|
||||||
|
lucideCode,
|
||||||
|
lucideCreditCard,
|
||||||
|
lucideRefreshCw,
|
||||||
|
lucideBell,
|
||||||
|
lucideAlertTriangle,
|
||||||
|
lucideChevronDown,
|
||||||
|
lucideChevronRight,
|
||||||
|
lucideMessageSquare,
|
||||||
|
lucideExternalLink,
|
||||||
|
lucideArrowLeft,
|
||||||
|
lucideSearch,
|
||||||
|
lucideZap,
|
||||||
|
} from '@ng-icons/lucide';
|
||||||
|
|
||||||
|
interface FaqItem {
|
||||||
|
question: string;
|
||||||
|
answer: string;
|
||||||
|
isOpen: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FaqCategory {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
icon: string;
|
||||||
|
color: string;
|
||||||
|
questions: FaqItem[];
|
||||||
|
isOpen: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-help',
|
selector: 'app-faq',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule, NgIcon, RouterLink],
|
||||||
|
viewProviders: [
|
||||||
|
provideIcons({
|
||||||
|
lucideHelpCircle,
|
||||||
|
lucideCode,
|
||||||
|
lucideCreditCard,
|
||||||
|
lucideRefreshCw,
|
||||||
|
lucideBell,
|
||||||
|
lucideAlertTriangle,
|
||||||
|
lucideChevronDown,
|
||||||
|
lucideChevronRight,
|
||||||
|
lucideMessageSquare,
|
||||||
|
lucideExternalLink,
|
||||||
|
lucideArrowLeft,
|
||||||
|
lucideSearch,
|
||||||
|
lucideZap,
|
||||||
|
}),
|
||||||
|
],
|
||||||
templateUrl: './help.html',
|
templateUrl: './help.html',
|
||||||
|
styleUrl: './help.scss',
|
||||||
})
|
})
|
||||||
export class Help {}
|
export class Help {
|
||||||
|
searchQuery = signal<string>('');
|
||||||
|
|
||||||
|
faqCategories: FaqCategory[] = [
|
||||||
|
{
|
||||||
|
id: 'general',
|
||||||
|
title: 'Questions Générales',
|
||||||
|
icon: 'lucideHelpCircle',
|
||||||
|
color: 'indigo',
|
||||||
|
isOpen: true,
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
question: "Qu'est-ce que le DCB Hub ?",
|
||||||
|
answer: "Le DCB Hub est une plateforme d'agrégation de paiements Direct Carrier Billing (DCB) qui permet aux marchands d'intégrer facilement les services de paiement mobile de plusieurs opérateurs télécoms africains (Orange, MTN, Airtel) via une API unifiée. Les clients peuvent ainsi payer leurs achats numériques directement sur leur facture mobile.",
|
||||||
|
isOpen: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: 'Quels pays et opérateurs sont supportés ?',
|
||||||
|
answer: "Actuellement, le DCB Hub supporte les opérateurs suivants : Orange (Côte d'Ivoire, Sénégal, Cameroun, RDC), MTN (Côte d'Ivoire, Cameroun), Airtel (plusieurs pays africains). La liste des opérateurs s'étend régulièrement. Utilisez l'endpoint GET /api/v1/operators pour obtenir la liste complète et à jour.",
|
||||||
|
isOpen: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: 'Quels types de paiements sont supportés ?',
|
||||||
|
answer: "Le DCB Hub supporte deux types de paiements : les paiements uniques (one-time) pour les achats ponctuels, et les abonnements récurrents (subscriptions) avec facturation automatique quotidienne, hebdomadaire ou mensuelle. Les deux modes incluent la gestion des périodes d'essai.",
|
||||||
|
isOpen: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: 'Quelles sont les limites de transaction ?',
|
||||||
|
answer: "Les limites varient selon l'opérateur et le pays. En général, conformément aux réglementations DSP2 : maximum 50€ par transaction, maximum 300€ par mois et par utilisateur. Ces limites peuvent être différentes selon les accords avec chaque opérateur. Consultez votre account manager pour les détails spécifiques.",
|
||||||
|
isOpen: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'integration',
|
||||||
|
title: 'Intégration Technique',
|
||||||
|
icon: 'lucideCode',
|
||||||
|
color: 'blue',
|
||||||
|
isOpen: false,
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
question: "Comment obtenir mes credentials d'API ?",
|
||||||
|
answer: "Pour obtenir vos credentials (Merchant ID et API Key), vous devez d'abord créer un compte partenaire sur notre plateforme. Après validation de votre dossier, vous recevrez un accès au backoffice où vous pourrez générer vos clés API pour l'environnement sandbox, puis production après les tests.",
|
||||||
|
isOpen: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "Quelle est la différence entre l'environnement Sandbox et Production ?",
|
||||||
|
answer: "L'environnement Sandbox (sandbox.dcb-hub.com) permet de tester votre intégration sans effectuer de vrais paiements. Les numéros de test et les réponses simulées sont disponibles. L'environnement Production (api.dcb-hub.com) traite les vrais paiements. Vous devez valider vos tests en sandbox avant d'accéder à la production.",
|
||||||
|
isOpen: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: 'Comment fonctionne le flux OTP ?',
|
||||||
|
answer: "Le flux OTP se déroule en 2 étapes : 1) Initiation - Vous appelez POST /api/v1/otp-challenge/initiate avec le MSISDN de l'utilisateur. Un SMS contenant un code à 6 chiffres est envoyé. 2) Validation - L'utilisateur saisit le code, vous appelez POST /api/v1/otp-challenge/{id}/verify. En cas de succès, vous recevez un token ISE2 valide pour créer des souscriptions et effectuer des charges.",
|
||||||
|
isOpen: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "Combien de temps le token ISE2 est-il valide ?",
|
||||||
|
answer: "Le token ISE2 a une validité de 24 heures après la validation OTP. Passé ce délai, vous devrez initier un nouveau challenge OTP. Pour les abonnements actifs, le token est automatiquement renouvelé lors des renouvellements. Conservez toujours le token de manière sécurisée côté serveur.",
|
||||||
|
isOpen: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "Comment détecter automatiquement l'opérateur ?",
|
||||||
|
answer: "Utilisez l'endpoint GET /api/v1/operators/detect/{msisdn} en passant le numéro de téléphone. L'API retourne l'opérateur détecté avec son code, ses fonctionnalités supportées et sa disponibilité actuelle. Cela vous permet d'adapter votre flux en fonction des capacités de l'opérateur.",
|
||||||
|
isOpen: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'payments',
|
||||||
|
title: 'Paiements & Facturation',
|
||||||
|
icon: 'lucideCreditCard',
|
||||||
|
color: 'green',
|
||||||
|
isOpen: false,
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
question: 'Comment effectuer un paiement (charge) ?',
|
||||||
|
answer: "Après avoir obtenu le token ISE2 via le flux OTP, appelez POST /api/v1/payments/charge avec les headers X-Merchant-ID, X-COUNTRY et X-OPERATOR. Le body doit contenir userToken (ISE2), amount, currency (XOF, XAF...), description et une reference unique. La réponse indique immédiatement le statut (SUCCESS, PENDING ou FAILED).",
|
||||||
|
isOpen: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: 'Comment gérer les paiements échoués ?',
|
||||||
|
answer: "En cas d'échec (status FAILED), consultez le champ failureReason pour comprendre la cause (solde insuffisant, limite atteinte, etc.). Vous pouvez retenter le paiement avec POST /api/v1/payments/{id}/retry. Implémentez également une stratégie de retry avec backoff exponentiel pour les erreurs temporaires.",
|
||||||
|
isOpen: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: 'Comment effectuer un remboursement ?',
|
||||||
|
answer: "Utilisez POST /api/v1/payments/{paymentId}/refund. Vous pouvez effectuer un remboursement total (sans amount) ou partiel (avec amount < montant original). Le remboursement est soumis aux politiques de l'opérateur et peut prendre jusqu'à 48h pour apparaître sur le compte du client.",
|
||||||
|
isOpen: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: 'Quelles devises sont supportées ?',
|
||||||
|
answer: "Les devises supportées dépendent des pays : XOF (Franc CFA BCEAO) pour l'Afrique de l'Ouest (Côte d'Ivoire, Sénégal), XAF (Franc CFA BEAC) pour l'Afrique Centrale (Cameroun), CDF (Franc congolais) pour la RDC. Utilisez toujours la devise locale du pays de l'opérateur.",
|
||||||
|
isOpen: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'subscriptions',
|
||||||
|
title: 'Abonnements',
|
||||||
|
icon: 'lucideRefreshCw',
|
||||||
|
color: 'purple',
|
||||||
|
isOpen: false,
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
question: 'Comment créer un abonnement ?',
|
||||||
|
answer: "Après validation OTP, appelez POST /api/v1/subscriptions avec userToken, userAlias (MSISDN), planId (ID du plan tarifaire configuré) et callbackUrl pour les webhooks. L'abonnement peut démarrer immédiatement ou après une période d'essai selon la configuration du plan.",
|
||||||
|
isOpen: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "Comment gérer les périodes d'essai (trial) ?",
|
||||||
|
answer: "Les périodes d'essai sont configurées au niveau du plan tarifaire. Pendant le trial, l'abonnement a le statut TRIAL et aucune facturation n'est effectuée. À la fin du trial, le statut passe à ACTIVE et la première facturation est déclenchée. Vous pouvez annuler pendant le trial sans frais.",
|
||||||
|
isOpen: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: 'Comment annuler un abonnement ?',
|
||||||
|
answer: "Appelez DELETE /api/v1/subscriptions/{id}. L'abonnement passe au statut CANCELLED et aucune facturation future ne sera effectuée. L'utilisateur conserve l'accès jusqu'à la fin de la période payée en cours. Vous recevrez un webhook subscription.cancelled.",
|
||||||
|
isOpen: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "Que se passe-t-il si un renouvellement échoue ?",
|
||||||
|
answer: "En cas d'échec de renouvellement (solde insuffisant), l'abonnement passe au statut SUSPENDED. Le système réessaie automatiquement selon une politique de retry configurable (par défaut : 3 tentatives sur 7 jours). Après épuisement des retries, l'abonnement passe à EXPIRED.",
|
||||||
|
isOpen: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'webhooks',
|
||||||
|
title: 'Webhooks & Notifications',
|
||||||
|
icon: 'lucideBell',
|
||||||
|
color: 'orange',
|
||||||
|
isOpen: false,
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
question: 'Quels événements déclenchent des webhooks ?',
|
||||||
|
answer: "Les événements webhook incluent : subscription.created, subscription.renewed, subscription.cancelled, subscription.suspended, subscription.expired, payment.success, payment.failed, payment.refunded. Chaque webhook contient l'événement, un timestamp, les données associées et une signature pour vérification.",
|
||||||
|
isOpen: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: 'Comment sécuriser les webhooks ?',
|
||||||
|
answer: "Chaque webhook inclut un header X-Webhook-Signature contenant un hash SHA-256 du body avec votre clé secrète. Vérifiez toujours cette signature avant de traiter le webhook. Exemple : signature = HMAC-SHA256(webhook_secret, request_body). Rejetez tout webhook avec une signature invalide.",
|
||||||
|
isOpen: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: 'Que faire si mon endpoint webhook est indisponible ?',
|
||||||
|
answer: "Le DCB Hub implémente une politique de retry automatique : 3 tentatives avec délais de 1min, 5min, puis 30min. Si toutes les tentatives échouent, le webhook est marqué comme failed et visible dans le backoffice. Vous pouvez rejouer manuellement les webhooks failed depuis l'interface.",
|
||||||
|
isOpen: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: 'Comment tester les webhooks en développement ?',
|
||||||
|
answer: "En environnement sandbox, vous pouvez : 1) Utiliser un service comme ngrok pour exposer votre localhost, 2) Configurer une URL de webhook temporaire dans le backoffice, 3) Déclencher des événements de test via les endpoints de simulation. Les logs webhook sont disponibles dans le backoffice pour le debugging.",
|
||||||
|
isOpen: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'errors',
|
||||||
|
title: 'Erreurs & Dépannage',
|
||||||
|
icon: 'lucideAlertTriangle',
|
||||||
|
color: 'red',
|
||||||
|
isOpen: false,
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
question: "Que signifie l'erreur 401 Unauthorized ?",
|
||||||
|
answer: "L'erreur 401 indique un problème d'authentification. Vérifiez que : 1) Votre header Authorization contient un token JWT valide et non expiré, 2) Les headers X-Merchant-ID et X-API-Key sont corrects, 3) Vos credentials correspondent à l'environnement (sandbox vs production).",
|
||||||
|
isOpen: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "Que signifie l'erreur INSUFFICIENT_BALANCE ?",
|
||||||
|
answer: "Cette erreur indique que le solde du compte mobile de l'utilisateur est insuffisant pour le montant demandé. Informez l'utilisateur et suggérez de recharger son compte. Vous pouvez proposer un montant inférieur ou retenter plus tard. Ne pas retry immédiatement car le résultat sera identique.",
|
||||||
|
isOpen: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "Que signifie l'erreur OPERATOR_UNAVAILABLE ?",
|
||||||
|
answer: "L'opérateur est temporairement indisponible (maintenance, problème réseau). C'est une erreur transitoire. Implémentez un retry avec backoff exponentiel (attendre 30s, puis 1min, puis 5min). Vérifiez le statut de l'opérateur via GET /api/v1/operators/{code}/health.",
|
||||||
|
isOpen: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "Que signifie l'erreur OTP_EXPIRED ?",
|
||||||
|
answer: "Le code OTP a expiré (validité de 5 minutes) ou le nombre maximum de tentatives (3) a été atteint. Vous devez initier un nouveau challenge OTP. Informez l'utilisateur et relancez le flux depuis POST /api/v1/otp-challenge/initiate.",
|
||||||
|
isOpen: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: 'Comment contacter le support technique ?',
|
||||||
|
answer: "Pour le support technique : 1) Consultez d'abord cette documentation et les logs dans votre backoffice, 2) Pour les questions d'intégration : integration@dcb-hub.com, 3) Pour les incidents de production : support@dcb-hub.com ou le canal Slack dédié partenaires, 4) Incluez toujours votre Merchant ID et les IDs de transaction concernés.",
|
||||||
|
isOpen: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
toggleCategory(category: FaqCategory): void {
|
||||||
|
category.isOpen = !category.isOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleQuestion(question: FaqItem): void {
|
||||||
|
question.isOpen = !question.isOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSearch(event: Event): void {
|
||||||
|
const value = (event.target as HTMLInputElement).value;
|
||||||
|
this.searchQuery.set(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilteredCategories(): FaqCategory[] {
|
||||||
|
const query = this.searchQuery().toLowerCase().trim();
|
||||||
|
if (!query) return this.faqCategories;
|
||||||
|
|
||||||
|
return this.faqCategories.map(category => ({
|
||||||
|
...category,
|
||||||
|
isOpen: true,
|
||||||
|
questions: category.questions.filter(
|
||||||
|
q => q.question.toLowerCase().includes(query) ||
|
||||||
|
q.answer.toLowerCase().includes(query)
|
||||||
|
).map(q => ({ ...q, isOpen: q.question.toLowerCase().includes(query) || q.answer.toLowerCase().includes(query) }))
|
||||||
|
})).filter(category => category.questions.length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTotalQuestions(): number {
|
||||||
|
return this.faqCategories.reduce((sum, cat) => sum + cat.questions.length, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user