simikkk commited on
Commit
e171ca9
·
verified ·
1 Parent(s): 7c45434

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +186 -193
app.py CHANGED
@@ -20,7 +20,7 @@ TRANSLATIONS = {
20
  },
21
  "SK": {
22
  "title": "🏆 TradeFlow AI", "caption": "Chytry operačný systém pre novú generáciu remeselníkov.",
23
- "tab_dash": "📈 Prehľad & Metriky", "tab_gen": "📝 AI Generátor Ponúk", "tab_team": "👥 Správa Tímu", "tab_crm": "👥 Adresár (CRM)", "tab_inv": "🧾 Fakturácia jedným klikom", "tab_pro": "🚀 PRO Exkluzívne Funkcie",
24
  "profit": "Skutočný zisk", "pending": "Čakajúce peniaze", "potential": "Otvorené AI Ponuky", "limit": "Využitie Free limitu",
25
  "cust_name": "Meno a priezvisko", "company": "Firma (Voliteľné)", "phone": "Telefónne číslo", "save_cust": "Uložiť klienta",
26
  "generate_btn": "⚡ Vygenerovať/Uložiť rozpočet", "invoice_btn": "🧾 VYSTAVIŤ FAKTÚRU JEDNÝM KLIKNUTÍM",
@@ -35,41 +35,6 @@ TRANSLATIONS = {
35
  "generate_btn": "⚡ Generate/Save Quote", "invoice_btn": "🧾 ISSUE 1-CLICK INVOICE",
36
  "paywall_msg": "🔒 You have reached the 3 free quotes limit. Unlock PRO tier for unlimited access.",
37
  "pro_router": "📍 AI Route & Logistics Planner", "pro_sms": "📲 Intelligent SMS Reminder Center"
38
- },
39
- "DE": {
40
- "title": "🏆 TradeFlow AI", "caption": "Intelligentes Betriebssystem für Handwerker.",
41
- "tab_dash": "📈 Dashboard & Metriken", "tab_gen": "📝 AI-Angebotsgenerator", "tab_team": "👥 Team-Management", "tab_crm": "👥 Kontakte (CRM)", "tab_inv": "🧾 1-Klick-Rechnungsstellung", "tab_pro": "🚀 PRO-Funktionen",
42
- "profit": "Tatsächlicher Gewinn", "pending": "Ausstehende Einnahmen", "potential": "Offene KI-Angebote", "limit": "Free-Tier-Nutzung",
43
- "cust_name": "Vollständiger Name", "company": "Firma (Optional)", "phone": "Telefonnummer", "save_cust": "Kunde保存",
44
- "generate_btn": "⚡ Angebot generieren/speichern", "invoice_btn": "🧾 1-KLICK-RECHNUNG ERSTELLEN",
45
- "paywall_msg": "🔒 Sie haben das Limit von 3 kostenlosen Angeboten erreicht. Aktivieren Sie PRO.",
46
- "pro_router": "📍 KI-Routen- & Logistikplaner", "pro_sms": "📲 Intelligentes SMS-Mahnzentrum"
47
- },
48
- "SPA": {
49
- "title": "🏆 TradeFlow AI", "caption": "Sistema operativo inteligente para profesionales.",
50
- "tab_dash": "📈 Panel y Métricas", "tab_gen": "📝 Generador AI", "tab_team": "👥 Gestión de Equipo", "tab_crm": "👥 Contactos (CRM)", "tab_inv": "🧾 Facturación 1 Clic", "tab_pro": "🚀 Funciones PRO",
51
- "profit": "Ganancia Real", "pending": "Ingresos Pendientes", "potential": "Presupuestos AI", "limit": "Uso Free",
52
- "cust_name": "Nombre Completo", "company": "Empresa", "phone": "Teléfono", "save_cust": "Guardar",
53
- "generate_btn": "⚡ Guardar Presupuesto", "invoice_btn": "🧾 FACTURA EN 1 CLIC",
54
- "paywall_msg": "🔒 Límite gratis alcanzado. Active PRO.",
55
- "pro_router": "📍 Planificador IA", "pro_sms": "📲 Centro SMS Inteligente"
56
- },
57
- "ITA": {
58
- "title": "🏆 TradeFlow AI", "caption": "Sistema operativo intelligente per artigiani.",
59
- "tab_dash": "📈 Dashboard", "tab_gen": "📝 Generatore AI", "tab_team": "👥 Gestione Team", "tab_crm": "👥 Contatti (CRM)", "tab_inv": "🧾 Fatturazione 1 Clic", "tab_pro": "🚀 Info PRO",
60
- "profit": "Profitto Reale", "pending": "In Sospeso", "potential": "Preventivi AI", "limit": "Free Tier",
61
- "cust_name": "Nome", "company": "Azienda", "phone": "Telefono", "save_cust": "Salva",
62
- "generate_btn": "⚡ Salva Preventivo", "invoice_btn": "🧾 GENERA FATTURA",
63
- "paywall_msg": "🔒 Limite raggiunto. Attiva PRO.",
64
- "pro_router": "📍 Logistica IA", "pro_sms": "📲 Centro SMS"
65
- },
66
- "FRA": {
67
- "title": "🏆 TradeFlow AI", "caption": "Système d'exploitation intelligent pour les artisans.",
68
- "tab_dash": "📈 Tableau de bord", "tab_gen": "📝 Générateur IA", "tab_team": "👥 Gestion de l'équipe", "tab_crm": "👥 Contacts", "tab_inv": "🧾 Facturation 1 Clic", "tab_pro": "🚀 PRO",
69
- "profit": "Bénéfice Réel", "pending": "En Attente", "potential": "Devis IA", "limit": "Usage Free",
70
- "cust_name": "Nom", "company": "Entreprise", "phone": "Numéro", "save_cust": "Enregistrer",
71
- "generate_btn": "⚡ Enregistrer le Devis", "invoice_btn": "🧾 CRÉER LA FACTURE",
72
- "paywall_msg": "🔒 Limite atteinte. Activez PRO."
73
  }
74
  }
75
 
