Oumar3c commited on
Commit
e1ee076
·
verified ·
1 Parent(s): 8c00b2b

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -1100
app.py DELETED
@@ -1,1100 +0,0 @@
1
- import streamlit as st
2
- import plotly.graph_objects as go
3
- import plotly.express as px
4
- from streamlit_lottie import st_lottie
5
- import requests
6
- import json
7
- import pandas as pd
8
- from datetime import datetime
9
- import io
10
- import base64
11
- import matplotlib.pyplot as plt
12
- import numpy as np
13
- import os
14
- import tempfile
15
- import zipfile
16
- from reportlab.lib import colors
17
- from reportlab.lib.pagesizes import A4
18
- from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image, PageBreak
19
- from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
20
- from reportlab.lib.units import cm
21
- from reportlab.lib.enums import TA_CENTER, TA_LEFT
22
-
23
- # --- CHARGEMENT DES ANIMATIONS LOTTIE ---
24
- def load_lottieurl(url):
25
- try:
26
- r = requests.get(url)
27
- if r.status_code == 200:
28
- return r.json()
29
- except:
30
- pass
31
- return None
32
-
33
- lottie_hero = load_lottieurl("https://assets2.lottiefiles.com/packages/lf20_ghp9v4re.json")
34
-
35
- # --- CONFIGURATION DE LA PAGE ---
36
- st.set_page_config(
37
- page_title="Indice de Formalisation | Cabinet Cissé",
38
- page_icon="📊",
39
- layout="wide",
40
- initial_sidebar_state="collapsed"
41
- )
42
-
43
- # --- INITIALISATION DES SESSION STATES ---
44
- if 'generated' not in st.session_state:
45
- st.session_state.generated = False
46
- if 'uploaded_files' not in st.session_state:
47
- st.session_state.uploaded_files = {}
48
-
49
- # Fonction pour convertir hex en couleur ReportLab
50
- def hex_to_reportlab_color(hex_color):
51
- try:
52
- hex_color = hex_color.lstrip('#')
53
- r = int(hex_color[0:2], 16) / 255.0
54
- g = int(hex_color[2:4], 16) / 255.0
55
- b = int(hex_color[4:6], 16) / 255.0
56
- return colors.Color(r, g, b)
57
- except:
58
- return colors.HexColor('#2563eb')
59
-
60
- # ═══════════════════════════════════════════════════════════════
61
- # FONCTION DE GÉNÉRATION DU GRAPHIQUE RADAR
62
- # ═══════════════════════════════════════════════════════════════
63
- def generate_radar_chart(scores, color_hex):
64
- labels = ['Admin', 'Compta', 'Fiscal', 'Travail', 'Finance', 'Digital']
65
- values = [
66
- scores.get('Admin.', 0),
67
- scores.get('Compta.', 0),
68
- scores.get('Fiscal.', 0),
69
- scores.get('Social.', 0),
70
- scores.get('Finance.', 0),
71
- scores.get('Digital.', 0)
72
- ]
73
-
74
- angles = np.linspace(0, 2*np.pi, len(labels), endpoint=False)
75
- values_radar = values + values[:1]
76
- angles_radar = np.concatenate((angles, [angles[0]]))
77
-
78
- fig = plt.figure(figsize=(8, 8), facecolor='white', dpi=150)
79
- ax = fig.add_subplot(111, polar=True)
80
-
81
- color_hex = color_hex.lstrip('#')
82
- rgb = tuple(int(color_hex[i:i+2], 16)/255 for i in (0, 2, 4))
83
-
84
- ax.plot(angles_radar, values_radar, 'o-', linewidth=2.5, color=rgb, markersize=6)
85
- ax.fill(angles_radar, values_radar, alpha=0.25, color=rgb)
86
- ax.set_xticks(angles)
87
- ax.set_xticklabels(labels, fontsize=11, fontweight='bold')
88
- ax.set_ylim(0, 100)
89
- ax.set_yticks([20, 40, 60, 80, 100])
90
- ax.set_yticklabels(['20', '40', '60', '80', '100'], fontsize=9)
91
- ax.grid(True, linestyle='--', alpha=0.7)
92
- ax.spines['polar'].set_visible(False)
93
-
94
- radar_path = os.path.join(tempfile.gettempdir(), 'radar_chart.png')
95
- plt.savefig(radar_path, bbox_inches='tight', dpi=150, facecolor='white')
96
- plt.close()
97
-
98
- return radar_path
99
-
100
- # ═══════════════════════════════════════════════════════════════
101
- # FONCTION DE GÉNÉRATION PDF PROFESSIONNELLE
102
- # ═══════════════════════════════════════════════════════════════
103
- def generate_professional_pdf(entreprise_info, score_global, niveau, label, color, scores, packages, uploaded_files_info):
104
-
105
- radar_path = generate_radar_chart(scores, color)
106
-
107
- buffer = io.BytesIO()
108
- doc = SimpleDocTemplate(buffer, pagesize=A4,
109
- rightMargin=1.8*cm, leftMargin=1.8*cm,
110
- topMargin=2*cm, bottomMargin=2*cm)
111
-
112
- styles = getSampleStyleSheet()
113
- reportlab_color = hex_to_reportlab_color(color)
114
-
115
- custom_title = ParagraphStyle('CustomTitle', parent=styles['Title'], fontSize=24,
116
- fontName='Helvetica-Bold', textColor=colors.HexColor('#1e3a8a'),
117
- alignment=TA_CENTER, spaceAfter=15)
118
-
119
- custom_subtitle = ParagraphStyle('CustomSubtitle', parent=styles['Normal'], fontSize=10,
120
- fontName='Helvetica', textColor=colors.HexColor('#64748b'),
121
- alignment=TA_CENTER, spaceAfter=25)
122
-
123
- custom_section = ParagraphStyle('CustomSection', parent=styles['Heading2'], fontSize=14,
124
- fontName='Helvetica-Bold', textColor=colors.HexColor('#1e40af'),
125
- spaceBefore=15, spaceAfter=12)
126
-
127
- custom_normal = ParagraphStyle('CustomNormal', parent=styles['Normal'], fontSize=10,
128
- fontName='Helvetica', textColor=colors.HexColor('#334155'),
129
- spaceAfter=6, alignment=TA_LEFT)
130
-
131
- custom_bold = ParagraphStyle('CustomBold', parent=styles['Normal'], fontSize=10,
132
- fontName='Helvetica-Bold', textColor=colors.HexColor('#1e293b'),
133
- spaceAfter=4)
134
-
135
- score_style = ParagraphStyle('ScoreStyle', parent=styles['Normal'], fontSize=52,
136
- fontName='Helvetica-Bold', textColor=reportlab_color,
137
- alignment=TA_CENTER, spaceAfter=8)
138
-
139
- story = []
140
-
141
- # ==================== PAGE 1 ====================
142
- header_frame = Table([["CABINET CONSEIL CISSÉ"]], colWidths=[16*cm])
143
- header_frame.setStyle(TableStyle([
144
- ('BACKGROUND', (0, 0), (0, 0), colors.HexColor('#1e3a8a')),
145
- ('TEXTCOLOR', (0, 0), (0, 0), colors.white),
146
- ('ALIGN', (0, 0), (0, 0), 'CENTER'),
147
- ('FONTNAME', (0, 0), (0, 0), 'Helvetica-Bold'),
148
- ('FONTSIZE', (0, 0), (0, 0), 11),
149
- ('TOPPADDING', (0, 0), (0, 0), 8),
150
- ('BOTTOMPADDING', (0, 0), (0, 0), 8),
151
- ]))
152
- story.append(header_frame)
153
- story.append(Spacer(1, 15))
154
-
155
- story.append(Paragraph("RAPPORT D'INDICE DE FORMALISATION", custom_title))
156
- story.append(Paragraph(f"Document établi le {datetime.now().strftime('%d %B %Y')}", custom_subtitle))
157
- story.append(Spacer(1, 20))
158
- story.append(Paragraph("✦ ✦ ✦", custom_subtitle))
159
- story.append(Spacer(1, 25))
160
-
161
- # Identification
162
- story.append(Paragraph("IDENTIFICATION DE L'ENTREPRISE", custom_section))
163
- story.append(Spacer(1, 8))
164
-
165
- info_data = [
166
- ["Nom de l'entreprise", entreprise_info.get('nom', 'Non renseigné')],
167
- ["Secteur d'activité", entreprise_info.get('secteur', 'Non renseigné')],
168
- ["Date de création", entreprise_info.get('date_creation', 'Non renseignée')],
169
- ["Effectif", str(entreprise_info.get('employes', 'Non renseigné')) + " employés"],
170
- ]
171
-
172
- info_table = Table(info_data, colWidths=[5.5*cm, 9.5*cm])
173
- info_table.setStyle(TableStyle([
174
- ('BACKGROUND', (0, 0), (0, -1), colors.HexColor('#f1f5f9')),
175
- ('FONTNAME', (0, 0), (0, -1), 'Helvetica-Bold'),
176
- ('FONTNAME', (1, 0), (1, -1), 'Helvetica'),
177
- ('FONTSIZE', (0, 0), (-1, -1), 10),
178
- ('TEXTCOLOR', (0, 0), (0, -1), colors.HexColor('#1e293b')),
179
- ('TEXTCOLOR', (1, 0), (1, -1), colors.HexColor('#475569')),
180
- ('ALIGN', (0, 0), (0, -1), 'LEFT'),
181
- ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
182
- ('TOPPADDING', (0, 0), (-1, -1), 8),
183
- ('BOTTOMPADDING', (0, 0), (-1, -1), 8),
184
- ('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor('#e2e8f0')),
185
- ]))
186
- story.append(info_table)
187
- story.append(Spacer(1, 25))
188
-
189
- # Score Global
190
- story.append(Paragraph("SCORE GLOBAL DE FORMALISATION", custom_section))
191
- story.append(Spacer(1, 15))
192
-
193
- score_frame = Table([[f"{score_global:.1f}%"]], colWidths=[16*cm])
194
- score_frame.setStyle(TableStyle([
195
- ('BACKGROUND', (0, 0), (0, 0), colors.HexColor(color + '15')),
196
- ('ALIGN', (0, 0), (0, 0), 'CENTER'),
197
- ('VALIGN', (0, 0), (0, 0), 'MIDDLE'),
198
- ('TOPPADDING', (0, 0), (0, 0), 25),
199
- ('BOTTOMPADDING', (0, 0), (0, 0), 15),
200
- ]))
201
- story.append(score_frame)
202
-
203
- gauge_width = 12 * cm
204
- fill_width = (score_global / 100) * gauge_width
205
- gauge_data = [["", ""]]
206
- gauge_table = Table(gauge_data, colWidths=[fill_width, gauge_width - fill_width])
207
- gauge_table.setStyle(TableStyle([
208
- ('BACKGROUND', (0, 0), (0, 0), reportlab_color),
209
- ('BACKGROUND', (1, 0), (1, 0), colors.HexColor('#e2e8f0')),
210
- ('TOPPADDING', (0, 0), (-1, -1), 0),
211
- ('BOTTOMPADDING', (0, 0), (-1, -1), 0),
212
- ]))
213
- story.append(gauge_table)
214
- story.append(Spacer(1, 10))
215
-
216
- story.append(Paragraph(f"NIVEAU {niveau} : {label}", custom_bold))
217
- story.append(Spacer(1, 20))
218
-
219
- story.append(PageBreak())
220
-
221
- # ==================== PAGE 2 ====================
222
- if os.path.exists(radar_path):
223
- story.append(Paragraph("ANALYSE VISUELLE", custom_section))
224
- story.append(Spacer(1, 8))
225
- img = Image(radar_path, width=10*cm, height=10*cm, hAlign='CENTER')
226
- story.append(img)
227
- story.append(Spacer(1, 10))
228
- story.append(Paragraph("Graphique radar des 6 dimensions de formalisation", custom_subtitle))
229
-
230
- story.append(PageBreak())
231
-
232
- # ==================== PAGE 3 ====================
233
- story.append(Paragraph("DÉTAIL PAR DIMENSION", custom_section))
234
- story.append(Spacer(1, 12))
235
-
236
- dims = [
237
- ('Admin.', 'Formalisation Administrative', '#3b82f6'),
238
- ('Compta.', 'Gestion Comptable', '#10b981'),
239
- ('Fiscal.', 'Conformité Fiscale', '#f59e0b'),
240
- ('Social.', 'Travail Décent', '#8b5cf6'),
241
- ('Finance.', 'Gestion Financière', '#ef4444'),
242
- ('Digital.', 'Capacités Numériques', '#06b6d4'),
243
- ]
244
-
245
- for key, name, c in dims:
246
- val = scores[key]
247
- story.append(Paragraph(f"{name}", custom_bold))
248
- data = [[f"{val:.0f}%", ""]]
249
- t = Table(data, colWidths=[val/100*10*cm, (100-val)/100*10*cm])
250
- t.setStyle(TableStyle([
251
- ('BACKGROUND', (0, 0), (0, 0), colors.HexColor(c)),
252
- ('BACKGROUND', (1, 0), (1, 0), colors.HexColor('#e2e8f0')),
253
- ('TEXTCOLOR', (0, 0), (0, 0), colors.white),
254
- ('FONTNAME', (0, 0), (0, 0), 'Helvetica-Bold'),
255
- ('FONTSIZE', (0, 0), (0, 0), 8),
256
- ('ALIGN', (0, 0), (0, 0), 'RIGHT'),
257
- ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
258
- ('TOPPADDING', (0, 0), (-1, -1), 4),
259
- ('BOTTOMPADDING', (0, 0), (-1, -1), 4),
260
- ('RIGHTPADDING', (0, 0), (0, 0), 5),
261
- ]))
262
- story.append(t)
263
- story.append(Spacer(1, 10))
264
-
265
- story.append(Spacer(1, 15))
266
-
267
- # Tableau récapitulatif
268
- story.append(Paragraph("RÉCAPITULATIF DES SCORES", custom_section))
269
- story.append(Spacer(1, 8))
270
-
271
- recap_data = [["Dimension", "Score", "Appréciation"]]
272
- for key, name, c in dims:
273
- val = scores[key]
274
- appreciation = "Excellent" if val >= 80 else "Bon" if val >= 60 else "À améliorer" if val >= 40 else "Critique"
275
- recap_data.append([name, f"{val:.0f}%", appreciation])
276
-
277
- recap_table = Table(recap_data, colWidths=[6.5*cm, 3*cm, 6.5*cm])
278
- recap_table.setStyle(TableStyle([
279
- ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#1e40af')),
280
- ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
281
- ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
282
- ('FONTSIZE', (0, 0), (-1, -1), 9),
283
- ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
284
- ('ALIGN', (1, 1), (1, -1), 'CENTER'),
285
- ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
286
- ('TOPPADDING', (0, 0), (-1, -1), 6),
287
- ('BOTTOMPADDING', (0, 0), (-1, -1), 6),
288
- ('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor('#e2e8f0')),
289
- ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, colors.HexColor('#f8fafc')]),
290
- ]))
291
- story.append(recap_table)
292
-
293
- # Section Justificatifs
294
- if uploaded_files_info:
295
- story.append(Spacer(1, 20))
296
- story.append(Paragraph("JUSTIFICATIFS FOURNIS", custom_section))
297
- story.append(Spacer(1, 8))
298
-
299
- current_dim = ""
300
- for file_info in uploaded_files_info:
301
- if file_info['dimension'] != current_dim:
302
- current_dim = file_info['dimension']
303
- story.append(Paragraph(f"<b>{current_dim}</b>", custom_bold))
304
- story.append(Paragraph(f" • {file_info['sub_item']} : {file_info['filename']}", custom_normal))
305
- story.append(Spacer(1, 2))
306
-
307
- story.append(PageBreak())
308
-
309
- # ==================== PAGE 4 ====================
310
- story.append(Paragraph("PROGRAMME D'ACCOMPAGNEMENT", custom_section))
311
- story.append(Spacer(1, 12))
312
-
313
- level_box = Table([[f"NIVEAU {niveau} : {label}"]], colWidths=[16*cm])
314
- level_box.setStyle(TableStyle([
315
- ('BACKGROUND', (0, 0), (0, 0), reportlab_color),
316
- ('TEXTCOLOR', (0, 0), (0, 0), colors.white),
317
- ('ALIGN', (0, 0), (0, 0), 'CENTER'),
318
- ('FONTNAME', (0, 0), (0, 0), 'Helvetica-Bold'),
319
- ('FONTSIZE', (0, 0), (0, 0), 12),
320
- ('TOPPADDING', (0, 0), (0, 0), 10),
321
- ('BOTTOMPADDING', (0, 0), (0, 0), 10),
322
- ]))
323
- story.append(level_box)
324
- story.append(Spacer(1, 15))
325
-
326
- for i, package in enumerate(packages[:5], 1):
327
- story.append(Paragraph(f"{i}. {package}", custom_normal))
328
- story.append(Spacer(1, 6))
329
-
330
- story.append(Spacer(1, 25))
331
-
332
- # Conclusion
333
- story.append(Paragraph("CONCLUSION", custom_section))
334
- story.append(Spacer(1, 10))
335
-
336
- if score_global < 40:
337
- conclusion = f"L'entreprise {entreprise_info.get('nom', '')} se trouve à un stade critique de formalisation. Une action immédiate est nécessaire."
338
- elif score_global < 60:
339
- conclusion = f"L'entreprise {entreprise_info.get('nom', '')} a entamé les premières démarches de formalisation mais reste fragile."
340
- elif score_global < 80:
341
- conclusion = f"L'entreprise {entreprise_info.get('nom', '')} a atteint un bon niveau de formalisation."
342
- else:
343
- conclusion = f"Félicitations à {entreprise_info.get('nom', '')} ! L'entreprise a atteint le niveau d'excellence."
344
-
345
- story.append(Paragraph(conclusion, custom_normal))
346
-
347
- # Pied de page
348
- story.append(Spacer(1, 40))
349
- story.append(Paragraph("─" * 50, custom_subtitle))
350
- story.append(Paragraph("Cabinet Conseil Cissé - Sénégal", custom_normal))
351
- story.append(Paragraph("3cabinetconseilcisse@gmail.com | +221 77 495 07 58", custom_normal))
352
-
353
- doc.build(story)
354
-
355
- if os.path.exists(radar_path):
356
- os.remove(radar_path)
357
-
358
- buffer.seek(0)
359
- return buffer
360
-
361
- # ═══════════════════════════════════════════════════════════════
362
- # FONCTION DE CRÉATION DU ZIP AVEC RAPPORT + JUSTIFICATIFS
363
- # ═══════════════════════════════════════════════════════════════
364
- def create_zip_with_attachments(entreprise_info, pdf_buffer, uploaded_files_dict):
365
- """Crée un fichier ZIP contenant le PDF et tous les justificatifs"""
366
- zip_buffer = io.BytesIO()
367
-
368
- with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
369
- # Ajouter le PDF
370
- pdf_filename = f"Rapport_Formalisation_{entreprise_info['nom'].replace(' ', '_')}.pdf"
371
- zip_file.writestr(pdf_filename, pdf_buffer.getvalue())
372
-
373
- # Ajouter les justificatifs
374
- for key, file_data in uploaded_files_dict.items():
375
- if file_data and file_data.get('file_data') is not None:
376
- # Nettoyer le nom du fichier
377
- safe_filename = file_data['filename'].replace(' ', '_')
378
- # Créer un dossier par dimension
379
- folder_name = file_data['dimension'].replace(' ', '_')
380
- zip_path = f"Justificatifs/{folder_name}/{safe_filename}"
381
- zip_file.writestr(zip_path, file_data['file_data'])
382
-
383
- zip_buffer.seek(0)
384
- return zip_buffer
385
-
386
- # ═══════════════════════════════════════════════════════════════
387
- # STYLE CSS (Interface Streamlit)
388
- # ═══════════════════════════════════════════════════════════════
389
- st.markdown("""
390
- <style>
391
- @import url('https://fonts.googleapis.com/css2?family=Syne:wght@400;500;600;700;800&family=DM+Sans:wght@300;400;500;600;700&display=swap');
392
-
393
- html, body, [class*="css"] {
394
- font-family: 'DM Sans', sans-serif;
395
- background: linear-gradient(135deg, #f5f7fa 0%, #eef2f7 100%);
396
- color: #1e293b;
397
- }
398
-
399
- .main { background: linear-gradient(135deg, #f5f7fa 0%, #eef2f7 100%); }
400
- .block-container { padding: 2rem 3rem; max-width: 1400px; }
401
-
402
- .hero-wrapper {
403
- text-align: center;
404
- padding: 40px 20px;
405
- border-radius: 24px;
406
- background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
407
- border: 1px solid rgba(0,0,0,0.08);
408
- box-shadow: 0 20px 40px -15px rgba(0,0,0,0.1);
409
- margin-bottom: 40px;
410
- }
411
- .hero-badge {
412
- display: inline-block;
413
- background: linear-gradient(135deg, #1e3a8a10 0%, #3b82f610 100%);
414
- border: 1px solid #3b82f630;
415
- color: #1e40af;
416
- font-size: 0.75rem;
417
- font-weight: 700;
418
- letter-spacing: 0.15em;
419
- text-transform: uppercase;
420
- padding: 6px 18px;
421
- border-radius: 50px;
422
- margin-bottom: 20px;
423
- }
424
- .hero-title {
425
- font-family: 'Syne', sans-serif;
426
- font-size: clamp(2rem, 5vw, 3.5rem);
427
- font-weight: 800;
428
- line-height: 1.1;
429
- background: linear-gradient(135deg, #1e3a8a 0%, #2563eb 50%, #3b82f6 100%);
430
- -webkit-background-clip: text;
431
- -webkit-text-fill-color: transparent;
432
- margin-bottom: 16px;
433
- }
434
- .hero-subtitle {
435
- color: #475569;
436
- font-size: 1.1rem;
437
- max-width: 600px;
438
- margin: 0 auto 30px;
439
- }
440
- .hero-stat {
441
- display: inline-block;
442
- background: #ffffff;
443
- border: 1px solid rgba(0,0,0,0.08);
444
- border-radius: 12px;
445
- padding: 14px 28px;
446
- margin: 6px;
447
- transition: all 0.3s ease;
448
- }
449
- .hero-stat:hover { transform: translateY(-5px); border-color: #3b82f6; }
450
- .hero-stat-num { font-family: 'Syne', sans-serif; font-size: 1.8rem; font-weight: 800; color: #2563eb; }
451
- .hero-stat-label { font-size: 0.75rem; color: #64748b; font-weight: 500; }
452
-
453
- .company-card {
454
- background: #ffffff;
455
- border: 1px solid #e2e8f0;
456
- border-radius: 20px;
457
- padding: 28px;
458
- margin-bottom: 30px;
459
- box-shadow: 0 4px 12px rgba(0,0,0,0.05);
460
- }
461
- .company-title {
462
- font-family: 'Syne', sans-serif;
463
- font-size: 1.1rem;
464
- font-weight: 700;
465
- color: #1e293b;
466
- margin-bottom: 20px;
467
- display: flex;
468
- align-items: center;
469
- gap: 10px;
470
- padding-bottom: 12px;
471
- border-bottom: 2px solid #e2e8f0;
472
- }
473
-
474
- .dim-card {
475
- background: #ffffff;
476
- border: 1px solid #e2e8f0;
477
- border-radius: 20px;
478
- padding: 28px;
479
- margin-bottom: 20px;
480
- transition: all 0.3s ease;
481
- }
482
- .dim-card:hover { border-color: #3b82f6; transform: translateY(-3px); box-shadow: 0 12px 24px -8px rgba(0,0,0,0.1); }
483
-
484
- .section-header {
485
- display: flex;
486
- align-items: center;
487
- gap: 12px;
488
- margin-bottom: 20px;
489
- padding-bottom: 12px;
490
- border-bottom: 2px solid #e2e8f0;
491
- }
492
- .section-icon {
493
- width: 44px; height: 44px;
494
- border-radius: 12px;
495
- display: flex; align-items: center; justify-content: center;
496
- font-size: 1.2rem;
497
- }
498
- .section-title {
499
- font-family: 'Syne', sans-serif;
500
- font-size: 1.1rem;
501
- font-weight: 700;
502
- color: #1e293b;
503
- }
504
- .section-weight {
505
- font-size: 0.8rem;
506
- color: #ffffff;
507
- background: #2563eb;
508
- padding: 4px 12px;
509
- border-radius: 20px;
510
- margin-left: auto;
511
- font-weight: 600;
512
- }
513
-
514
- .sub-item {
515
- display: flex;
516
- justify-content: space-between;
517
- align-items: center;
518
- padding: 12px 0;
519
- border-bottom: 1px solid #f1f5f9;
520
- font-size: 0.9rem;
521
- color: #475569;
522
- font-weight: 500;
523
- }
524
- .sub-item-weight {
525
- background: #dbeafe;
526
- color: #1e40af;
527
- padding: 4px 12px;
528
- border-radius: 20px;
529
- font-size: 0.78rem;
530
- font-weight: 700;
531
- }
532
-
533
- .stFileUploader {
534
- background: #f8fafc !important;
535
- border: 1px dashed #3b82f6 !important;
536
- border-radius: 12px !important;
537
- margin-top: 8px;
538
- padding: 5px;
539
- }
540
-
541
- div.stButton > button {
542
- background: linear-gradient(135deg, #1d4ed8 0%, #3b82f6 100%);
543
- color: white;
544
- border: none;
545
- padding: 18px 40px;
546
- border-radius: 14px;
547
- font-family: 'Syne', sans-serif;
548
- font-weight: 700;
549
- font-size: 1.05rem;
550
- width: 100%;
551
- box-shadow: 0 8px 20px rgba(37,99,235,0.2);
552
- transition: all 0.2s ease;
553
- }
554
- div.stButton > button:hover { transform: translateY(-2px); box-shadow: 0 12px 28px rgba(37,99,235,0.3); }
555
-
556
- .score-ring-container {
557
- text-align: center;
558
- padding: 40px 30px;
559
- background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
560
- border: 1px solid #e2e8f0;
561
- border-radius: 24px;
562
- }
563
- .score-value {
564
- font-family: 'Syne', sans-serif;
565
- font-size: 5rem;
566
- font-weight: 800;
567
- line-height: 1;
568
- margin: 20px 0 10px;
569
- }
570
- .score-level-badge {
571
- display: inline-block;
572
- padding: 10px 28px;
573
- border-radius: 50px;
574
- font-size: 0.9rem;
575
- font-weight: 700;
576
- margin-bottom: 20px;
577
- }
578
-
579
- .footer {
580
- text-align: center;
581
- padding: 40px 20px 20px;
582
- color: #64748b;
583
- font-size: 0.85rem;
584
- }
585
- .footer strong { color: #2563eb; font-weight: 700; }
586
- </style>
587
- """, unsafe_allow_html=True)
588
-
589
- # ═══════════════════════════════════════════════════════════════
590
- # HERO SECTION
591
- # ═══════════════════════════════════════════════════════════════
592
- col_left, col_center, col_right = st.columns([1, 2, 1])
593
- with col_center:
594
- if lottie_hero:
595
- st_lottie(lottie_hero, height=150, key="hero_animation")
596
-
597
- st.markdown("""
598
- <div class="hero-wrapper">
599
- <div class="hero-badge">Cabinet Conseil Cissé &nbsp;•&nbsp; Sénégal</div>
600
- <div class="hero-title">Indice de Formalisation<br>des Entreprises</div>
601
- <div class="hero-subtitle">
602
- Évaluez votre maturité administrative et accédez à des programmes<br>d'accompagnement sur mesure.
603
- </div>
604
- <div>
605
- <div class="hero-stat"><div class="hero-stat-num">6</div><div class="hero-stat-label">Dimensions évaluées</div></div>
606
- <div class="hero-stat"><div class="hero-stat-num">5</div><div class="hero-stat-label">Niveaux de maturité</div></div>
607
- <div class="hero-stat"><div class="hero-stat-num">100%</div><div class="hero-stat-label">Score pondéré</div></div>
608
- </div>
609
- </div>
610
- """, unsafe_allow_html=True)
611
-
612
- # ═══════════════════════════════════════════════════════════════
613
- # FORMULAIRE ENTREPRISE
614
- # ═══════════════════════════════════════════════════════════════
615
- st.markdown('<div class="company-card"><div class="company-title"><span>🏢</span> Informations de l\'entreprise</div></div>', unsafe_allow_html=True)
616
-
617
- col_info1, col_info2 = st.columns(2)
618
- with col_info1:
619
- nom_entreprise = st.text_input("🏢 Nom de l'entreprise *", placeholder="Ex: SARL CISSÉ & Frères", key="nom_entreprise")
620
- secteur = st.selectbox("📊 Secteur d'activité", ["Sélectionnez", "Commerce", "Services", "Industrie", "Agriculture", "BTP", "Technologie", "Autre"], key="secteur")
621
- with col_info2:
622
- date_creation = st.date_input("📅 Date de création", value=None, key="date_creation")
623
- nb_employes = st.number_input("👥 Nombre d'employés", min_value=0, step=1, key="nb_employes")
624
-
625
- # ═══════════════════════════════════════════════════════════════
626
- # DIMENSIONS
627
- # ═══════════════════════════════════════════════════════════════
628
-
629
- # DIMENSION 1
630
- st.markdown("""
631
- <div class="dim-card">
632
- <div class="section-header">
633
- <div class="section-icon" style="background:#dbeafe; color:#1e40af;">📁</div>
634
- <div class="section-title">Formalisation Administrative</div>
635
- <div class="section-weight">Poids : 30%</div>
636
- </div>
637
- <div class="sub-item"><span>RCCM / Actes administratifs</span><span class="sub-item-weight">40%</span></div>
638
- <div class="sub-item"><span>NINEA</span><span class="sub-item-weight">40%</span></div>
639
- <div class="sub-item" style="border:none"><span>Autorisations spécifiques / RSE</span><span class="sub-item-weight">20%</span></div>
640
- </div>
641
- """, unsafe_allow_html=True)
642
-
643
- col1, col2, col3 = st.columns(3)
644
- with col1:
645
- rccm = st.checkbox("✅ RCCM / Actes administratifs", key="rccm")
646
- if rccm:
647
- uploaded = st.file_uploader("Justificatif RCCM", type=['pdf', 'jpg', 'png'], key="f1", label_visibility="collapsed")
648
- if uploaded:
649
- st.session_state.uploaded_files['f1'] = {
650
- 'dimension': 'Formalisation Administrative',
651
- 'sub_item': 'RCCM / Actes administratifs',
652
- 'filename': uploaded.name,
653
- 'file_data': uploaded.getvalue(),
654
- 'date': datetime.now().strftime('%d/%m/%Y %H:%M')
655
- }
656
- with col2:
657
- ninea = st.checkbox("✅ NINEA", key="ninea")
658
- if ninea:
659
- uploaded = st.file_uploader("Justificatif NINEA", type=['pdf', 'jpg', 'png'], key="f2", label_visibility="collapsed")
660
- if uploaded:
661
- st.session_state.uploaded_files['f2'] = {
662
- 'dimension': 'Formalisation Administrative',
663
- 'sub_item': 'NINEA',
664
- 'filename': uploaded.name,
665
- 'file_data': uploaded.getvalue(),
666
- 'date': datetime.now().strftime('%d/%m/%Y %H:%M')
667
- }
668
- with col3:
669
- aut = st.checkbox("✅ Autorisations spécifiques / RSE", key="aut")
670
- if aut:
671
- uploaded = st.file_uploader("Justificatif Autorisations", type=['pdf', 'jpg'], key="f3", label_visibility="collapsed")
672
- if uploaded:
673
- st.session_state.uploaded_files['f3'] = {
674
- 'dimension': 'Formalisation Administrative',
675
- 'sub_item': 'Autorisations spécifiques / RSE',
676
- 'filename': uploaded.name,
677
- 'file_data': uploaded.getvalue(),
678
- 'date': datetime.now().strftime('%d/%m/%Y %H:%M')
679
- }
680
-
681
- s1 = (rccm * 0.4 + ninea * 0.4 + aut * 0.2) * 100
682
- scores = {'Admin.': s1}
683
-
684
- # DIMENSION 2
685
- st.markdown("""
686
- <div class="dim-card">
687
- <div class="section-header">
688
- <div class="section-icon" style="background:#dcfce7; color:#166534;">📈</div>
689
- <div class="section-title">Gestion Comptable</div>
690
- <div class="section-weight">Poids : 20%</div>
691
- </div>
692
- </div>
693
- """, unsafe_allow_html=True)
694
-
695
- systeme = st.selectbox(
696
- "Système comptable utilisé",
697
- ["Aucun (0%)", "Cahier recettes-dépenses (20%)", "SMT sans états financiers (30%)",
698
- "SMT avec états financiers (50%)", "Système Normal sans états visés (75%)",
699
- "Système Normal avec états financiers visés (100%)"],
700
- key="systeme"
701
- )
702
-
703
- uploaded = st.file_uploader("Justificatif états financiers / Brouillard", type=['pdf', 'xlsx'], key="f4")
704
- if uploaded:
705
- st.session_state.uploaded_files['f4'] = {
706
- 'dimension': 'Gestion Comptable',
707
- 'sub_item': 'États financiers / Brouillard',
708
- 'filename': uploaded.name,
709
- 'file_data': uploaded.getvalue(),
710
- 'date': datetime.now().strftime('%d/%m/%Y %H:%M')
711
- }
712
-
713
- score_compta_map = {
714
- "Aucun (0%)": 0, "Cahier recettes-dépenses (20%)": 20, "SMT sans états financiers (30%)": 30,
715
- "SMT avec états financiers (50%)": 50, "Système Normal sans états visés (75%)": 75,
716
- "Système Normal avec états financiers visés (100%)": 100
717
- }
718
- s2 = score_compta_map[systeme]
719
- scores['Compta.'] = s2
720
-
721
- # DIMENSION 3
722
- st.markdown("""
723
- <div class="dim-card">
724
- <div class="section-header">
725
- <div class="section-icon" style="background:#fed7aa; color:#9a3412;">🏛️</div>
726
- <div class="section-title">Conformité Fiscale</div>
727
- <div class="section-weight">Poids : 15%</div>
728
- </div>
729
- </div>
730
- """, unsafe_allow_html=True)
731
-
732
- col_f1, col_f2, col_f3 = st.columns(3)
733
- with col_f1:
734
- cofi = st.checkbox("✅ Enregistrement fiscal / COFI (20%)", key="cofi")
735
- if cofi:
736
- uploaded = st.file_uploader("Justificatif enregistrement fiscal", type=['pdf', 'jpg'], key="f5", label_visibility="collapsed")
737
- if uploaded:
738
- st.session_state.uploaded_files['f5'] = {
739
- 'dimension': 'Conformité Fiscale',
740
- 'sub_item': 'Enregistrement fiscal / COFI',
741
- 'filename': uploaded.name,
742
- 'file_data': uploaded.getvalue(),
743
- 'date': datetime.now().strftime('%d/%m/%Y %H:%M')
744
- }
745
- with col_f2:
746
- declarations = st.checkbox("✅ Déclarations fiscales régulières (40%)", key="decl")
747
- if declarations:
748
- uploaded = st.file_uploader("Justificatif déclarations fiscales", type=['pdf'], key="f6", label_visibility="collapsed")
749
- if uploaded:
750
- st.session_state.uploaded_files['f6'] = {
751
- 'dimension': 'Conformité Fiscale',
752
- 'sub_item': 'Déclarations fiscales régulières',
753
- 'filename': uploaded.name,
754
- 'file_data': uploaded.getvalue(),
755
- 'date': datetime.now().strftime('%d/%m/%Y %H:%M')
756
- }
757
- with col_f3:
758
- quitus = st.checkbox("✅ Quitus fiscal (40%)", key="quitus")
759
- if quitus:
760
- uploaded = st.file_uploader("Justificatif quitus fiscal", type=['pdf', 'jpg'], key="f7", label_visibility="collapsed")
761
- if uploaded:
762
- st.session_state.uploaded_files['f7'] = {
763
- 'dimension': 'Conformité Fiscale',
764
- 'sub_item': 'Quitus fiscal',
765
- 'filename': uploaded.name,
766
- 'file_data': uploaded.getvalue(),
767
- 'date': datetime.now().strftime('%d/%m/%Y %H:%M')
768
- }
769
-
770
- s3 = (cofi * 0.20 + declarations * 0.40 + quitus * 0.40) * 100
771
- scores['Fiscal.'] = s3
772
-
773
- # DIMENSION 4
774
- st.markdown("""
775
- <div class="dim-card">
776
- <div class="section-header">
777
- <div class="section-icon" style="background:#e9d5ff; color:#4c1d95;">👥</div>
778
- <div class="section-title">Travail Décent</div>
779
- <div class="section-weight">Poids : 15%</div>
780
- </div>
781
- </div>
782
- """, unsafe_allow_html=True)
783
-
784
- col_t1, col_t2 = st.columns(2)
785
- with col_t1:
786
- contrats = st.checkbox("✅ Contrats de travail enregistrés (35%)", key="contrats")
787
- if contrats:
788
- uploaded = st.file_uploader("Justificatif contrats de travail", type=['pdf'], key="f8", label_visibility="collapsed")
789
- if uploaded:
790
- st.session_state.uploaded_files['f8'] = {
791
- 'dimension': 'Travail Décent',
792
- 'sub_item': 'Contrats de travail enregistrés',
793
- 'filename': uploaded.name,
794
- 'file_data': uploaded.getvalue(),
795
- 'date': datetime.now().strftime('%d/%m/%Y %H:%M')
796
- }
797
- affiliations = st.checkbox("✅ Affiliations CSS / IPRES / IPM (40%)", key="affil")
798
- if affiliations:
799
- uploaded = st.file_uploader("Justificatif affiliations sociales", type=['pdf', 'jpg'], key="f9", label_visibility="collapsed")
800
- if uploaded:
801
- st.session_state.uploaded_files['f9'] = {
802
- 'dimension': 'Travail Décent',
803
- 'sub_item': 'Affiliations CSS / IPRES / IPM',
804
- 'filename': uploaded.name,
805
- 'file_data': uploaded.getvalue(),
806
- 'date': datetime.now().strftime('%d/%m/%Y %H:%M')
807
- }
808
- with col_t2:
809
- sst = st.checkbox("✅ Conditions SST / conformité (15%)", key="sst")
810
- droits = st.checkbox("✅ Respect droits collectifs (10%)", key="droits")
811
-
812
- s4 = (contrats * 0.35 + affiliations * 0.40 + sst * 0.15 + droits * 0.10) * 100
813
- scores['Social.'] = s4
814
-
815
- # DIMENSION 5
816
- st.markdown("""
817
- <div class="dim-card">
818
- <div class="section-header">
819
- <div class="section-icon" style="background:#fecaca; color:#991b1b;">💳</div>
820
- <div class="section-title">Gestion Financière</div>
821
- <div class="section-weight">Poids : 10%</div>
822
- </div>
823
- </div>
824
- """, unsafe_allow_html=True)
825
-
826
- col_g1, col_g2 = st.columns(2)
827
- with col_g1:
828
- compte = st.checkbox("✅ Compte bancaire / IMF / wallet (50%)", key="compte")
829
- if compte:
830
- uploaded = st.file_uploader("Justificatif RIB ou extrait de compte", type=['pdf', 'jpg'], key="f10", label_visibility="collapsed")
831
- if uploaded:
832
- st.session_state.uploaded_files['f10'] = {
833
- 'dimension': 'Gestion Financière',
834
- 'sub_item': 'Compte bancaire / IMF / wallet',
835
- 'filename': uploaded.name,
836
- 'file_data': uploaded.getvalue(),
837
- 'date': datetime.now().strftime('%d/%m/%Y %H:%M')
838
- }
839
- with col_g2:
840
- tracabilite = st.checkbox("✅ Traçabilité des opérations (50%)", key="tracab")
841
- if tracabilite:
842
- uploaded = st.file_uploader("Justificatif registre / tableau de trésorerie", type=['pdf', 'xlsx'], key="f11", label_visibility="collapsed")
843
- if uploaded:
844
- st.session_state.uploaded_files['f11'] = {
845
- 'dimension': 'Gestion Financière',
846
- 'sub_item': 'Traçabilité des opérations',
847
- 'filename': uploaded.name,
848
- 'file_data': uploaded.getvalue(),
849
- 'date': datetime.now().strftime('%d/%m/%Y %H:%M')
850
- }
851
-
852
- s5 = (compte * 0.50 + tracabilite * 0.50) * 100
853
- scores['Finance.'] = s5
854
-
855
- # DIMENSION 6
856
- st.markdown("""
857
- <div class="dim-card">
858
- <div class="section-header">
859
- <div class="section-icon" style="background:#cffafe; color:#164e63;">💻</div>
860
- <div class="section-title">Capacités Organisationnelles & Numériques</div>
861
- <div class="section-weight">Poids : 10%</div>
862
- </div>
863
- </div>
864
- """, unsafe_allow_html=True)
865
-
866
- col_n1, col_n2, col_n3 = st.columns(3)
867
- with col_n1:
868
- manuel = st.checkbox("✅ Manuel de procédures / organigramme (40%)", key="manuel")
869
- if manuel:
870
- uploaded = st.file_uploader("Justificatif manuel / organigramme", type=['pdf', 'docx'], key="f12", label_visibility="collapsed")
871
- if uploaded:
872
- st.session_state.uploaded_files['f12'] = {
873
- 'dimension': 'Capacités Organisationnelles & Numériques',
874
- 'sub_item': 'Manuel de procédures / organigramme',
875
- 'filename': uploaded.name,
876
- 'file_data': uploaded.getvalue(),
877
- 'date': datetime.now().strftime('%d/%m/%Y %H:%M')
878
- }
879
- with col_n2:
880
- bizplan = st.checkbox("✅ Plan stratégique / business plan (20%)", key="bizplan")
881
- if bizplan:
882
- uploaded = st.file_uploader("Justificatif business plan", type=['pdf', 'docx'], key="f13", label_visibility="collapsed")
883
- if uploaded:
884
- st.session_state.uploaded_files['f13'] = {
885
- 'dimension': 'Capacités Organisationnelles & Numériques',
886
- 'sub_item': 'Plan stratégique / business plan',
887
- 'filename': uploaded.name,
888
- 'file_data': uploaded.getvalue(),
889
- 'date': datetime.now().strftime('%d/%m/%Y %H:%M')
890
- }
891
- with col_n3:
892
- digital = st.checkbox("✅ Maturité digitale avérée (40%)", key="digital")
893
- if digital:
894
- uploaded = st.file_uploader("Justificatif outils numériques", type=['pdf', 'jpg', 'png'], key="f14", label_visibility="collapsed")
895
- if uploaded:
896
- st.session_state.uploaded_files['f14'] = {
897
- 'dimension': 'Capacités Organisationnelles & Numériques',
898
- 'sub_item': 'Maturité digitale avérée',
899
- 'filename': uploaded.name,
900
- 'file_data': uploaded.getvalue(),
901
- 'date': datetime.now().strftime('%d/%m/%Y %H:%M')
902
- }
903
-
904
- s6 = (manuel * 0.40 + bizplan * 0.20 + digital * 0.40) * 100
905
- scores['Digital.'] = s6
906
-
907
- # ═══════════════════════════════════════════════════════════════
908
- # CALCUL DU SCORE GLOBAL
909
- # ═══════════════════════════════════════════════════════════════
910
- score_global = (s1 * 0.30 + s2 * 0.20 + s3 * 0.15 + s4 * 0.15 + s5 * 0.10 + s6 * 0.10)
911
-
912
- # ═══════════════════════════════════════════════════════════════
913
- # BOUTON GÉNÉRATION
914
- # ═══════════════════════════════════════════════════════════════
915
- st.markdown("<br>", unsafe_allow_html=True)
916
- col_btn1, col_btn2, col_btn3 = st.columns([1, 2, 1])
917
- with col_btn2:
918
- generate = st.button("🚀 GÉNÉRER L'INDICE DE FORMALISATION")
919
-
920
- # ═══════════════════════════════════════════════════════════════
921
- # RÉSULTATS
922
- # ═══════════════════════════════════════════════════════════════
923
- if generate:
924
- if not nom_entreprise:
925
- st.error("⚠️ Veuillez renseigner le nom de l'entreprise avant de générer l'indice.")
926
- st.stop()
927
-
928
- st.markdown("<hr>", unsafe_allow_html=True)
929
-
930
- # Préparer la liste des justificatifs pour le PDF
931
- uploaded_files_info = []
932
- for key, file_info in st.session_state.uploaded_files.items():
933
- uploaded_files_info.append({
934
- 'dimension': file_info['dimension'],
935
- 'sub_item': file_info['sub_item'],
936
- 'filename': file_info['filename'],
937
- 'date': file_info['date']
938
- })
939
-
940
- entreprise_info = {
941
- 'nom': nom_entreprise,
942
- 'secteur': secteur if secteur != "Sélectionnez" else "Non précisé",
943
- 'date_creation': date_creation.strftime('%d/%m/%Y') if date_creation else "Non renseignée",
944
- 'employes': nb_employes if nb_employes > 0 else "Non renseigné"
945
- }
946
-
947
- # Détermination du niveau
948
- if score_global < 20:
949
- niveau, label, color = 0, "Informel pur — Zone Grise", "#ef4444"
950
- packages = [
951
- "📋 Campagnes de sensibilisation sur la formalisation",
952
- "📝 Appui à l'immatriculation (NINEA, RCCM)",
953
- "🏢 Accompagnement à la domiciliation",
954
- "💳 Ouverture d'un compte bancaire",
955
- "📜 Assistance sur les autorisations"
956
- ]
957
- elif score_global < 40:
958
- niveau, label, color = 1, "Pré-formalisation — Premiers pas", "#f59e0b"
959
- packages = [
960
- "📚 Formation en comptabilité simplifiée",
961
- "📊 Assistance pour déclarations fiscales",
962
- "🏦 Formation à l'utilisation d'un compte pro",
963
- "⚖️ Information sur le droit du travail",
964
- "💰 Accès à microfinancements adaptés"
965
- ]
966
- elif score_global < 60:
967
- niveau, label, color = 2, "Formalisation de base — Fondations", "#3b82f6"
968
- packages = [
969
- "🎓 Formation avancée en gestion",
970
- "📈 Coaching fiscal",
971
- "📝 Rédaction des contrats de travail",
972
- "🤝 Sensibilisation au travail décent",
973
- "💼 Préparation au crédit bancaire"
974
- ]
975
- elif score_global < 80:
976
- niveau, label, color = 3, "Formalisation intermédiaire — Consolidation", "#8b5cf6"
977
- packages = [
978
- "📊 Passage au Système Normal",
979
- "🏛️ Formation en gouvernance",
980
- "🛡️ Généralisation couverture sociale",
981
- "🏗️ Mise en conformité lieu de travail",
982
- "💎 Accès crédits bancaires importants"
983
- ]
984
- else:
985
- niveau, label, color = 4, "Formalisation avancée — Excellence", "#10b981"
986
- packages = [
987
- "🏅 Appui certification qualité",
988
- "💻 Digitalisation des processus",
989
- "🌍 Accès aux marchés publics",
990
- "🤝 Participation foires internationales",
991
- "💰 Accès au capital-risque",
992
- "🎯 Coaching internationalisation"
993
- ]
994
-
995
- # Affichage des résultats
996
- st.markdown(f"""
997
- <div style="background: linear-gradient(135deg, #f8fafc 0%, #ffffff 100%); border-radius: 16px; padding: 20px; margin-bottom: 20px;">
998
- <div style="display: flex; justify-content: space-between; flex-wrap: wrap;">
999
- <div><span style="font-size: 0.8rem; color: #64748b;">ENTREPRISE</span>
1000
- <div style="font-family: 'Syne', sans-serif; font-size: 1.4rem; font-weight: 800;">{nom_entreprise}</div></div>
1001
- <div><span style="font-size: 0.8rem; color: #64748b;">SECTEUR</span>
1002
- <div style="font-weight: 600; color: #2563eb;">{secteur if secteur != 'Sélectionnez' else 'Non précisé'}</div></div>
1003
- <div><span style="font-size: 0.8rem; color: #64748b;">DATE</span>
1004
- <div style="font-weight: 600;">{datetime.now().strftime('%d/%m/%Y')}</div></div>
1005
- </div>
1006
- </div>
1007
- """, unsafe_allow_html=True)
1008
-
1009
- # Score
1010
- col_res1, col_res2 = st.columns([1, 1])
1011
-
1012
- with col_res1:
1013
- st.markdown(f"""
1014
- <div class="score-ring-container">
1015
- <div style="font-size:0.8rem; color:#64748b; font-weight:600;">SCORE GLOBAL</div>
1016
- <div class="score-value" style="color:{color};">{score_global:.1f}%</div>
1017
- <div class="score-level-badge" style="background:{color}10; color:{color};">NIVEAU {niveau} | {label}</div>
1018
- </div>
1019
- """, unsafe_allow_html=True)
1020
-
1021
- with col_res2:
1022
- dim_details = [
1023
- ('Admin.', 'Formalisation Administrative', '#3b82f6'),
1024
- ('Compta.', 'Gestion Comptable', '#10b981'),
1025
- ('Fiscal.', 'Conformité Fiscale', '#f59e0b'),
1026
- ('Social.', 'Travail Décent', '#8b5cf6'),
1027
- ('Finance.', 'Gestion Financière', '#ef4444'),
1028
- ('Digital.', 'Capacités Numériques', '#06b6d4'),
1029
- ]
1030
- categories = [d[1] for d in dim_details]
1031
- values = [scores[d[0]] for d in dim_details]
1032
-
1033
- fig = go.Figure()
1034
- fig.add_trace(go.Scatterpolar(r=values + [values[0]], theta=categories + [categories[0]], fill='toself',
1035
- fillcolor=f'rgba({int(color[1:3],16)},{int(color[3:5],16)},{int(color[5:7],16)},0.2)',
1036
- line=dict(color=color, width=3), name='Score'))
1037
- fig.update_layout(polar=dict(bgcolor='rgba(0,0,0,0)', radialaxis=dict(range=[0,100])),
1038
- paper_bgcolor='rgba(0,0,0,0)', plot_bgcolor='rgba(0,0,0,0)', height=400)
1039
- st.plotly_chart(fig, use_container_width=True)
1040
-
1041
- # Détail
1042
- st.markdown("### 📊 Détail par dimension")
1043
- for key, name, c in dim_details:
1044
- val = scores[key]
1045
- st.markdown(f"""
1046
- <div style="margin:10px 0;">
1047
- <div style="display:flex; justify-content:space-between;"><span>{name}</span><span style="color:{c}; font-weight:700;">{val:.0f}%</span></div>
1048
- <div style="background:#f1f5f9; border-radius:50px; height:8px;"><div style="width:{val}%; height:100%; background:{c}; border-radius:50px;"></div></div>
1049
- </div>
1050
- """, unsafe_allow_html=True)
1051
-
1052
- # Recommandations
1053
- st.markdown(f"### 🎯 Programme d'accompagnement - Niveau {niveau}")
1054
- cols = st.columns(2)
1055
- for i, p in enumerate(packages):
1056
- with cols[i % 2]:
1057
- st.markdown(f"- {p}")
1058
-
1059
- # Génération du PDF
1060
- with st.spinner("Génération du rapport PDF..."):
1061
- pdf_buffer = generate_professional_pdf(entreprise_info, score_global, niveau, label, color, scores, packages, uploaded_files_info)
1062
-
1063
- # Création du ZIP avec les justificatifs
1064
- with st.spinner("Préparation des justificatifs..."):
1065
- zip_buffer = create_zip_with_attachments(entreprise_info, pdf_buffer, st.session_state.uploaded_files)
1066
-
1067
- # Bouton unique de téléchargement
1068
- col_zip1, col_zip2, col_zip3 = st.columns([1, 2, 1])
1069
- with col_zip2:
1070
- st.download_button(
1071
- label="📦 TÉLÉCHARGER LE RAPPORT COMPLET (PDF + JUSTIFICATIFS)",
1072
- data=zip_buffer,
1073
- file_name=f"Rapport_Complet_{nom_entreprise.replace(' ', '_')}_{datetime.now().strftime('%Y%m%d_%H%M')}.zip",
1074
- mime="application/zip",
1075
- use_container_width=True
1076
- )
1077
-
1078
- # Affichage du nombre de justificatifs
1079
- nb_justificatifs = len(st.session_state.uploaded_files)
1080
- if nb_justificatifs > 0:
1081
- st.success(f"📎 {nb_justificatifs} justificatif(s) inclus dans le téléchargement")
1082
-
1083
- # Afficher la liste des justificatifs dans un expander
1084
- with st.expander("📋 Voir la liste des justificatifs inclus"):
1085
- for key, file_info in st.session_state.uploaded_files.items():
1086
- st.markdown(f"- **{file_info['dimension']}** : {file_info['sub_item']} → `{file_info['filename']}`")
1087
- else:
1088
- st.info("📎 Aucun justificatif téléchargé - Le rapport PDF sera téléchargé seul")
1089
-
1090
- if score_global >= 80:
1091
- st.balloons()
1092
- st.success("🎉 FÉLICITATIONS ! Niveau d'excellence atteint !")
1093
-
1094
- # Footer
1095
- st.markdown("""
1096
- <div class="footer">
1097
- <hr>
1098
- Propulsé par <strong>Cabinet Conseil Cissé</strong> | 2026 - Sénégal
1099
- </div>
1100
- """, unsafe_allow_html=True)