Mise à jour des pages d'accueil
This commit is contained in:
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.6 MiB |
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* AuthBgCol — colonne image gauche partagée entre toutes les pages d'authentification.
|
||||
*
|
||||
* L'image /login-bg.jpg contient les deux thèmes côte à côte (dark à gauche, light à droite).
|
||||
* La classe CSS .login-bg-col utilise background-position pour focaliser sur la bonne moitié
|
||||
* selon le thème actif ([data-theme="dark"] sur <html>).
|
||||
*/
|
||||
export default function AuthBgCol({ appInfo = {} }) {
|
||||
return (
|
||||
<div className="login-bg-col">
|
||||
<div className="login-bg-overlay" aria-hidden="true" />
|
||||
<div className="login-bg-brand">
|
||||
{appInfo.iconUrl && (
|
||||
<img src={appInfo.iconUrl} alt="" width={36} height={36}
|
||||
style={{ borderRadius: 8, flexShrink: 0 }} />
|
||||
)}
|
||||
<span>{appInfo.appName || 'Crowdlending Tracker'}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -93,8 +93,8 @@ export default function FamilleEntreprises() {
|
||||
const [editTarget, setEditTarget] = useState(null); // membre à éditer
|
||||
|
||||
/* Formulaires */
|
||||
const emptyFam = { prenom: '', nom_famille: '' };
|
||||
const emptyEnt = { nom: '', type_fiscal: 'PM' };
|
||||
const emptyFam = { prenom: '', nom_famille: '', email: '' };
|
||||
const emptyEnt = { nom: '', type_fiscal: 'PM', email: '' };
|
||||
const [famForm, setFamForm] = useState(emptyFam);
|
||||
const [entForm, setEntForm] = useState(emptyEnt);
|
||||
const [saving, setSaving] = useState(false);
|
||||
@@ -116,10 +116,10 @@ export default function FamilleEntreprises() {
|
||||
setEditTarget(m);
|
||||
if (m.type === 'famille') {
|
||||
const restNom = m.prenom ? m.nom.replace(m.prenom, '').trim() : m.nom;
|
||||
setFamForm({ prenom: m.prenom || '', nom_famille: restNom });
|
||||
setFamForm({ prenom: m.prenom || '', nom_famille: restNom, email: m.email || '' });
|
||||
setModalFamille(true);
|
||||
} else {
|
||||
setEntForm({ nom: m.nom, type_fiscal: m.type_fiscal || 'PM' });
|
||||
setEntForm({ nom: m.nom, type_fiscal: m.type_fiscal || 'PM', email: m.email || '' });
|
||||
setModalEntreprise(true);
|
||||
}
|
||||
};
|
||||
@@ -142,6 +142,7 @@ export default function FamilleEntreprises() {
|
||||
prenom: famForm.prenom.trim() || null,
|
||||
type: 'famille',
|
||||
type_fiscal: 'PP',
|
||||
email: famForm.email.trim() || null,
|
||||
};
|
||||
if (editTarget) {
|
||||
await api.put(`/investisseurs/${editTarget.id}`, payload);
|
||||
@@ -163,6 +164,7 @@ export default function FamilleEntreprises() {
|
||||
prenom: null,
|
||||
type: 'entreprise',
|
||||
type_fiscal: entForm.type_fiscal,
|
||||
email: entForm.email.trim() || null,
|
||||
};
|
||||
if (editTarget) {
|
||||
await api.put(`/investisseurs/${editTarget.id}`, payload);
|
||||
@@ -279,6 +281,12 @@ export default function FamilleEntreprises() {
|
||||
onChange={e => setFamForm({ ...famForm, nom_famille: e.target.value })}
|
||||
placeholder="CROGUENNEC" />
|
||||
</div>
|
||||
<div className="modal-field">
|
||||
<label>Adresse e-mail <span className="text-muted" style={{ fontWeight: 400 }}>(optionnel)</span></label>
|
||||
<input type="email" value={famForm.email}
|
||||
onChange={e => setFamForm({ ...famForm, email: e.target.value })}
|
||||
placeholder="olivier@example.com" />
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
|
||||
@@ -317,6 +325,12 @@ export default function FamilleEntreprises() {
|
||||
<option value="SA">SA</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="modal-field">
|
||||
<label>Adresse e-mail <span className="text-muted" style={{ fontWeight: 400 }}>(optionnel)</span></label>
|
||||
<input type="email" value={entForm.email}
|
||||
onChange={e => setEntForm({ ...entForm, email: e.target.value })}
|
||||
placeholder="contact@entreprise.com" />
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
<ConfirmModal
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import AuthBgCol from '../components/AuthBgCol.jsx';
|
||||
|
||||
export default function ForgotPassword() {
|
||||
const [email, setEmail] = useState('');
|
||||
@@ -35,39 +36,8 @@ export default function ForgotPassword() {
|
||||
return (
|
||||
<div style={{ display: 'flex', minHeight: '100dvh' }}>
|
||||
|
||||
{/* ── Colonne gauche — image ───────────────────────────── */}
|
||||
<div className="login-bg-col" style={{
|
||||
flex: '1 1 50%',
|
||||
display: 'none',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
background: '#0d0d0d',
|
||||
}}>
|
||||
<img
|
||||
src="/login-bg.jpg"
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
onError={e => { e.target.style.display = 'none'; }}
|
||||
style={{
|
||||
position: 'absolute', inset: 0,
|
||||
width: '100%', height: '100%',
|
||||
objectFit: 'cover', opacity: 0.85,
|
||||
}}
|
||||
/>
|
||||
<div style={{
|
||||
position: 'absolute', bottom: 40, left: 40,
|
||||
color: '#fff',
|
||||
display: 'flex', alignItems: 'center', gap: 12,
|
||||
}}>
|
||||
{appInfo.iconUrl && (
|
||||
<img src={appInfo.iconUrl} alt="" width={36} height={36}
|
||||
style={{ borderRadius: 8, flexShrink: 0 }} />
|
||||
)}
|
||||
<span style={{ fontSize: 18, fontWeight: 600, letterSpacing: '-0.3px' }}>
|
||||
{appInfo.appName}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<AuthBgCol appInfo={appInfo} />
|
||||
|
||||
|
||||
{/* ── Colonne droite — formulaire ──────────────────────── */}
|
||||
<div style={{
|
||||
@@ -212,12 +182,6 @@ export default function ForgotPassword() {
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>{`
|
||||
@media (min-width: 768px) {
|
||||
.login-bg-col { display: block !important; }
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useState, useEffect, useRef } from 'react';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { useAuth } from '../context/AuthContext.jsx';
|
||||
import { api } from '../api.js';
|
||||
import AuthBgCol from '../components/AuthBgCol.jsx';
|
||||
|
||||
// ── Helpers ────────────────────────────────────────────────────────────────
|
||||
const DEVICE_KEY = 'cl_device_token';
|
||||
@@ -12,25 +13,6 @@ function fmtCountdown(sec) {
|
||||
return `${m}:${s}`;
|
||||
}
|
||||
|
||||
// ── Colonne image gauche (partagée) ───────────────────────────────────────
|
||||
function BgCol({ appInfo }) {
|
||||
return (
|
||||
<div className="login-bg-col" style={{
|
||||
flex: '1 1 50%', display: 'none',
|
||||
position: 'relative', overflow: 'hidden', background: '#0d0d0d',
|
||||
}}>
|
||||
<img src="/login-bg.jpg" alt="" aria-hidden="true"
|
||||
onError={e => { e.target.style.display = 'none'; }}
|
||||
style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', opacity: 0.85 }}
|
||||
/>
|
||||
<div style={{ position: 'absolute', bottom: 40, left: 40, color: '#fff', display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
{appInfo.iconUrl && <img src={appInfo.iconUrl} alt="" width={36} height={36} style={{ borderRadius: 8, flexShrink: 0 }} />}
|
||||
<span style={{ fontSize: 18, fontWeight: 600, letterSpacing: '-0.3px' }}>{appInfo.appName}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ── Header logo (colonne droite) ──────────────────────────────────────────
|
||||
function AppHeader({ appInfo }) {
|
||||
return (
|
||||
@@ -188,7 +170,7 @@ export default function Login() {
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', minHeight: '100dvh' }}>
|
||||
<BgCol appInfo={appInfo} />
|
||||
<AuthBgCol appInfo={appInfo} />
|
||||
|
||||
<div style={{
|
||||
flex: '1 1 50%', display: 'flex', flexDirection: 'column',
|
||||
@@ -381,9 +363,6 @@ export default function Login() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>{`
|
||||
@media (min-width: 768px) { .login-bg-col { display: block !important; } }
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { useAuth } from '../context/AuthContext.jsx';
|
||||
import PasswordStrength from '../components/PasswordStrength.jsx';
|
||||
import AuthBgCol from '../components/AuthBgCol.jsx';
|
||||
|
||||
export default function Register() {
|
||||
const { register } = useAuth();
|
||||
@@ -39,39 +40,8 @@ export default function Register() {
|
||||
return (
|
||||
<div style={{ display: 'flex', minHeight: '100dvh' }}>
|
||||
|
||||
{/* ── Colonne gauche — image ───────────────────────────── */}
|
||||
<div className="login-bg-col" style={{
|
||||
flex: '1 1 50%',
|
||||
display: 'none',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
background: '#0d0d0d',
|
||||
}}>
|
||||
<img
|
||||
src="/login-bg.jpg"
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
onError={e => { e.target.style.display = 'none'; }}
|
||||
style={{
|
||||
position: 'absolute', inset: 0,
|
||||
width: '100%', height: '100%',
|
||||
objectFit: 'cover', opacity: 0.85,
|
||||
}}
|
||||
/>
|
||||
<div style={{
|
||||
position: 'absolute', bottom: 40, left: 40,
|
||||
color: '#fff',
|
||||
display: 'flex', alignItems: 'center', gap: 12,
|
||||
}}>
|
||||
{appInfo.iconUrl && (
|
||||
<img src={appInfo.iconUrl} alt="" width={36} height={36}
|
||||
style={{ borderRadius: 8, flexShrink: 0 }} />
|
||||
)}
|
||||
<span style={{ fontSize: 18, fontWeight: 600, letterSpacing: '-0.3px' }}>
|
||||
{appInfo.appName}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<AuthBgCol appInfo={appInfo} />
|
||||
|
||||
|
||||
{/* ── Colonne droite — formulaire ──────────────────────── */}
|
||||
<div style={{
|
||||
@@ -255,12 +225,6 @@ export default function Register() {
|
||||
</>)} {/* fin verifyEmail ternaire */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>{`
|
||||
@media (min-width: 768px) {
|
||||
.login-bg-col { display: block !important; }
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import PasswordStrength from '../components/PasswordStrength.jsx';
|
||||
import AuthBgCol from '../components/AuthBgCol.jsx';
|
||||
|
||||
export default function ResetPassword() {
|
||||
const [params] = useSearchParams();
|
||||
@@ -42,39 +43,8 @@ export default function ResetPassword() {
|
||||
return (
|
||||
<div style={{ display: 'flex', minHeight: '100dvh' }}>
|
||||
|
||||
{/* ── Colonne gauche — image ───────────────────────────── */}
|
||||
<div className="login-bg-col" style={{
|
||||
flex: '1 1 50%',
|
||||
display: 'none',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
background: '#0d0d0d',
|
||||
}}>
|
||||
<img
|
||||
src="/login-bg.jpg"
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
onError={e => { e.target.style.display = 'none'; }}
|
||||
style={{
|
||||
position: 'absolute', inset: 0,
|
||||
width: '100%', height: '100%',
|
||||
objectFit: 'cover', opacity: 0.85,
|
||||
}}
|
||||
/>
|
||||
<div style={{
|
||||
position: 'absolute', bottom: 40, left: 40,
|
||||
color: '#fff',
|
||||
display: 'flex', alignItems: 'center', gap: 12,
|
||||
}}>
|
||||
{appInfo.iconUrl && (
|
||||
<img src={appInfo.iconUrl} alt="" width={36} height={36}
|
||||
style={{ borderRadius: 8, flexShrink: 0 }} />
|
||||
)}
|
||||
<span style={{ fontSize: 18, fontWeight: 600, letterSpacing: '-0.3px' }}>
|
||||
{appInfo.appName}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<AuthBgCol appInfo={appInfo} />
|
||||
|
||||
|
||||
{/* ── Colonne droite — formulaire ──────────────────────── */}
|
||||
<div style={{
|
||||
@@ -245,12 +215,6 @@ export default function ResetPassword() {
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>{`
|
||||
@media (min-width: 768px) {
|
||||
.login-bg-col { display: block !important; }
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Link, useSearchParams } from 'react-router-dom';
|
||||
import AuthBgCol from '../components/AuthBgCol.jsx';
|
||||
|
||||
export default function VerifyEmail() {
|
||||
const [params] = useSearchParams();
|
||||
@@ -26,19 +27,8 @@ export default function VerifyEmail() {
|
||||
return (
|
||||
<div style={{ display: 'flex', minHeight: '100dvh' }}>
|
||||
|
||||
<div className="login-bg-col" style={{
|
||||
flex: '1 1 50%', display: 'none', position: 'relative',
|
||||
overflow: 'hidden', background: '#0d0d0d',
|
||||
}}>
|
||||
<img src="/login-bg.jpg" alt="" aria-hidden="true"
|
||||
onError={e => { e.target.style.display = 'none'; }}
|
||||
style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', opacity: 0.85 }}
|
||||
/>
|
||||
<div style={{ position: 'absolute', bottom: 40, left: 40, color: '#fff', display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
{appInfo.iconUrl && <img src={appInfo.iconUrl} alt="" width={36} height={36} style={{ borderRadius: 8 }} />}
|
||||
<span style={{ fontSize: 18, fontWeight: 600 }}>{appInfo.appName}</span>
|
||||
</div>
|
||||
</div>
|
||||
<AuthBgCol appInfo={appInfo} />
|
||||
|
||||
|
||||
<div style={{
|
||||
flex: '1 1 50%', display: 'flex', flexDirection: 'column',
|
||||
@@ -118,10 +108,6 @@ export default function VerifyEmail() {
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>{`
|
||||
@media (min-width: 768px) { .login-bg-col { display: block !important; } }
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+59
-34
@@ -1899,7 +1899,10 @@ tr:hover td { background: var(--surface-2); }
|
||||
font-weight: 500;
|
||||
color: var(--text-muted);
|
||||
background: var(--surface-2);
|
||||
bord
|
||||
border-radius: 4px;
|
||||
padding: 1px 5px;
|
||||
}
|
||||
|
||||
/* ── DrillCellPanel table ── */
|
||||
.drill-table {
|
||||
width: 100% !important;
|
||||
@@ -1973,42 +1976,64 @@ tr:hover td { background: var(--surface-2); }
|
||||
border-color: var(--primary);
|
||||
color: var(--primary);
|
||||
}
|
||||
.pagination-btn:disabled {
|
||||
opacity: .35;
|
||||
cursor: default;
|
||||
}
|
||||
.pagination-pages {
|
||||
font-size: var(--fs-xs);
|
||||
color: var(--text-muted);
|
||||
padding: 0 8px;
|
||||
white-space: nowrap;
|
||||
|
||||
/* ── Pages d'authentification (login, register, forgot, reset, verify) ───── */
|
||||
.login-bg-col {
|
||||
flex: 1 1 50%;
|
||||
display: none;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
/* Image split dark/light : dark à gauche, light à droite.
|
||||
background-size: 200% auto → zoom sur une seule moitié.
|
||||
Par défaut (thème clair) : moitié droite. */
|
||||
background-image: url('/login-bg.png');
|
||||
background-size: 200% auto;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 100% 50%;
|
||||
background-color: #f0f2f8;
|
||||
}
|
||||
|
||||
/* ── Barre de filtres admin users ─────────────────────────────────────────── */
|
||||
.admin-filters {
|
||||
[data-theme="dark"] .login-bg-col {
|
||||
/* Thème sombre : moitié gauche de l'image. */
|
||||
background-position: 0% 50%;
|
||||
background-color: #0d0e1a;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.login-bg-col { display: block !important; }
|
||||
}
|
||||
|
||||
.login-bg-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
/* léger dégradé pour améliorer la lisibilité du badge app en bas */
|
||||
background: linear-gradient(to top, rgba(0,0,0,.35) 0%, transparent 40%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .login-bg-overlay {
|
||||
background: linear-gradient(to top, rgba(0,0,0,.55) 0%, transparent 40%);
|
||||
}
|
||||
|
||||
.login-bg-brand {
|
||||
position: absolute;
|
||||
bottom: 36px;
|
||||
left: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
color: #fff;
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -.3px;
|
||||
text-shadow: 0 1px 4px rgba(0,0,0,.4);
|
||||
}
|
||||
|
||||
.login-mobile-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.admin-filters input[type=search] {
|
||||
flex: 1;
|
||||
min-width: 180px;
|
||||
padding: 7px 12px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
color: var(--text);
|
||||
font-size: var(--fs-sm);
|
||||
}
|
||||
.admin-filters input[type=search]::placeholder { color: var(--text-muted); }
|
||||
.admin-filters select {
|
||||
padding: 7px 12px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
color: var(--text);
|
||||
font-size: var(--fs-sm);
|
||||
min-width: 160px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user