@@ -84,7 +49,7 @@ CURRENCIES = {
84
  # -----------------------------------------------------------------------------
85
  # INICIALIZACE STAVU
86
  # -----------------------------------------------------------------------------
87
- VERSION = "1.8.0-ENTERPRISE"
88
 
89
  st.set_page_config(page_title="TradeFlow AI", page_icon="🏆", layout="wide", initial_sidebar_state="expanded")
90
 
@@ -97,7 +62,13 @@ if "quotes_count" not in st.session_state: st.session_state.quotes_count = 1
97
  if "stripe_mode" not in st.session_state: st.session_state.stripe_mode = "Sandbox (Simulace)"
98
  if "stripe_api_key" not in st.session_state: st.session_state.stripe_api_key = ""
99
 
100
- # Inicializace výchozího týmu (Sazby ukládáme interně v základní měně EUR)
 
 
 
 
 
 
101
  if "db_team" not in st.session_state:
102
  st.session_state.db_team = [
103
  {"id": 1, "jmeno": "Šimon (Director)", "sazba": 45.0},
@@ -112,11 +83,27 @@ if "db_customers" not in st.session_state:
112
  {"id": 3, "jmeno": "Zdeněk Urban", "firma": "Instalatérství Urban", "telefon": "+420 731 555 999"}
113
  ]
114
 
115
- if "db_quotes" not in st.session_state: st.session_state.db_quotes = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
 
117
  if "db_invoices" not in st.session_state:
118
  st.session_state.db_invoices = [
119
- {"id": 1, "cislo": "20260001", "zakaznik": "Jan Novák", "castka": 420.00, "stav": "Odesláno", "datum": "2026-06-01"},
120
  {"id": 2, "cislo": "20260002", "zakaznik": "Marie Podlahová", "castka": 1580.50, "stav": "Odesláno", "datum": "2026-06-10"},
121
  {"id": 3, "cislo": "20260003", "zakaznik": "Zdeněk Urban", "castka": 290.00, "stav": "Zaplaceno", "datum": "2026-06-12"}
122
  ]
@@ -135,7 +122,6 @@ def format_price(amount_in_eur):
135
  return f"{converted:,.2f} {c_info['symbol']}"
136
  return f"{c_info['symbol']}{converted:,.2f}"
137
 
138
- # Spuštění jednorázových balónků při zakoupení / aktivaci PRO
139
  if st.session_state.user_subscribed:
140
  if not st.session_state.balloons_triggered:
141
  st.balloons()
@@ -143,7 +129,7 @@ if st.session_state.user_subscribed:
143
  else:
144
  st.session_state.balloons_triggered = False
145
 
146
- t = TRANSLATIONS[st.session_state.lang]
147
 
148
  # -----------------------------------------------------------------------------
149
  # SIDEBAR: CONFIG & ADMIN
@@ -161,7 +147,7 @@ with st.sidebar:
161
  if st.button("Odemknout Admin Panel"):
162
  if pwd_input == "admin123":
163
  st.session_state.admin_authenticated = True
164
- st.success("Přístup udělen!")
165
  st.rerun()
166
  else:
167
  st.error("Nesprávné heslo!")
@@ -169,16 +155,11 @@ with st.sidebar:
169
  st.info("🔓 Úspěšně autorizováno")
170
  st.session_state.user_subscribed = st.toggle("Uživatel má zakoupen PRO tarif", value=st.session_state.user_subscribed)
171
  st.session_state.quotes_count = st.number_input("Počet vygenerovaných nabídek", min_value=0, max_value=10, value=st.session_state.quotes_count)
172
- st.session_state.stripe_mode = st.radio("Režim Stripe", ["Sandbox (Simulace)", "Stripe Live API"])
173
 
174
  if st.button("Zamknout panel & Odhlásit se"):
175
  st.session_state.admin_authenticated = False
176
  st.rerun()
177
- if st.button("Resetovat kompletní aplikaci"):
178
- st.session_state.clear()
179
- st.rerun()
180
 
181
- t = TRANSLATIONS[st.session_state.lang]
182
  st.title(t["title"])
183
  st.caption(f"{t['caption']} | Version: {VERSION}")
184
 
@@ -189,7 +170,7 @@ tab_dashboard, tab_generator, tab_team, tab_crm, tab_fakturace, tab_pro_features
189
  ])
190
 
191
  # -----------------------------------------------------------------------------
192
- # TAB 1: METRIKY
193
  # -----------------------------------------------------------------------------
194
  with tab_dashboard:
195
  st.header(t["tab_dash"])
@@ -203,6 +184,71 @@ with tab_dashboard:
203
  c3.metric(t["potential"], format_price(potencial), help="Počítá pouze Přesné fixní nabídky. Orientační odhady se sem nepromítají.")
204
  c4.metric(t["limit"], f"{st.session_state.quotes_count} / 3", delta="⚠️ LIMIT" if is_locked else None)
205
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  st.subheader("Přehled vytvořených nabídek a kalkulací")
207
  if st.session_state.db_quotes:
208
  df_raw = pd.DataFrame(st.session_state.db_quotes)
@@ -210,28 +256,15 @@ with tab_dashboard:
210
  df_display["celkem"] = df_display["celkem"].apply(format_price)
211
  df_display["typ"] = df_display["typ"].map({"odhad": "🔮 Orientační odhad", "fixni": "🎯 Přesná fixní"})
212
  st.dataframe(df_display[["datum", "zakaznik", "typ", "popis", "celkem"]], use_container_width=True)
213
- else:
214
- st.info("Žádné vytvořené nabídky přes AI. Historie faktur je k dispozici v samostatné záložce.")
215
 
216
  # -----------------------------------------------------------------------------
217
- # TAB 2: AI GENERÁTOR NABÍDEK (PŘEKOPANÝ PODLE SPECIFIKACE)
218
  # -----------------------------------------------------------------------------
219
  with tab_generator:
220
  st.header(t["tab_gen"])
221
  if is_locked:
222
  st.error(t["paywall_msg"])
223
- with st.form("stripe_pay_form"):
224
- st.subheader("TradeFlow PRO Subscription Gateway")
225
- st.write(f"Částka k úhradě: {format_price(29.00)} / měsíčně")
226
- st.text_input("Card Number", value="4242 4242 4242 4242")
227
- st.text_input("CVC", value="123")
228
- if st.form_submit_button("Aktivovat neomezený účet PRO"):
229
- st.session_state.user_subscribed = True
230
- st.success("Platba proběhla úspěšně!")
231
- time.sleep(0.5)
232
- st.rerun()
233
  else:
