File size: 6,149 Bytes
74b1d78
 
4e9edac
 
6ea973b
 
108f5b4
6ea973b
108f5b4
 
 
 
 
 
d90111a
108f5b4
 
 
d90111a
108f5b4
10e2e8f
4e9edac
10e2e8f
74b1d78
 
ddb6622
74b1d78
 
 
 
4e9edac
 
 
 
 
 
 
 
 
 
74b1d78
 
 
 
4e9edac
 
74b1d78
4e9edac
8e2cc26
74b1d78
 
 
 
 
8e2cc26
4e9edac
74b1d78
4e9edac
74b1d78
4e9edac
3500e60
74b1d78
 
 
10e2e8f
4e9edac
10e2e8f
4e9edac
 
 
10e2e8f
8e2cc26
4e9edac
 
8e2cc26
6ea973b
4e9edac
 
 
 
 
 
 
 
 
 
 
 
 
74b1d78
4e9edac
 
 
 
10e2e8f
74b1d78
4e9edac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74b1d78
10e2e8f
4e9edac
 
 
 
 
 
108f5b4
4e9edac
10e2e8f
4e9edac
 
 
 
 
10e2e8f
8e2cc26
10e2e8f
 
 
d90111a
 
 
10e2e8f
d90111a
8e2cc26
 
4e9edac
10e2e8f
 
 
 
 
 
 
 
8e2cc26
4e9edac
 
 
 
 
d90111a
 
 
ae5eb96
 
d90111a
 
8e2cc26
ae5eb96
108f5b4
 
3500e60
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
import streamlit as st
import pandas as pd
import gspread
from google.oauth2.service_account import Credentials
import os
import json
import sys


# Ajout du chemin pour trouver les modules
sys.path.append(os.path.join(os.path.dirname(__file__), 'modules'))

# IMPORT DU NOUVEAU MODULE
try:
    from modules import kyc_form, map_dashboard # <-- Ajout ici
except ImportError:
    # Fallback si l'import direct échoue (structure de dossier simple)
    import kyc_form
    import map_dashboard # <-- Ajout ici
    
# ==============================================================================
# 1. CONFIGURATION DU "DIRECT DRIVER" & DESIGN
# ==============================================================================
st.set_page_config(
    page_title="Vortex-Flux | Ontology",
    page_icon="🔺",
    layout="wide",
    initial_sidebar_state="expanded"
)

# Nom exact de votre fichier Google Sheets (Doit être partagé avec l'email du bot)
SHEET_NAME = "Vortex-Flux"

# Scopes requis pour l'accès Google Drive/Sheets
SCOPES = [
    "https://www.googleapis.com/auth/spreadsheets",
    "https://www.googleapis.com/auth/drive"
]

# Injection CSS : Design Gotham (Palantir) & Space Grotesk
st.markdown("""
    <style>
    @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;700&display=swap');
    
    .stApp { background-color: #10161A; color: #F5F8FA; font-family: 'Space Grotesk', sans-serif; }
    [data-testid="stSidebar"] { background-color: #182026; border-right: 1px solid #2B95D6; }
    
    /* Boutons Cobalt */
    div.stButton > button:first-child {
        background-color: #2B95D6 !important;
        color: white !important;
        border: none !important;
        font-weight: 700;
        text-transform: uppercase;
        width: 100%;
        transition: 0.2s;
    }
    div.stButton > button:hover { background-color: #1F78B4 !important; }
    
    h1, h2, h3 { font-family: 'Space Grotesk', sans-serif !important; color: #2B95D6 !important; }
    
    </style>
    """, unsafe_allow_html=True)