234
- # Volba typu rozpočtu
235
  quote_type = st.radio(
236
  "Vyberte typ výstupu a rozpočtování:",
237
  ["🔮 Orientační odhad od AI (Nenahrává se do dlužníků/faktur, čistě orientační budget)",
@@ -244,22 +277,22 @@ with tab_generator:
244
  input_task = st.text_area("Specifikace projektu a popis zakázky", value="Kompletní rekonstrukce rodinného domu, rozvody vody a finální zapojení sanitární techniky.")
245
 
246
  st.markdown("### 🕒 Odpracované hodiny týmu na projektu")
247
- st.caption("Systém automaticky načítá hodinové sazby z karty 'Správa Týmu'.")
248
 
249
- # Simulace AI odhadu hodin, pokud si uživatel vyžádá pomoc AI
250
  if st.button("🪄 Předvyplnit hodiny a materiál pomocí AI odhadu"):
251
  st.session_state[f"hours_mock_triggered"] = True
252
- st.toast("AI provedla analýzu textu a navrhla optimální časy!", icon="🪄")
 
 
 
 
 
 
253
 
254
  team_hours = {}
255
  cols_team = st.columns(max(len(st.session_state.db_team), 1))
256
 
257
  for idx, member in enumerate(st.session_state.db_team):
258
  with cols_team[idx]:
259
- # Přepočet sazby do aktuální měny pro zobrazení
260
- local_rate = member["sazba"] * CURRENCIES[st.session_state.curr]["rate"]
261
-
262
- # Výchozí hodiny na základě stisknutého tlačítka AI odhadu
263
  default_hours = 0
264
  if st.session_state.get(f"hours_mock_triggered", False):
265
  if "director" in member["jmeno"].lower(): default_hours = 12
@@ -272,53 +305,92 @@ with tab_generator:
272
  )
273
  team_hours[member["id"]] = hours_input
274
 
275
- st.markdown("### 🚚 Dodatečné přímé náklady zakázky")
276
- c_cost1, c_cost2, c_cost3 = st.columns(3)
 
 
277
  curr_sym = CURRENCIES[st.session_state.curr]["symbol"]
278
  curr_rate = CURRENCIES[st.session_state.curr]["rate"]
279
 
280
- # Výchozí hodnoty pro materiál při AI odhadu
281
- default_mat = 0.0
282
- if st.session_state.get(f"hours_mock_triggered", False):
283
- default_mat = 450.0 * curr_rate
284
-
285
- with c_cost1:
286
- cost_material = st.number_input(f"Materiál ({curr_sym})", min_value=0.0, value=default_mat)
287
- with c_cost2:
288
- cost_gas = st.number_input(f"Benzín / Doprava ({curr_sym})", min_value=0.0, value=40.0 if default_mat > 0 else 0.0)
289
- with c_cost3:
290
- cost_rent = st.number_input(f"Pronájem vybavení a strojů ({curr_sym})", min_value=0.0, value=0.0)
291
-
292
- # Výpočet celkové sumy
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
293
  total_labor_eur = 0.0
294
  for member in st.session_state.db_team:
295
  total_labor_eur += team_hours[member["id"]] * member["sazba"]
296
 
297
- total_extra_local = cost_material + cost_gas + cost_rent
298
  total_extra_eur = total_extra_local / curr_rate
299
  total_project_eur = total_labor_eur + total_extra_eur
300
 
301
- st.markdown(f"### 📈 Celková kalkulace: {format_price(total_project_eur)}")
302
 
303
  if st.button("💾 Uložit rozpočet do systému", type="primary"):
 
 
 
 
 
 
 
 
 
304
  new_quote = {
305
  "id": len(st.session_state.db_quotes) + 1,
306
  "zakaznik": selected_cust,
307
  "popis": input_task,
308
  "celkem": total_project_eur,
309
  "typ": "fixni" if is_fixed else "odhad",
310
- "datum": time.strftime("%Y-%m-%d")
 
 
311
  }
312
  st.session_state.db_quotes.append(new_quote)
313
  st.session_state.quotes_count += 1
314
  st.success(f"Uloženo jako {'🎯 PŘESNÁ FIXNÍ NABÍDKA' if is_fixed else '🔮 ORIENTAČNÍ ODHAD'}")
315
- # Reset flagu pro AI odhad hodin
 
316
  st.session_state[f"hours_mock_triggered"] = False
 
317
  time.sleep(0.5)
318
  st.rerun()
319
 
320
  # -----------------------------------------------------------------------------
321
- # TAB 3: NOVÁ KARTA — 👥 SPRÁVA TÝMU (ZADÁNO UŽIVATELEM)
322
  # -----------------------------------------------------------------------------
323
  with tab_team:
324
  st.header("👥 Správa firemního týmu a hodinových sazeb")
@@ -340,7 +412,6 @@ with tab_team:
340
  else:
341
  st.warning("Tým nemá žádné členy. Přidejte lidi v pravém panelu.")
342
 
343
- # Smazání člena týmu
344
  st.markdown("**❌ Odebrat člena z týmu**")
345
  if st.session_state.db_team:
346
  member_to_remove = st.selectbox("Vyberte osobu k propuštění/odebrání", [m["jmeno"] for m in st.session_state.db_team])
@@ -354,17 +425,12 @@ with tab_team:
354
  st.markdown("### ➕ Přidat / Upravit člena")
355
  with st.form("add_team_member_form"):
356
  tm_name = st.text_input("Jméno a příjmení / Role", placeholder="Např. Honza (Zedník)")
357
-
358
- # Sazbu zadává uživatel v aktuálně zvolené měně dashboardu, systém ji interně přepočte do EUR základny
359
  c_symbol = CURRENCIES[st.session_state.curr]["symbol"]
360
  tm_rate_local = st.number_input(f"Hodinová sazba ({c_symbol}/hod)", min_value=1, value=500 if st.session_state.curr == "CZK" else 25)
361
 
362
  if st.form_submit_button("Uložit do týmu"):
363
  if tm_name:
364
- # Přepočet zpět do interní EUR měny
365
  rate_in_eur = tm_rate_local / CURRENCIES[st.session_state.curr]["rate"]
366
-
367
- # Kontrola, zda už jméno neexistuje pro update, jinak insert
368
  existing = next((m for m in st.session_state.db_team if m["jmeno"].lower() == tm_name.lower()), None)
369
  if existing:
370
  existing["sazba"] = rate_in_eur
@@ -375,8 +441,6 @@ with tab_team:
375
  st.success(f"Člen {tm_name} byl úspěšně přidán do týmu.")
376
  time.sleep(0.4)
377
  st.rerun()
378
- else:
379
- st.error("Vyplňte jméno člena týmu.")
380
 
381
  # -----------------------------------------------------------------------------
382
  # TAB 4: CRM LITE
@@ -394,15 +458,13 @@ with tab_crm:
394
  p = st.text_input(t["phone"])
395
  if st.form_submit_button(t["save_cust"]) and n:
396
  st.session_state.db_customers.append({"id": len(st.session_state.db_customers)+1, "jmeno": n, "firma": f if f else "Fyzická osoba", "telefon": p})
397
- st.success("Saved"); time.sleep(0.3); st.rerun()
398
 
399
  # -----------------------------------------------------------------------------
400
  # TAB 5: FAKTURACE (POUZE PRO FIXNÍ NABÍDKY)
401
  # -----------------------------------------------------------------------------
402
  with tab_fakturace:
403
  st.header(t["tab_inv"])
404
-
405
- # FILTROVÁNÍ: Do výběru jdou VÝHRADNĚ fixní přesné nabídky! Odhady jsou skryty!
406
  valid_quotes = [q for q in st.session_state.db_quotes if q.get("typ") == "fixni"]
407
 
408
  if valid_quotes:
@@ -419,7 +481,7 @@ with tab_fakturace:
419
  "stav": "Odesláno",
420
  "datum": time.strftime("%Y-%m-%d")
421
  })
422
- st.success("Faktura vystavena a odeslána neplatiči!")
423
  st.rerun()
424
  else:
425
  st.info("V systému nejsou žádné hotové 'Přesné fixní nabídky' k fakturaci. (Orientační odhady nelze fakturovat).")
@@ -435,17 +497,12 @@ with tab_fakturace:
435
  st.rerun()
436
 
437
  # -----------------------------------------------------------------------------
438
- # TAB 6: PRO BONUSY (AUTOMATIZOVANÉ SMS CENTRUM S OPRAVOU FONTU A AI GENERÁTOREM)
439
  # -----------------------------------------------------------------------------
440
  with tab_pro_features:
441
  st.header(t["tab_pro"])
442
  if not st.session_state.user_subscribed:
443
  st.warning("🔒 Tato sekce obsahuje prémiové enterprise moduly dostupné pouze pro uživatele s aktivovaným tarifem TradeFlow PRO.")
444
- st.markdown("""
445
- ### Co se zde nachází?
446
- 1. **AI Smart Logistics Router:** Spočítá nejoptimálnější trasu výjezdů mezi klienty pro maximální úsporu paliva a času.
447
- 2. **Pokročilé SMS Centrum dlužníků:** Kompletní přehled neplatičů, vyhledávání, pokročilé řazení pohledávek a správa vlastních SMS šablon s inteligentním párováním dat.
448
- """)
449
  else:
450
  p_subtab1, p_subtab2 = st.tabs([t["pro_router"], t["pro_sms"]])
451
 
@@ -458,143 +515,79 @@ with tab_pro_features:
458
  with p_subtab2:
459
  st.subheader("📱 Pokročilé SMS Centrum upomínek")
460
 
461
- # 1. FILTRACE A ŘAZENÍ NEPLATIČŮ
462
  st.markdown("### 🔍 1. Vyhledání a seřazení dlužníků")
463
  unpaid_invoices = [inv for inv in st.session_state.db_invoices if inv["stav"] == "Odesláno"]
464
 
465
  sc1, sc2 = st.columns([1, 1])
466
  with sc1:
467
- search_query = st.text_input("Hledat dlužníka podle jména nebo čísla faktury", value="", placeholder="Zadejte jméno nebo FA...")
468
  with sc2:
469
- sort_option = st.selectbox("Seřadit neplatiče podle kritéria", [
470
- "Od A do Z", "Od Z do A",
471
- "Od nejstarší pohledávky", "Od nejnovější pohledávky",
472
- "Od nejvyšší částky", "Od nejnižší částky"
473
- ])
474
 
475
- if search_query:
476
- unpaid_invoices = [i for i in unpaid_invoices if search_query.lower() in i["zakaznik"].lower() or search_query in i["cislo"]]
477
-
478
- if sort_option == "Od A do Z": unpaid_invoices.sort(key=lambda x: x["zakaznik"].lower())
479
- elif sort_option == "Od Z do A": unpaid_invoices.sort(key=lambda x: x["zakaznik"].lower(), reverse=True)
480
- elif sort_option == "Od nejstarší pohledávky": unpaid_invoices.sort(key=lambda x: x["datum"])
481
- elif sort_option == "Od nejnovější pohledávky": unpaid_invoices.sort(key=lambda x: x["datum"], reverse=True)
482
- elif sort_option == "Od nejvyšší částky": unpaid_invoices.sort(key=lambda x: x["castka"], reverse=True)
483
  elif sort_option == "Od nejnižší částky": unpaid_invoices.sort(key=lambda x: x["castka"])
484
 
485
  if not unpaid_invoices:
486
- st.info("V systému aktuálně nejsou žádné nezaplacené faktury odpovídající filtrům.")
487
  chosen_debtor = None
488
  else:
489
- debtor_options = {f"👤 {inv['zakaznik']} (FA: {inv['cislo']} | Dluží: {format_price(inv['castka'])} | Vystaveno: {inv['datum']})": inv for inv in unpaid_invoices}
490
- selected_debtor_label = st.selectbox("🎯 Vyberte dlužníka, kterému chcete poslat upomínku", list(debtor_options.keys()))
491
  chosen_debtor = debtor_options[selected_debtor_label]
492
 
493
  st.markdown("---")
494
-
495
- # 2. SPRÁVA PRESETŮ (CRUD + NOVÝ AI GENERÁTOR TEXTU PRESETŮ)
496
  st.markdown("### 📋 2. Výběr a správa šablon (Presetů)")
497
 
498
  preset_names = [p["název"] for p in st.session_state.sms_presets]
499
  selected_preset_name = st.selectbox("Zvolte aktivní SMS šablonu", preset_names)
500
  current_preset = next(p for p in st.session_state.sms_presets if p["název"] == selected_preset_name)
501
 
502
- with st.expander("⚙️ Nastavení presetů & AI psaní šablon (Přidat / Upravit / Smazat)"):
503
- st.write(f"Aktuální počet šablon: {len(st.session_state.sms_presets)} / 10")
504
-
505
- # NOVÁ FUNKCE: AI GENERÁTOR TEXTU PRO PRESETY
506
  st.markdown("🤖 **AI Asistent pro tvorbu textu šablony**")
507
- ai_preset_prompt = st.text_input("Zadejte krátké zadání pro AI (např. 'velmi urgující výzva s hrozbou soudu' nebo 'přátelské upozornění')", placeholder="Napište jak má šablona působit...")
508
 
509
  if st.button("🪄 Vygenerovat text šablony pomocí AI"):
510
  if ai_preset_prompt:
511
- # Simulace komplexní generace na základě promptu klienta
512
  p_lower = ai_preset_prompt.lower()
513
- if "soud" in p_lower or "urg" in p_lower or "důraz" in p_lower:
514
- generated_text = "DŮRAZNÁ VÝZVA: Vážený pane/paní {jmeno}. Upozorňujeme Vás, že faktura {cislo} na částku {castka} je dlouhodobě po splatnosti. Pokud nedojde k okamžité nápravě, budeme nuceni věc postoupit právnímu oddělení."
515
- elif "přátel" in p_lower or "kamarád" in p_lower or "mil" in p_lower:
516
- generated_text = "Zdravím, {jmeno}! Jen přátelské echo, že se nám někde zatoulala platba za fakturu {cislo} ({castka}). Mrkneš na to prosím, až budeš mít chvilku? Díky moc!"
517
  else:
518
- generated_text = "Dobrý den, {jmeno}. Připomínáme splatnost faktury {cislo} na částku {castka}. Prosíme o kontrolu a odeslání platby. Děkujeme, Tým TradeFlow AI."
519
-
520
  st.session_state["ai_generated_preset_text"] = generated_text
521
- st.success("AI text úspěšně sestaven! Náhled vidíte níže v poli pro text šablony.")
522
-
523
- st.markdown("---")
524
- st.markdown("**➕ Přidat nový preset**")
525
- new_name = st.text_input("Název nové šablony", key="new_preset_name")
526
 
527
- # Pokud existuje vygenerovaný text od AI, vložíme ho jako výchozí
528
- default_preset_body = st.session_state.get("ai_generated_preset_text", "Dobrý den, {jmeno}, ... text šablony ...")
529
- new_text = st.text_area("Text šablony. Povinné značky k automatickému propisování: {jmeno}, {cislo}, {castka}", value=default_preset_body, key="new_preset_text")
530
 
531
- if st.button("Uložit novou šablonu do paměti"):
532
- if len(st.session_state.sms_presets) >= 10:
533
- st.error("Dosaženo limitu maximálně 10 šablon.")
534
- elif not new_name or not new_text:
535
- st.error("Vyplňte název i text šablony.")
536
- else:
537
  st.session_state.sms_presets.append({"název": new_name, "text": new_text})
538
- st.success("Šablona úspěšně přidána!")
539
- if "ai_generated_preset_text" in st.session_state: del st.session_state["ai_generated_preset_text"]
540
- st.rerun()
541
-
542
- st.markdown("---")
543
- st.markdown(f"**📝 Upravit aktuální šablonu: {selected_preset_name}**")
544
- edited_text = st.text_area("Upravit tělo šablony", value=current_preset["text"], key="edit_preset_text")
545
- if st.button("Uložit provedené změny"):
546
- current_preset["text"] = edited_text
547
- st.success("Změny uloženy!")
548
- st.rerun()
549
-
550
- st.markdown("---")
551
- st.markdown(f"**❌ Odstranit šablonu: {selected_preset_name}**")
552
- if st.button("Smazat vybranou šablonu z databáze", type="secondary"):
553
- if len(st.session_state.sms_presets) <= 1:
554
- st.error("Musíte v systému nechat alespoň jednu výchozí šablonu.")
555
- else:
556
- st.session_state.sms_presets = [p for p in st.session_state.sms_presets if p["název"] != selected_preset_name]
557
- st.success("Šablona byla vymazána.")
558
  st.rerun()
559
 