# ==============================================================================
# 2. MOTEUR DE CONNEXION (GSPREAD PUR)
# ==============================================================================
@st.cache_resource
def get_gspread_client():
    """Authentification robuste via gspread sans passer par st.connection"""
    raw_json = os.environ.get("GSHEETS_JSON")
    
    if not raw_json:
        st.error("🛑 SECRET MANQUANT : 'GSHEETS_JSON' est introuvable.")
        return None

    try:
        # Création du fichier temporaire sécurisé
        creds_dict = json.loads(raw_json)
        with open("temp_creds.json", "w") as f:
            json.dump(creds_dict, f)
        
        # Authentification Google
        credentials = Credentials.from_service_account_file("temp_creds.json", scopes=SCOPES)
        client = gspread.authorize(credentials)
        return client
    except Exception as e:
        st.error(f"⚠️ Erreur d'authentification GSpread : {e}")
        return None

def get_data_from_sheet(worksheet_name):
    """Récupère les données d'un onglet sous forme de DataFrame"""
    client = get_gspread_client()
    if not client: return pd.DataFrame()
    
    try:
        # Ouverture du classeur par son nom
        sh = client.open(SHEET_NAME)
        # Sélection de l'onglet
        worksheet = sh.worksheet(worksheet_name)
        # Lecture des données
        data = worksheet.get_all_records()
        return pd.DataFrame(data)
    except gspread.WorksheetNotFound:
        # Si l'onglet n'existe pas encore, on renvoie vide sans planter
        return pd.DataFrame()
    except gspread.SpreadsheetNotFound:
        st.error(f"⚠️ Fichier Google Sheet '{SHEET_NAME}' introuvable. Vérifiez le nom et le partage.")
        return pd.DataFrame()
    except Exception as e:
        st.error(f"Erreur de lecture : {e}")
        return pd.DataFrame()

# ==============================================================================
# 3. LOGIQUE ONTOLOGIQUE (IDs)
# ==============================================================================
def generate_ontology_id(prefix, sheet_tab):
    df = get_data_from_sheet(sheet_tab)
    # Calcul ID : Nombre de lignes + 1
    next_id = len(df) + 1
    return f"{prefix}-2025-{next_id:04d}" #2025 a changé pour 2026 plus tard

# ==============================================================================
# 4. INTERFACE UTILISATEUR
# ==============================================================================
client = get_gspread_client()

if client:
    # --- BARRE LATÉRALE ---
    st.sidebar.title("🔺🔻 VORTEX-FLUX")
    st.sidebar.caption("Jumeau Numérique & Ontologie")
    st.sidebar.divider()
    
    menu = st.sidebar.radio("NAVIGATION", 
                            ["TABLEAU DE BORD", "CARTE TACTIQUE","KYC CLIENTS"]
                           )
    
    # --- A. TABLEAU DE BORD ---
    if menu == "TABLEAU DE BORD":
        st.header("OBJECT EXPLORER")
        st.success(f"✅ Liaison Directe établie avec '{SHEET_NAME}'")
        
        col1, col2, col3 = st.columns(3)
        col1.metric("CAPITAL DEHORS", "1.2M XOF", "▲ 5%")
        col2.metric("FLUX ATTENDU (J+7)", "450k XOF", "▼ 2%")
        col3.metric("SCORE LIQUIDITÉ", "8.5/10")
        
        st.divider()
        st.subheader("VUE ONTOLOGIQUE : CLIENTS")
        
        df_clients = get_data_from_sheet("Clients_KYC")
        if not df_clients.empty:
            st.dataframe(df_clients, use_container_width=True)
        else:
            st.info("ℹ️ L'onglet 'Clients_KYC' est vide ou n'existe pas encore.")
            
    # --- B. CODE DU MODULE CARTE TACTIQUE ---       
    elif menu == "CARTE TACTIQUE":
        # Appel du nouveau module
        map_dashboard.show_map_dashboard(client, SHEET_NAME)
    
    # --- C. CODE DU MODULE KYC ---
    elif menu == "KYC CLIENTS":
        # APPEL DU MODULE EXTERNE
        # C'est ici que la magie opère : on passe le client et la fonction ID au module
        kyc_form.show_kyc_form(client, SHEET_NAME, generate_ontology_id)