560
  st.markdown("---")
561
-
562
- # 3. GENERÁTOR VÝSLEDNÉ SMS (ZDE JE OPRAVA HNUSNÉHO FONTU A ZOBRAZENÍ Z OBRÁZKU)
563
  st.markdown("### 📱 3. Finální vygenerovaná zpráva pro klienta")
564
  if chosen_debtor:
565
  raw_sms_template = current_preset["text"]
566
-
567
- # Propojení reálných dat dlužníka do tagů v šabloně
568
  compiled_sms = raw_sms_template.replace("{jmeno}", chosen_debtor["zakaznik"])\
569
  .replace("{cislo}", chosen_debtor["cislo"])\
570
  .replace("{castka}", format_price(chosen_debtor["castka"]))
571
 
572
- # OPRAVA FONTU A ZALAMOVÁNÍ TEXTU (Zpětná vazba k obrázku image_dd77dc.png):
573
- # Používáme čistý systémový bezpatkový font a vynucujeme zalamování slov pomocí CSS stylů.
574
  st.markdown(f"""
575
  <div style="
576
  background-color: #f8f9fa;
577
  border: 1px solid #dee2e6;
578
  padding: 18px;
579
  border-radius: 8px;
580
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
581
  font-size: 16px;
582
  color: #212529;
583
  white-space: pre-wrap;
584
  word-wrap: break-word;
585
- line-height: 1.6;
586
- box-shadow: inset 0 1px 2px rgba(0,0,0,0.05);
587
  margin-bottom: 15px;
588
  ">{compiled_sms}</div>
589
  """, unsafe_allow_html=True)
590
 
591
- # Tlačítko pro přímé zkopírování textu do schránky (One-Click Copy)
592
- # Použijeme pomocný schovaný text-area pro nativní kopírování, které Streamlit umí, ale upravený tak, aby netrpěl neduhem zkrácení
593
- st.text_area("📋 Rychlá schránka pro zkopírování (Ctrl+A -> Ctrl+C):", value=compiled_sms, height=100, help="Klikněte dovnitř a zkopírujte text.")
594
-
595
- if st.button("📲 Odeslat na mobilní telefon přes API bránu", type="primary", use_container_width=True):
596
- with st.spinner("Odesílám zprávu dlužníkovi..."):
597
- time.sleep(1.0)
598
- st.success(f"SMS byla úspěšně předána operátorovi a odeslána na číslo klienta {chosen_debtor['zakaznik']}.")
599
- else:
600
- st.warning("Chcete-li sestavit upomínku, musíte mít v sekci 1 vybraného dlužníka (fakturu ve stavu Nezaplaceno).")
 
20
  },
21
  "SK": {
22
  "title": "🏆 TradeFlow AI", "caption": "Chytry operačný systém pre novú generáciu remeselníkov.",
23
+ "tab_dash": "📈 Prehľad & Metriky", "tab_gen": "📝 AI Generátor Ponúk", "tab_team": "👥 Správa Týmu", "tab_crm": "👥 Adresár (CRM)", "tab_inv": "🧾 Fakturácia jedným klikom", "tab_pro": "🚀 PRO Exkluzívne Funkcie",
24
  "profit": "Skutočný zisk", "pending": "Čakajúce peniaze", "potential": "Otvorené AI Ponuky", "limit": "Využitie Free limitu",
25
  "cust_name": "Meno a priezvisko", "company": "Firma (Voliteľné)", "phone": "Telefónne číslo", "save_cust": "Uložiť klienta",
26
  "generate_btn": "⚡ Vygenerovať/Uložiť rozpočet", "invoice_btn": "🧾 VYSTAVIŤ FAKTÚRU JEDNÝM KLIKNUTÍM",
 
35
  "generate_btn": "⚡ Generate/Save Quote", "invoice_btn": "🧾 ISSUE 1-CLICK INVOICE",
36
  "paywall_msg": "🔒 You have reached the 3 free quotes limit. Unlock PRO tier for unlimited access.",
37
  "pro_router": "📍 AI Route & Logistics Planner", "pro_sms": "📲 Intelligent SMS Reminder Center"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  }
39
  }
40
 
 
49
  # -----------------------------------------------------------------------------
50
  # INICIALIZACE STAVU
51
  # -----------------------------------------------------------------------------
52
+ VERSION = "1.9.0-ENTERPRISE"
53
 
54
  st.set_page_config(page_title="TradeFlow AI", page_icon="🏆", layout="wide", initial_sidebar_state="expanded")
55
 
 
62
  if "stripe_mode" not in st.session_state: st.session_state.stripe_mode = "Sandbox (Simulace)"
63
  if "stripe_api_key" not in st.session_state: st.session_state.stripe_api_key = ""
64
 
65
+ # Dočasný paměťový zásobník pro dynamické řádky nákladů v generátoru
66
+ if "temp_extra_costs" not in st.session_state:
67
+ st.session_state.temp_extra_costs = [
68
+ {"kategorie": "Materiál", "nazev": "Spojovací materiál & Sanitární trubky", "castka": 250.0}
69
+ ]
70
+
71
+ # Inicializace výchozího týmu
72
  if "db_team" not in st.session_state:
73
  st.session_state.db_team = [
74
  {"id": 1, "jmeno": "Šimon (Director)", "sazba": 45.0},
 
83
  {"id": 3, "jmeno": "Zdeněk Urban", "firma": "Instalatérství Urban", "telefon": "+420 731 555 999"}
84
  ]
85
 
86
+ # Předvyplněná vzorová nabídka, aby analýza nákladů hned fungovala a ukazovala data
87
+ if "db_quotes" not in st.session_state:
88
+ st.session_state.db_quotes = [
89
+ {
90
+ "id": 1,
91
+ "zakaznik": "Jan Novák",
92
+ "popis": "Kompletní instalace rozvodů",
93
+ "celkem": 1250.0,
94
+ "typ": "fixni",
95
+ "datum": "2026-06-05",
96
+ "labor_eur": 750.0,
97
+ "extra_costs": [
98
+ {"kategorie": "Materiál", "nazev": "Měděné trubky", "eur": 400.0},
99
+ {"kategorie": "Benzín / Doprava", "nazev": "Doprava na stavbu", "eur": 100.0}
100
+ ]
101
+ }
102
+ ]
103
 
104
  if "db_invoices" not in st.session_state:
105
  st.session_state.db_invoices = [
106
+ {"id": 1, "cislo": "20260001", "zakaznik": "Jan Novák", "castka": 1250.00, "stav": "Zaplaceno", "datum": "2026-06-05"},
107
  {"id": 2, "cislo": "20260002", "zakaznik": "Marie Podlahová", "castka": 1580.50, "stav": "Odesláno", "datum": "2026-06-10"},
108
  {"id": 3, "cislo": "20260003", "zakaznik": "Zdeněk Urban", "castka": 290.00, "stav": "Zaplaceno", "datum": "2026-06-12"}
109
  ]
 
122
  return f"{converted:,.2f} {c_info['symbol']}"
123
  return f"{c_info['symbol']}{converted:,.2f}"
124
 
 
125
  if st.session_state.user_subscribed:
126
  if not st.session_state.balloons_triggered:
127
  st.balloons()
 
129
  else:
130
  st.session_state.balloons_triggered = False
131
 
132
+ t = TRANSLATIONS[st.session_state.lang if st.session_state.lang in TRANSLATIONS else "CZ"]
133
 
134
  # -----------------------------------------------------------------------------
135
  # SIDEBAR: CONFIG & ADMIN
 
147
  if st.button("Odemknout Admin Panel"):
148
  if pwd_input == "admin123":
149
  st.session_state.admin_authenticated = True
150
+ st.success("Přístupováno!")
151
  st.rerun()
152
  else:
153
  st.error("Nesprávné heslo!")
 
155
  st.info("🔓 Úspěšně autorizováno")
156
  st.session_state.user_subscribed = st.toggle("Uživatel má zakoupen PRO tarif", value=st.session_state.user_subscribed)
157
  st.session_state.quotes_count = st.number_input("Počet vygenerovaných nabídek", min_value=0, max_value=10, value=st.session_state.quotes_count)
 
158
 
159
  if st.button("Zamknout panel & Odhlásit se"):
160
  st.session_state.admin_authenticated = False
161
  st.rerun()
 
 
 
162
 
 
163
  st.title(t["title"])
164
  st.caption(f"{t['caption']} | Version: {VERSION}")
165
 
 
170
  ])
171
 
172
  # -----------------------------------------------------------------------------
173
+ # TAB 1: METRIKY & NOVÉ FILTROVÁNÍ NÁKLADOVÝCH SEKCÍ
174
  # -----------------------------------------------------------------------------
175
  with tab_dashboard:
176
  st.header(t["tab_dash"])
 
184
  c3.metric(t["potential"], format_price(potencial), help="Počítá pouze Přesné fixní nabídky. Orientační odhady se sem nepromítají.")
185
  c4.metric(t["limit"], f"{st.session_state.quotes_count} / 3", delta="⚠️ LIMIT" if is_locked else None)
186
 
187
+ # 🌟 NOVÁ FUNKCE: POKROČILÉ FILTROVÁNÍ SEKCÍ NÁKLADŮ
188
+ st.markdown("---")
189
+ st.subheader("📊 Anal��za a filtrování nákladových sekcí")
190
+ st.write("Zde vidíte globální přehled investovaných financí roztříděný podle konkrétních typů nákladů napříč všemi uloženými rozpočty.")
191
+
192
+ # Sestavení ploché tabulky všech existujících nákladů z paměti
193
+ flat_costs = []
194
+ for q in st.session_state.db_quotes:
195
+ # Přidání výplat z personálních hodin
196
+ if q.get("labor_eur", 0) > 0:
197
+ flat_costs.append({
198
+ "Projekt / Klient": f"{q['zakaznik']} ({q['datum']})",
199
+ "Sekce nákladu": "Výplaty zaměstnancům",
200
+ "Název položky": "Mzda týmu na projektu (odpracované hodiny)",
201
+ "Částka (Základ v EUR)": q["labor_eur"],
202
+ "Typ nabídky": "🎯 Přesná fixní" if q["typ"] == "fixni" else "🔮 Orientační odhad"
203
+ })
204
+ # Přidání dynamických vedlejších nákladů
205
+ for ec in q.get("extra_costs", []):
206
+ flat_costs.append({
207
+ "Projekt / Klient": f"{q['zakaznik']} ({q['datum']})",
208
+ "Sekce nákladu": ec["kategorie"],
209
+ "Název položky": ec["nazev"] if ec["nazev"] else "Nespecifikováno",
210
+ "Částka (Základ v EUR)": ec["eur"],
211
+ "Typ nabídky": "🎯 Přesná fixní" if q["typ"] == "fixni" else "🔮 Orientační odhad"
212
+ })
213
+
214
+ if flat_costs:
215
+ df_costs = pd.DataFrame(flat_costs)
216
+
217
+ # Filtrační lišta
218
+ f_col1, f_col2 = st.columns(2)
219
+ with f_col1:
220
+ available_cats = ["Zobrazit vše"] + list(df_costs["Sekce nákladu"].unique())
221
+ selected_cat_filter = st.selectbox("Vyberte sekci pro filtr:", available_cats)
222
+ with f_col2:
223
+ selected_type_filter = st.radio("Filtrovat typ budgetu:", ["Všechny rozpočty", "Pouze závazné přesné nabídky", "Pouze orientační odhady"], horizontal=True)
224
+
225
+ # Aplikace filtrů
226
+ if selected_cat_filter != "Zobrazit vše":
227
+ df_costs = df_costs[df_costs["Sekce nákladu"] == selected_cat_filter]
228
+
229
+ if selected_type_filter == "Pouze závazné přesné nabídky":
230
+ df_costs = df_costs[df_costs["Typ nabídky"] == "🎯 Přesná fixní"]
231
+ elif selected_type_filter == "Pouze orientační odhady":
232
+ df_costs = df_costs[df_costs["Typ nabídky"] == "🔮 Orientační odhad"]
233
+
234
+ if not df_costs.empty:
235
+ # Výpočet sumy v měně uživatele
236
+ total_filtered_eur = df_costs["Částka (Základ v EUR)"].sum()
237
+
238
+ st.markdown(f"#### 💰 Celková útrata ve vyfiltrované sekci: **{format_price(total_filtered_eur)}**")
239
+
240
+ # Formátování tabulky pro zobrazení uživateli
241
+ df_costs_display = df_costs.copy()
242
+ df_costs_display["Částka"] = df_costs_display["Částka (Základ v EUR)"].apply(format_price)
243
+ df_costs_display = df_costs_display.drop(columns=["Částka (Základ v EUR)"])
244
+
245
+ st.dataframe(df_costs_display, use_container_width=True, hide_index=True)
246
+ else:
247
+ st.info("Pro vybranou kombinaci filtrů nebyly nalezeny žádné uložené položky.")
248
+ else:
249
+ st.info("Zatím nebyly uloženy žádné nabídky s podrobným rozpadem nákladů.")
250
+
251
+ st.markdown("---")
252
  st.subheader("Přehled vytvořených nabídek a kalkulací")
253
  if st.session_state.db_quotes:
254
  df_raw = pd.DataFrame(st.session_state.db_quotes)
 
256
  df_display["celkem"] = df_display["celkem"].apply(format_price)
257
  df_display["typ"] = df_display["typ"].map({"odhad": "🔮 Orientační odhad", "fixni": "🎯 Přesná fixní"})
258
  st.dataframe(df_display[["datum", "zakaznik", "typ", "popis", "celkem"]], use_container_width=True)
 
 
259
 
260
  # -----------------------------------------------------------------------------
261
+ # TAB 2: AI GENERÁTOR NABÍDEK (S DYNAMICKÝMI KOLONKAMI NÁKLADŮ)
262
  # -----------------------------------------------------------------------------
263
  with tab_generator:
264
  st.header(t["tab_gen"])
265
  if is_locked:
266
  st.error(t["paywall_msg"])
 
 
 
 
 
 
 
 
 
 
267
  else:
 
268
  quote_type = st.radio(
269
  "Vyberte typ výstupu a rozpočtování:",
270
  ["🔮 Orientační odhad od AI (Nenahrává se do dlužníků/faktur, čistě orientační budget)",
 
277
  input_task = st.text_area("Specifikace projektu a popis zakázky", value="Kompletní rekonstrukce rodinného domu, rozvody vody a finální zapojení sanitární techniky.")
278
 
279
  st.markdown("### 🕒 Odpracované hodiny týmu na projektu")
 
280
 
 
281
  if st.button("🪄 Předvyplnit hodiny a materiál pomocí AI odhadu"):
282
  st.session_state[f"hours_mock_triggered"] = True
283
+ # AI přepíše dynamické kolonky na odhadované položky
284
+ st.session_state.temp_extra_costs = [
285
+ {"kategorie": "Materiál", "nazev": "AI Odhad: Potrubí a fitinky", "castka": 480.0},
286
+ {"kategorie": "Benzín / Doprava", "nazev": "AI Odhad: Závoz materiálu", "castka": 50.0},
287
+ {"kategorie": "Pronájem vybavení", "nazev": "AI Odhad: Zapůjčení svářečky plastů", "castka": 120.0}
288
+ ]
289
+ st.toast("AI provedla analýzu textu a kompletně navrhla časy i položky!", icon="🪄")
290
 
291
  team_hours = {}
292
  cols_team = st.columns(max(len(st.session_state.db_team), 1))
293
 
294
  for idx, member in enumerate(st.session_state.db_team):
295
  with cols_team[idx]:
 
 
 
 
296
  default_hours = 0
297
  if st.session_state.get(f"hours_mock_triggered", False):
298
  if "director" in member["jmeno"].lower(): default_hours = 12
 
305
  )
306
  team_hours[member["id"]] = hours_input
307
 
308
+ # 🌟 VYLEPŠENÁ SEKCE: DYNAMICKÉ DODATEČNÉ NÁKLADY S PŘIDÁVÁNÍM A MAZÁNÍM
309
+ st.markdown("### 🚚 Přímé dodatečné náklady zakázky")
310
+ st.write("Přidejte libovolné položky nákladů. Každé položce přiřaďte správnou nákladovou sekci pro pozdější filtrování.")
311
+
312
  curr_sym = CURRENCIES[st.session_state.curr]["symbol"]
313
  curr_rate = CURRENCIES[st.session_state.curr]["rate"]
314
 
315
+ # Vykreslení dynamických kolonek
316
+ indices_to_remove = []
317
+ for idx, cost_item in enumerate(st.session_state.temp_extra_costs):
318
+ c_col1, c_col2, c_col3, c_col4 = st.columns([2, 3, 2, 1])
319
+
320
+ with c_col1:
321
+ cost_item["kategorie"] = st.selectbox(
322
+ f"Sekce nákladu #{idx+1}",
323
+ ["Materiál", "Benzín / Doprava", "Pronájem vybavení", "Ostatní režie"],
324
+ key=f"item_cat_{idx}",
325
+ index=["Materiál", "Benzín / Doprava", "Pronájem vybavení", "Ostatní režie"].index(cost_item["kategorie"])
326
+ )
327
+ with c_col2:
328
+ cost_item["nazev"] = st.text_input(f"Název položky / popis #{idx+1}", value=cost_item.get("nazev", ""), key=f"item_name_{idx}", placeholder="Např. Střešní latě 4m")
329
+ with c_col3:
330
+ cost_item["castka"] = st.number_input(f"Částka ({curr_sym}) #{idx+1}", min_value=0.0, value=float(cost_item.get("castka", 0.0)), key=f"item_amt_{idx}")
331
+ with c_col4:
332
+ st.markdown("<div style='padding-top: 24px;'></div>", unsafe_allow_html=True)
333
+ if st.button("🗑️", key=f"item_del_{idx}", help="Odebrat tuto kolonku"):
334
+ indices_to_remove.append(idx)
335
+
336
+ # Zpracování smazání řádku
337
+ if indices_to_remove:
338
+ for i in reversed(indices_to_remove):
339
+ st.session_state.temp_extra_costs.pop(i)
340
+ st.rerun()
341
+
342
+ # Tlačítka pro správu formuláře nákladů
343
+ act_col1, act_col2 = st.columns([1, 3])
344
+ with act_col1:
345
+ if st.button("➕ Přidat kolonku nákladu", use_container_width=True):
346
+ st.session_state.temp_extra_costs.append({"kategorie": "Materiál", "nazev": "", "castka": 0.0})
347
+ st.rerun()
348
+
349
+ st.markdown("---")
350
+
351
+ # Výpočty celkových sum
352
  total_labor_eur = 0.0
353
  for member in st.session_state.db_team:
354
  total_labor_eur += team_hours[member["id"]] * member["sazba"]
355
 
356
+ total_extra_local = sum(item["castka"] for item in st.session_state.temp_extra_costs)
357
  total_extra_eur = total_extra_local / curr_rate
358
  total_project_eur = total_labor_eur + total_extra_eur
359
 
360
+ st.markdown(f"### 📈 Celková kalkulace rozpočtu: {format_price(total_project_eur)}")
361
 
362
  if st.button("💾 Uložit rozpočet do systému", type="primary"):
363
+ # Transformace dynamických nákladů do trvalé databáze (přepočteno na základní EUR)
364
+ saved_extra_costs = []
365
+ for item in st.session_state.temp_extra_costs:
366
+ saved_extra_costs.append({
367
+ "kategorie": item["kategorie"],
368
+ "nazev": item["nazev"],
369
+ "eur": item["castka"] / curr_rate
370
+ })
371
+
372
  new_quote = {
373
  "id": len(st.session_state.db_quotes) + 1,
374
  "zakaznik": selected_cust,
375
  "popis": input_task,
376
  "celkem": total_project_eur,
377
  "typ": "fixni" if is_fixed else "odhad",
378
+ "datum": time.strftime("%Y-%m-%d"),
379
+ "labor_eur": total_labor_eur,
380
+ "extra_costs": saved_extra_costs
381
  }
382
  st.session_state.db_quotes.append(new_quote)
383
  st.session_state.quotes_count += 1
384
  st.success(f"Uloženo jako {'🎯 PŘESNÁ FIXNÍ NABÍDKA' if is_fixed else '🔮 ORIENTAČNÍ ODHAD'}")
385
+
386
+ # Reset stavů formuláře
387
  st.session_state[f"hours_mock_triggered"] = False
388
+ st.session_state.temp_extra_costs = [{"kategorie": "Materiál", "nazev": "", "castka": 0.0}]
389
  time.sleep(0.5)
390
  st.rerun()
391
 
392
  # -----------------------------------------------------------------------------
393
+ # TAB 3: SPRÁVA TÝMU
394
  # -----------------------------------------------------------------------------
395
  with tab_team:
396
  st.header("👥 Správa firemního týmu a hodinových sazeb")
 
412
  else:
413
  st.warning("Tým nemá žádné členy. Přidejte lidi v pravém panelu.")
414
 
 
415
  st.markdown("**❌ Odebrat člena z týmu**")
416
  if st.session_state.db_team:
417
  member_to_remove = st.selectbox("Vyberte osobu k propuštění/odebrání", [m["jmeno"] for m in st.session_state.db_team])
 
425
  st.markdown("### ➕ Přidat / Upravit člena")
426
  with st.form("add_team_member_form"):
427
  tm_name = st.text_input("Jméno a příjmení / Role", placeholder="Např. Honza (Zedník)")
 
 
428
  c_symbol = CURRENCIES[st.session_state.curr]["symbol"]
429
  tm_rate_local = st.number_input(f"Hodinová sazba ({c_symbol}/hod)", min_value=1, value=500 if st.session_state.curr == "CZK" else 25)
430
 
431
  if st.form_submit_button("Uložit do týmu"):
432
  if tm_name:
 
433
  rate_in_eur = tm_rate_local / CURRENCIES[st.session_state.curr]["rate"]
 
 
434
  existing = next((m for m in st.session_state.db_team if m["jmeno"].lower() == tm_name.lower()), None)
435
  if existing:
436
  existing["sazba"] = rate_in_eur
 
441
  st.success(f"Člen {tm_name} byl úspěšně přidán do týmu.")
442
  time.sleep(0.4)
443
  st.rerun()
 
 
444
 
445
  # -----------------------------------------------------------------------------
446
  # TAB 4: CRM LITE
 
458
  p = st.text_input(t["phone"])
459
  if st.form_submit_button(t["save_cust"]) and n:
460
  st.session_state.db_customers.append({"id": len(st.session_state.db_customers)+1, "jmeno": n, "firma": f if f else "Fyzická osoba", "telefon": p})
461
+ st.success("Uloženo"); time.sleep(0.3); st.rerun()
462
 
463
  # -----------------------------------------------------------------------------
464
  # TAB 5: FAKTURACE (POUZE PRO FIXNÍ NABÍDKY)
465
  # -----------------------------------------------------------------------------
466
  with tab_fakturace:
467
  st.header(t["tab_inv"])
 
 
468
  valid_quotes = [q for q in st.session_state.db_quotes if q.get("typ") == "fixni"]
469
 
470
  if valid_quotes:
 
481
  "stav": "Odesláno",
482
  "datum": time.strftime("%Y-%m-%d")
483
  })
484
+ st.success("Faktura vystavena a propsána dlužníkovi!")
485
  st.rerun()
486
  else:
487
  st.info("V systému nejsou žádné hotové 'Přesné fixní nabídky' k fakturaci. (Orientační odhady nelze fakturovat).")
 
497
  st.rerun()
498
 
499
  # -----------------------------------------------------------------------------
500
+ # TAB 6: PRO BONUSY (AUTOMATIZOVANÉ SMS CENTRUM S PÁROVÁNÍM DAT)
501
  # -----------------------------------------------------------------------------
502
  with tab_pro_features:
503
  st.header(t["tab_pro"])
504
  if not st.session_state.user_subscribed:
505
  st.warning("🔒 Tato sekce obsahuje prémiové enterprise moduly dostupné pouze pro uživatele s aktivovaným tarifem TradeFlow PRO.")
 
 
 
 
 
506
  else:
507
  p_subtab1, p_subtab2 = st.tabs([t["pro_router"], t["pro_sms"]])
508
 
 
515
  with p_subtab2:
516
  st.subheader("📱 Pokročilé SMS Centrum upomínek")
517
 
 
518
  st.markdown("### 🔍 1. Vyhledání a seřazení dlužníků")
519
  unpaid_invoices = [inv for inv in st.session_state.db_invoices if inv["stav"] == "Odesláno"]
520
 
521
  sc1, sc2 = st.columns([1, 1])
522
  with sc1:
523
+ search_query = st.text_input("Hledat dlužníka podle jména", value="")
524
  with sc2:
525
+ sort_option = st.selectbox("Seřadit neplatiče podle:", ["Od nejvyšší částky", "Od nejnižší částky"])
 
 
 
 
526
 
527
+ if sort_option == "Od nejvyšší částky": unpaid_invoices.sort(key=lambda x: x["castka"], reverse=True)
 
 
 
 
 
 
 
528
  elif sort_option == "Od nejnižší částky": unpaid_invoices.sort(key=lambda x: x["castka"])
529
 
530
  if not unpaid_invoices:
531
+ st.info("Žádné nezaplacené faktury.")
532
  chosen_debtor = None
533
  else:
534
+ debtor_options = {f"👤 {inv['zakaznik']} (FA: {inv['cislo']} | Dluží: {format_price(inv['castka'])})": inv for inv in unpaid_invoices}
535
+ selected_debtor_label = st.selectbox("🎯 Vyberte dlužníka", list(debtor_options.keys()))
536
  chosen_debtor = debtor_options[selected_debtor_label]
537
 
538
  st.markdown("---")
 
 
539
  st.markdown("### 📋 2. Výběr a správa šablon (Presetů)")
540
 
541
  preset_names = [p["název"] for p in st.session_state.sms_presets]
542
  selected_preset_name = st.selectbox("Zvolte aktivní SMS šablonu", preset_names)
543
  current_preset = next(p for p in st.session_state.sms_presets if p["název"] == selected_preset_name)
544
 
545
+ with st.expander("⚙️ Nastavení presetů & AI psaní šablon"):
 
 
 
546
  st.markdown("🤖 **AI Asistent pro tvorbu textu šablony**")
547
+ ai_preset_prompt = st.text_input("Zadejte krátké zadání pro AI", placeholder="Napište jak má šablona působit...")
548
 
549
  if st.button("🪄 Vygenerovat text šablony pomocí AI"):
550
  if ai_preset_prompt:
 
551
  p_lower = ai_preset_prompt.lower()
552
+ if "soud" in p_lower or "urg" in p_lower:
553
+ generated_text = "DŮRAZNÁ VÝZVA: Vážený pane/paní {jmeno}. Faktura {cislo} na částku {castka} je po splatnosti. Žádáme o okamžitou úhradu, abychom předešli právním krokům."
 
 
554
  else:
555
+ generated_text = "Ahoj {jmeno}, jen rychlé upozornění, že faktura {cislo} ({castka}) je po splatnosti. Díky za vyřešení!"
 
556
  st.session_state["ai_generated_preset_text"] = generated_text
557
+ st.success("AI text úspěšně sestaven!")
 
 
 
 
558
 
559
+ new_name = st.text_input("Název nové šablony")
560
+ default_preset_body = st.session_state.get("ai_generated_preset_text", "Dobrý den, {jmeno}, ...")
561
+ new_text = st.text_area("Text šablony ({jmeno}, {cislo}, {castka})", value=default_preset_body)
562
 
563
+ if st.button("Uložit novou šablonu"):
564
+ if new_name and new_text:
 
 
 
 
565
  st.session_state.sms_presets.append({"název": new_name, "text": new_text})
566
+ st.success("Přidáno!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
567
  st.rerun()
568
 
569
  st.markdown("---")
 
 
570
  st.markdown("### 📱 3. Finální vygenerovaná zpráva pro klienta")
571
  if chosen_debtor:
572
  raw_sms_template = current_preset["text"]
 
 
573
  compiled_sms = raw_sms_template.replace("{jmeno}", chosen_debtor["zakaznik"])\
574
  .replace("{cislo}", chosen_debtor["cislo"])\
575
  .replace("{castka}", format_price(chosen_debtor["castka"]))
576
 
 
 
577
  st.markdown(f"""
578
  <div style="
579
  background-color: #f8f9fa;
580
  border: 1px solid #dee2e6;
581
  padding: 18px;
582
  border-radius: 8px;
583
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
584
  font-size: 16px;
585
  color: #212529;
586
  white-space: pre-wrap;
587
  word-wrap: break-word;
588
+ line-height: 1.6;
 
589
  margin-bottom: 15px;
590
  ">{compiled_sms}</div>
591
  """, unsafe_allow_html=True)
592
 
593
+ st.text_area("📋 Rychlá schránka pro zkopírování (Ctrl+A -> Ctrl+C):", value=compiled_sms, height=100)