simikkk commited on
Commit
1f579c3
·
verified ·
1 Parent(s): 7b902be

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +666 -188
app.py CHANGED
@@ -2,39 +2,513 @@ import streamlit as st
2
  import pandas as pd
3
  import time
4
  import urllib.parse
5
- import stripe # Připraveno pro splnění tvých požadavků v requirements.txt
6
  import streamlit.components.v1 as components # Bezpečný import pro HTML komponenty na Hugging Face
7
 
8
  # -----------------------------------------------------------------------------
9
- # JAZYKOVÉ LOKALIZACE (CZ, SK, EN)
10
  # -----------------------------------------------------------------------------
11
  TRANSLATIONS = {
12
  "CZ": {
13
- "title": "🏆 TradeFlow AI", "caption": "Chytrý operační systém pro novou generaci řemeslníků.",
14
- "tab_dash": "📈 Přehled & Metriky", "tab_gen": "📝 AI Generátor Nabídek", "tab_team": "👥 Správa Týmu", "tab_crm": "👥 Adresář (CRM)", "tab_inv": "🧾 Fakturace jedním klikem", "tab_pro": "🚀 PRO Exkluzivní Funkce",
15
- "profit": "Skutečný zisk", "pending": "Čekající peníze", "potential": "Otevřené AI Nabídky", "limit": "Využití Free limitu",
16
- "cust_name": "Jméno a příjmení", "company": "Firma (Volitelné)", "phone": "Telefonní číslo", "save_cust": "Uložit klienta",
17
- "generate_btn": "⚡ Vygenerovat/Uložit rozpočet", "invoice_btn": "🧾 VYSTAVIT FAKTURU JEDNÍM KLIKNUTÍM",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  "paywall_msg": "🔒 Dosáhli jste limitu 3 bezplatných nabídek. Pro neomezený přístup aktivujte tarif PRO.",
19
- "pro_router": "📍 AI Plánovač tras a logistiky", "pro_sms": "📲 Inteligentní SMS Centrum upomínek"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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ář (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": "🧾 VYSTAÚŤ FAKTÚRU JEDNÝM KLIKNUTÍM",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  "paywall_msg": "🔒 Dosiahli ste limitu 3 bezplatných ponúk. Pre neobmedzený prístup aktivujte tarif PRO.",
28
- "pro_router": "📍 AI Plánovač trás a logistiky", "pro_sms": "📲 Inteligentné SMS Centrum upomienok"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  },
30
  "EN": {
31
- "title": "🏆 TradeFlow AI", "caption": "Smart operating system for the next-gen skilled trades.",
32
- "tab_dash": "📈 Dashboard & Metrics", "tab_gen": "📝 AI Quote Generator", "tab_team": "👥 Team Management", "tab_crm": "👥 Contacts (CRM)", "tab_inv": "🧾 1-Click Invoicing", "tab_pro": "🚀 PRO Features",
33
- "profit": "Actual Profit", "pending": "Pending Revenue", "potential": "Open AI Quotes", "limit": "Free Tier Usage",
34
- "cust_name": "Full Name", "company": "Company (Optional)", "phone": "Phone Number", "save_cust": "Save Client",
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
 
@@ -47,9 +521,9 @@ CURRENCIES = {
47
  }
48
 
49
  # -----------------------------------------------------------------------------
50
- # INICIALIZACE STAVU (ČISTÉ PRODUKČNÍ PROSTŘEDÍ)
51
  # -----------------------------------------------------------------------------
52
- VERSION = "2.1.1-ENTERPRISE"
53
 
54
  st.set_page_config(page_title="TradeFlow AI", page_icon="🏆", layout="wide", initial_sidebar_state="expanded")
55
 
@@ -59,7 +533,6 @@ if "admin_authenticated" not in st.session_state: st.session_state.admin_authent
59
  if "user_subscribed" not in st.session_state: st.session_state.user_subscribed = False
60
  if "quotes_count" not in st.session_state: st.session_state.quotes_count = 0
61
 
62
- # Ostré databáze startují 100% čisté a prázdné
63
  if "db_team" not in st.session_state: st.session_state.db_team = []
64
  if "db_customers" not in st.session_state: st.session_state.db_customers = []
65
  if "db_quotes" not in st.session_state: st.session_state.db_quotes = []
@@ -72,7 +545,8 @@ if "sms_presets" not in st.session_state:
72
  {"název": "Kamarádská připomínka", "text": "Ahoj {jmeno}, jen rychlá připomínka, že faktura {cislo} na částku {castka} už čeká na proplacení. Díky moc!"}
73
  ]
74
 
75
- # Čistý výchozí řádek editoru bez předvyplněných částek
 
76
  if "cost_items_df" not in st.session_state:
77
  st.session_state.cost_items_df = pd.DataFrame([
78
  {"Sekce nákladu": "Materiál", "Název položky / Popis": "", "Částka": 0.0}
@@ -85,36 +559,37 @@ def format_price(amount_in_eur):
85
  return f"{converted:,.2f} {c_info['symbol']}"
86
  return f"{c_info['symbol']}{converted:,.2f}"
87
 
88
- t = TRANSLATIONS[st.session_state.lang if st.session_state.lang in TRANSLATIONS else "CZ"]
89
-
90
  # -----------------------------------------------------------------------------
91
- # SIDEBAR: CONFIG & ROBUSTNÍ DATA SEEDING PRO POROTU / INVESTORY
92
  # -----------------------------------------------------------------------------
93
  with st.sidebar:
94
- st.header("🌐 Nastavení / Settings")
95
- st.session_state.lang = st.selectbox("Language / Jazyk", list(TRANSLATIONS.keys()), index=list(TRANSLATIONS.keys()).index(st.session_state.lang))
96
- st.session_state.curr = st.selectbox("Měna / Currency", list(CURRENCIES.keys()), index=list(CURRENCIES.keys()).index(st.session_state.curr))
 
 
 
 
97
 
98
  st.markdown("---")
99
- st.header("🛡️ Dev Admin Panel")
100
 
101
  if not st.session_state.admin_authenticated:
102
- pwd_input = st.text_input("Zadejte administrátorské heslo", type="password", help="Heslo pro testování je: admin123")
103
- if st.button("Odemknout Admin Panel"):
104
  if pwd_input == "admin123":
105
  st.session_state.admin_authenticated = True
106
- st.success("Přístup povolen!")
107
  st.rerun()
108
  else:
109
- st.error("Nesprávné heslo!")
110
  else:
111
- st.info("🔓 Vývojářská autorizace")
112
- st.session_state.user_subscribed = st.toggle("Uživatel má aktivní PRO tarif", value=st.session_state.user_subscribed)
113
- st.session_state.quotes_count = st.number_input("Počet vytvořených nabídek", min_value=0, max_value=20, value=st.session_state.quotes_count)
114
 
115
- st.markdown("### 🧪 Správa ukázkových dat")
116
- if st.button("📥 Importovat komplexní Demo Data", use_container_width=True):
117
- # Demo struktura týmu se nahraje až teď!
118
  st.session_state.db_team = [
119
  {"id": 1, "jmeno": "Šimon (Director)", "sazba": 45.0},
120
  {"id": 2, "jmeno": "Nikola (Prezentace & Podpora)", "sazba": 30.0},
@@ -147,23 +622,23 @@ with st.sidebar:
147
  {"id": 4, "cislo": "20260004", "zakaznik": "Jan Novák", "castka": 5010.00, "stav": "Odesláno", "datum": "2026-06-15"}
148
  ]
149
  st.session_state.quotes_count = 1
150
- st.toast("Systém byl úspěšně naplněn cvičnými daty!", icon="📥")
151
  time.sleep(0.3)
152
  st.rerun()
153
 
154
- if st.button("🗑️ Kompletní vyčištění databází", use_container_width=True):
155
  st.session_state.db_team = []
156
  st.session_state.db_customers = []
157
  st.session_state.db_quotes = []
158
  st.session_state.db_invoices = []
159
  st.session_state.quotes_count = 0
160
  st.session_state.cost_items_df = pd.DataFrame([{"Sekce nákladu": "Materiál", "Název položky / Popis": "", "Částka": 0.0}])
161
- st.toast("Aplikace byla kompletně vyčištěna.", icon="🗑️")
162
  time.sleep(0.3)
163
  st.rerun()
164
 
165
  st.markdown("---")
166
- if st.button("Zamknout panel & Odhlásit se", use_container_width=True):
167
  st.session_state.admin_authenticated = False
168
  st.rerun()
169
 
@@ -177,7 +652,7 @@ tab_dashboard, tab_generator, tab_team, tab_crm, tab_fakturace, tab_pro_features
177
  ])
178
 
179
  # -----------------------------------------------------------------------------
180
- # TAB 1: METRIKY & ROBUSTNÍ FILTROVÁNÍ NÁKLADOVÝCH SEKCÍ
181
  # -----------------------------------------------------------------------------
182
  with tab_dashboard:
183
  st.header(t["tab_dash"])
@@ -188,30 +663,30 @@ with tab_dashboard:
188
  c1, c2, c3, c4 = st.columns(4)
189
  c1.metric(t["profit"], format_price(total_revenue))
190
  c2.metric(t["pending"], format_price(pending_revenue))
191
- c3.metric(t["potential"], format_price(potencial), help="Počítá pouze Přesné fixní nabídky.")
192
- c4.metric(t["limit"], f"{st.session_state.quotes_count} / 3", delta="⚠️ LIMIT UKONČEN" if is_locked else None)
193
 
194
  st.markdown("---")
195
- st.subheader("📊 Pokročilá analýza a finanční filtrování sekcí")
196
- st.write("Sledujte finanční toky seskupené podle konkrétních nákladových sekcí napříč celou firmou.")
197
 
198
  flat_costs = []
199
  for q in st.session_state.db_quotes:
200
  if q.get("labor_eur", 0) > 0:
201
  flat_costs.append({
202
- "Projekt / Klient": f"{q['zakaznik']} ({q['datum']})",
203
- "Sekce nákladu": "Výplaty zaměstnancům",
204
- "Název položky": "Mzda týmu na projektu (odpracované hodiny)",
205
  "Částka (Základ v EUR)": q["labor_eur"],
206
- "Typ rozpočtu": "🎯 Přesná fixní" if q["typ"] == "fixni" else "🔮 Orientační odhad"
207
  })
208
  for ec in q.get("extra_costs", []):
209
  flat_costs.append({
210
- "Projekt / Klient": f"{q['zakaznik']} ({q['datum']})",
211
- "Sekce nákladu": ec["kategorie"],
212
- "Název položky": ec["nazev"] if ec["nazev"] else "Nespecifikovaný přímý výdaj",
213
  "Částka (Základ v EUR)": ec["eur"],
214
- "Typ rozpočtu": "🎯 Přesná fixní" if q["typ"] == "fixni" else "🔮 Orientační odhad"
215
  })
216
 
217
  if flat_costs:
@@ -219,83 +694,86 @@ with tab_dashboard:
219
 
220
  f_col1, f_col2 = st.columns(2)
221
  with f_col1:
222
- available_cats = ["Zobrazit vše"] + list(df_costs["Sekce nákladu"].unique())
223
- selected_cat_filter = st.selectbox("Zvolte nákladovou kategorii k odfiltrování:", available_cats)
224
  with f_col2:
225
- selected_type_filter = st.radio("Filtrovat typ zakázky:", ["Všechny rozpočty", "Pouze závazné přesné nabídky", "Pouze orientační odhady"], horizontal=True)
226
 
227
- if selected_cat_filter != "Zobrazit vše":
228
- df_costs = df_costs[df_costs["Sekce nákladu"] == selected_cat_filter]
229
 
230
- if selected_type_filter == "Pouze závazné přesné nabídky":
231
- df_costs = df_costs[df_costs["Typ rozpočtu"] == "🎯 Přesná fixní"]
232
- elif selected_type_filter == "Pouze orientační odhady":
233
- df_costs = df_costs[df_costs["Typ rozpočtu"] == "🔮 Orientační odhad"]
234
 
235
  if not df_costs.empty:
236
  total_filtered_eur = df_costs["Částka (Základ v EUR)"].sum()
237
- st.markdown(f"#### 💰 Agregovaná útrata ve zvoleném filtru: **{format_price(total_filtered_eur)}**")
238
 
239
  df_costs_display = df_costs.copy()
240
- df_costs_display["Částka"] = df_costs_display["Částka (Základ v EUR)"].apply(format_price)
241
  df_costs_display = df_costs_display.drop(columns=["Částka (Základ v EUR)"])
242
  st.dataframe(df_costs_display, use_container_width=True, hide_index=True)
243
  else:
244
- st.info("Pro zadaná kritéria a filtry nebyly nalezeny žádné záznamy.")
245
  else:
246
- st.info("V systému zatím nejsou evidovány žádné podrobné položky nákladů.")
247
 
248
  st.markdown("---")
249
- st.subheader("Kniha vytvořených kalkulací a nabídek")
250
  if st.session_state.db_quotes:
251
  df_raw = pd.DataFrame(st.session_state.db_quotes)
252
  df_display = df_raw.copy()
253
- df_display["celkem"] = df_display["celkem"].apply(format_price)
254
- df_display["typ"] = df_display["typ"].map({"odhad": "🔮 Orientační odhad", "fixni": "🎯 Přesná fixní"})
255
- st.dataframe(df_display[["datum", "zakaznik", "typ", "popis", "celkem"]], use_container_width=True, hide_index=True)
 
 
 
 
 
 
 
256
  else:
257
- st.info("Kniha nabídek je prázdná.")
258
 
259
  # -----------------------------------------------------------------------------
260
- # TAB 2: AI GENERÁTOR (S OPRAVENÝM NEBLIKAJÍCÍM CSV IMPORTEM)
261
  # -----------------------------------------------------------------------------
262
  with tab_generator:
263
  st.header(t["tab_gen"])
264
  if is_locked:
265
  st.error(t["paywall_msg"])
266
  else:
267
- quote_type = st.radio(
268
- "Vyberte povahu výstupu a nacenění:",
269
- ["🔮 Orientační odhad od AI (Čistě orientační kalkulace, nepropisuje se do dlužníků)",
270
- "🎯 Přesná fixní nabídka (Závazný finanční plán, slouží jako podklad pro fakturu)"]
271
- )
272
- is_fixed = "Přesná" in quote_type
273
 
274
- st.markdown("### 👤 Přiřazení klienta")
275
  cust_names = [c["jmeno"] for c in st.session_state.db_customers]
276
 
277
- client_mode = st.radio("Způsob zadání zákazníka:", ["Vybrat z adresáře CRM", "✍️ Zadat úplně nového klienta ručně"], horizontal=True)
278
- if client_mode == "Vybrat z adresáře CRM" and cust_names:
279
- selected_cust = st.selectbox("Zvolte klienta:", cust_names)
280
  else:
281
- selected_cust = st.text_input("Zadejte jméno a příjmení nového klienta:", value="", placeholder="Např. František Novotný")
282
 
283
- input_task = st.text_area("Detailní specifikace projektu a zadání pro AI", value="Kompletní rekonstrukce rodinného domu, rozvody vody a finální zapojení sanitární techniky.")
284
 
285
- st.markdown("### 🕒 Personální alokace (Odpracované hodiny týmu)")
286
 
287
  if not st.session_state.db_team:
288
- st.info("ℹ️ V interní databázi zatím nejsou žádní pracovníci. Přidejte je v záložce 'Správa Týmu' nebo zapněte Demo Data v boční liště.")
289
  team_hours = {}
290
  else:
291
- if st.button("🪄 Spustit AI simulaci a předvyplnit položky i hodiny"):
292
  st.session_state["ai_mock_active"] = True
293
  st.session_state.cost_items_df = pd.DataFrame([
294
  {"Sekce nákladu": "Materiál", "Název položky / Popis": "AI Odhad: Potrubí, ventily a fitinky", "Částka": 480.0},
295
- {"Sekce nákladu": "Benzín / Doprava", "Název položky / Popis": "AI Odhad: Nákladní závoz materiálu z velkoobchodu", "Částka": 65.0},
296
  {"Sekce nákladu": "Pronájem vybavení", "Název položky / Popis": "AI Odhad: Zapůjčení tlakové zkoušečky", "Částka": 110.0}
297
  ])
298
- st.toast("AI úspěšně zanalyzovala text a navrhla optimální strukturu nákladů!", icon="🪄")
299
 
300
  team_hours = {}
301
  cols_team = st.columns(len(st.session_state.db_team))
@@ -314,20 +792,20 @@ with tab_generator:
314
  )
315
  team_hours[member["id"]] = hours_input
316
 
317
- st.markdown("### 🚚 Přímé materiálové a vedlejší náklady")
318
- st.caption("💡 Nové řádky přidáte symbolem `+` vespod tabulky. Řádek smažete označením a stiskem klávesy `Delete` na klávesnici.")
319
 
320
- curr_sym = CURRENCIES[st.session_state.curr]["symbol"]
321
  curr_rate = CURRENCIES[st.session_state.curr]["rate"]
322
 
 
323
  column_configuration = {
324
  "Sekce nákladu": st.column_config.SelectboxColumn(
325
- "Sekce nákladu",
326
- options=["Materiál", "Benzín / Doprava", "Pronájem vybavení", "Ostatní režie"],
327
  required=True
328
  ),
329
- "Název položky / Popis": st.column_config.TextColumn("Název položky / Popis", required=True),
330
- "Částka": st.column_config.NumberColumn(f"Částka ({curr_sym})", min_value=0.0, required=True, format="%.2f")
331
  }
332
 
333
  edited_df = st.data_editor(
@@ -339,8 +817,7 @@ with tab_generator:
339
  )
340
  st.session_state.cost_items_df = edited_df
341
 
342
- # --- NOVÝ A OPRAVENÝ BLOK PRO CSV IMPORT (BEZ BLIKÁNÍ) ---
343
- st.markdown("### 📂 Hromadný import externích nákladů přes CSV")
344
 
345
  def zpracuj_csv_import():
346
  if st.session_state.csv_uploader_key is not None:
@@ -351,21 +828,19 @@ with tab_generator:
351
  sanitized_df = uploaded_df[needed_headers].copy()
352
  sanitized_df["Částka"] = pd.to_numeric(sanitized_df["Částka"], errors='coerce').fillna(0.0)
353
 
354
- # Bezpečné připojení dat do existující tabulky v session_state
355
  st.session_state.cost_items_df = pd.concat([st.session_state.cost_items_df, sanitized_df], ignore_index=True)
356
- st.toast("CSV data byla úspěšně nahrána do tabulky výše!", icon="📥")
357
  else:
358
- st.error("Chyba: CSV neodpovídá požadované struktuře záhlaví.")
359
  except Exception as e:
360
- st.error(f"Soubor nelze p��ečíst: {e}")
361
 
362
  st.file_uploader(
363
- "Nahrajte soubor .csv (Struktura sloupců: Sekce nákladu, Název položky / Popis, Částka)",
364
  type=["csv"],
365
  key="csv_uploader_key",
366
- on_change=zpracuj_csv_import
367
  )
368
- # --------------------------------------------------------
369
 
370
  st.markdown("---")
371
 
@@ -377,18 +852,18 @@ with tab_generator:
377
  total_extra_eur = total_extra_local / curr_rate
378
  total_project_eur = total_labor_eur + total_extra_eur
379
 
380
- st.markdown(f"### 📈 Celková kalkulace rozpočtu: {format_price(total_project_eur)}")
381
 
382
- if st.button("💾 Schválit a uložit rozpočet do systému", type="primary"):
383
  if not selected_cust or selected_cust.strip() == "":
384
- st.error("Chyba: Před uložením musíte uvést nebo vybrat jméno klienta!")
385
  else:
386
  if selected_cust not in cust_names:
387
  st.session_state.db_customers.append({
388
  "id": len(st.session_state.db_customers) + 1,
389
  "jmeno": selected_cust.strip(),
390
- "firma": "Fyzická osoba (Automatický import)",
391
- "telefon": "Nezadáno"
392
  })
393
 
394
  saved_extra_costs = []
@@ -415,7 +890,7 @@ with tab_generator:
415
  st.session_state["ai_mock_active"] = False
416
 
417
  st.session_state.cost_items_df = pd.DataFrame([{"Sekce nákladu": "Materiál", "Název položky / Popis": "", "Částka": 0.0}])
418
- st.success("Rozpočet byl úspěšně zanesen do firemní databáze.")
419
  time.sleep(0.4)
420
  st.rerun()
421
 
@@ -423,52 +898,51 @@ with tab_generator:
423
  # TAB 3: SPRÁVA TÝMU
424
  # -----------------------------------------------------------------------------
425
  with tab_team:
426
- st.header("👥 Správa firemního týmu a hodinových sazeb")
427
- st.write("Editujte profily a interní hodinové sazby svých pracovníků.")
428
 
429
  t_col1, t_col2 = st.columns([2, 1])
430
 
431
  with t_col1:
432
- st.markdown("### Evidovaní pracovníci a specialisté")
433
  team_display_list = []
434
  for m in st.session_state.db_team:
435
  team_display_list.append({
436
- "Interní ID": m["id"],
437
- "Jméno člena / Profesní role": m["jmeno"],
438
- "Hodinová mzdová sazba": format_price(m["sazba"])
439
  })
440
  if team_display_list:
441
  st.dataframe(pd.DataFrame(team_display_list), use_container_width=True, hide_index=True)
442
  else:
443
- st.info("V týmu se aktuálně nenachází žádný pracovník. Použijte formulář vpravo nebo načtěte Demo data.")
444
 
445
- st.markdown("**❌ Odstranění pracovníka z týmu**")
446
  if st.session_state.db_team:
447
- member_to_remove = st.selectbox("Vyberte osobu k odebrání:", [m["jmeno"] for m in st.session_state.db_team])
448
- if st.button("Definitivně odebrat člena"):
449
  st.session_state.db_team = [m for m in st.session_state.db_team if m["jmeno"] != member_to_remove]
450
- st.success(f"Pracovník {member_to_remove} byl vyřazen z interní databáze.")
451
  time.sleep(0.3)
452
  st.rerun()
453
 
454
  with t_col2:
455
- st.markdown("### ➕ Přidat / Upravit kartu pracovníka")
456
  with st.form("team_management_form"):
457
- new_tm_name = st.text_input("Jméno příjmení / Pracovní zařazení", placeholder="Např. Kamil (Zedník)")
458
- local_symbol = CURRENCIES[st.session_state.curr]["symbol"]
459
- new_tm_rate = st.number_input(f"Hodinová sazba v aktivní měně ({local_symbol}/h)", min_value=1, value=400 if st.session_state.curr == "CZK" else 20)
460
 
461
- if st.form_submit_button("Uložit profil"):
462
  if new_tm_name:
463
  rate_in_eur = new_tm_rate / CURRENCIES[st.session_state.curr]["rate"]
464
  matched_member = next((m for m in st.session_state.db_team if m["jmeno"].lower() == new_tm_name.lower()), None)
465
  if matched_member:
466
  matched_member["sazba"] = rate_in_eur
467
- st.success(f"Sazba u pracovníka {new_tm_name} byla zaktualizována.")
468
  else:
469
  next_id = max([m["id"] for m in st.session_state.db_team]) + 1 if st.session_state.db_team else 1
470
  st.session_state.db_team.append({"id": next_id, "jmeno": new_tm_name, "sazba": rate_in_eur})
471
- st.success(f"Nový člen {new_tm_name} byl úspěšně zaevidován.")
472
  time.sleep(0.3)
473
  st.rerun()
474
 
@@ -476,15 +950,21 @@ with tab_team:
476
  # TAB 4: CRM ADRESÁŘ
477
  # -----------------------------------------------------------------------------
478
  with tab_crm:
479
- st.header(t["tab_crm"])
480
  col_crm1, col_crm2 = st.columns([2, 1])
481
  with col_crm1:
482
  if st.session_state.db_customers:
483
- st.dataframe(pd.DataFrame(st.session_state.db_customers)[["jmeno", "firma", "telefon"]], use_container_width=True, hide_index=True)
 
 
 
 
 
 
484
  else:
485
- st.info("Adresář klientů je momentálně prázdný.")
486
  with col_crm2:
487
- st.markdown("### ➕ Rychlé ruční založení klienta")
488
  with st.form("manual_crm_form"):
489
  c_n = st.text_input(t["cust_name"], placeholder="Jan Novák")
490
  c_f = st.text_input(t["company"], placeholder="Stavebniny s.r.o.")
@@ -494,23 +974,25 @@ with tab_crm:
494
  st.session_state.db_customers.append({
495
  "id": len(st.session_state.db_customers) + 1,
496
  "jmeno": c_n,
497
- "firma": c_f if c_f else "Fyzická osoba",
498
  "telefon": c_p
499
  })
500
- st.success("Klient byl bezpečně uložen do CRM."); time.sleep(0.3); st.rerun()
 
 
501
  else:
502
- st.error("Jméno klienta je povinné pole.")
503
 
504
  # -----------------------------------------------------------------------------
505
  # TAB 5: FAKTURACE
506
  # -----------------------------------------------------------------------------
507
  with tab_fakturace:
508
- st.header(t["tab_inv"])
509
  only_fixni_quotes = [q for q in st.session_state.db_quotes if q.get("typ") == "fixni"]
510
 
511
  if only_fixni_quotes:
512
- invoice_options = {f"Zakázka ID: {q['id']} | Odběratel: {q['zakaznik']} — {format_price(q['celkem'])}": q for q in only_fixni_quotes}
513
- selected_quote_to_inv = st.selectbox("Zvolte schválený pevný rozpočet k okamžité fakturaci:", list(invoice_options.keys()))
514
  targeted_quote = invoice_options[selected_quote_to_inv]
515
 
516
  if st.button(t["invoice_btn"], type="primary", use_container_width=True):
@@ -522,30 +1004,30 @@ with tab_fakturace:
522
  "stav": "Odesláno",
523
  "datum": time.strftime("%Y-%m-%d")
524
  })
525
- st.success("Faktura byla vystavena a uložena do knihy!")
526
  time.sleep(0.3)
527
  st.rerun()
528
  else:
529
- st.info("V systému se nenachází žádná schválená pevná nabídka (Orientační odhady nelze fakturovat).")
530
 
531
  st.markdown("---")
532
- st.subheader("Kompletní přehled vydaných faktur a stavu plateb")
533
 
534
  if st.session_state.db_invoices:
535
  for index_inv, invoice_obj in enumerate(st.session_state.db_invoices):
536
  div_c1, div_c2, div_c3 = st.columns([2, 1, 1])
537
- div_c1.write(f"🧾 **Faktura č. {invoice_obj['cislo']}** — {invoice_obj['zakaznik']} [**{format_price(invoice_obj['castka'])}**]")
538
 
539
  if invoice_obj["stav"] == "Zaplaceno":
540
- div_c2.markdown("<span style='color:green; font-weight:bold;'>🟢 Uhrazeno</span>", unsafe_allow_html=True)
541
  div_c3.write("")
542
  else:
543
- div_c2.markdown("<span style='color:orange; font-weight:bold;'>🔵 Odesláno (Po splatnosti)</span>", unsafe_allow_html=True)
544
- if div_c3.button("Označit jako ZAPLACENO", key=f"payment_trigger_key_{index_inv}", use_container_width=True):
545
  invoice_obj["stav"] = "Zaplaceno"
546
  st.rerun()
547
  else:
548
- st.info("Kniha faktur je prázdná.")
549
 
550
  # -----------------------------------------------------------------------------
551
  # TAB 6: PRO BONUSY
@@ -553,78 +1035,77 @@ with tab_fakturace:
553
  with tab_pro_features:
554
  st.header(t["tab_pro"])
555
  if not st.session_state.user_subscribed:
556
- st.warning("🔒 Tento panel obsahuje pokročilé moduly TradeFlow PRO. Aktivujte zkušební verzi v Dev Admin Panelu na boční liště.")
557
  else:
558
  pro_sub1, pro_sub2 = st.tabs([t["pro_router"], t["pro_sms"]])
559
 
560
  with pro_sub1:
561
- st.subheader("📍 AI Geografický optimalizátor tras výjezdů")
562
- st.text_area("Seznam adres plánovaných zásahů", value="Plzeňská 15, Praha\nPrůmyslová 4, Beroun\nSukova 12, Plzeň")
563
- if st.button("🗺️ Spočítat matematicky nejkratší trasu", use_container_width=True):
564
- st.info("🚀 AI Výsledek kalkulace: Výjezd z Plzně -> Beroun -> Praha. Celková úspora paliva a logistických nákladů činí: 42 km.")
565
 
566
  with pro_sub2:
567
- st.subheader("📱 Inteligentní manažerské SMS centrum pohledávek")
568
-
569
- st.markdown("### 🔍 1. Vyhledání dlužníka v systému")
570
  open_debts = [invoice for invoice in st.session_state.db_invoices if invoice["stav"] == "Odesláno"]
571
 
572
  search_col1, search_col2 = st.columns([1, 1])
573
  with search_col1:
574
- query_name = st.text_input("Filtrovat neplatiče dle jména:", value="")
575
  with search_col2:
576
- sorting_order = st.selectbox("Seřadit dlužníky dle kritéria:", ["Od nejvyšší dlužné částky", "Od nejnižší dlužné částky"])
577
 
578
  if query_name:
579
  open_debts = [d for d in open_debts if query_name.lower() in d["zakaznik"].lower()]
580
 
581
- if sorting_order == "Od nejvyšší dlužné částky":
582
  open_debts.sort(key=lambda x: x["castka"], reverse=True)
583
  else:
584
  open_debts.sort(key=lambda x: x["castka"])
585
 
586
  if not open_debts:
587
- st.info("V systému aktuálně neevidujeme žádné neuhrazené faktury odpovídající filtrům.")
588
  active_debtor = None
589
  else:
590
  debtor_mapping = {f"👤 {d['zakaznik']} (Faktura č. {d['cislo']} | Dluh: {format_price(d['castka'])})": d for d in open_debts}
591
- selected_debtor_string = st.selectbox("🎯 Vyberte cílového dlužníka k obeslání:", list(debtor_mapping.keys()))
592
  active_debtor = debtor_mapping[selected_debtor_string]
593
 
594
  st.markdown("---")
595
- st.markdown("### 📋 2. Výběr textové šablony")
596
 
597
  preset_labels = [p["název"] for p in st.session_state.sms_presets]
598
- chosen_preset_label = st.selectbox("Zvolte aktivní komunikační šablonu:", preset_labels)
599
  active_preset = next(p for p in st.session_state.sms_presets if p["název"] == chosen_preset_label)
600
 
601
- with st.expander("⚙️ Pokročilý editor šablon & Generátor nových textů přes AI"):
602
- st.markdown("🤖 **AI Asistent pro tvorbu tónu textu**")
603
- prompt_text = st.text_input("Zadejte zadání pro AI (např. 'Napiš velmi naléhavou předžalobní výzvu')", placeholder="Napište zadání...")
604
 
605
- if st.button("🪄 Sestavit novou šablonu pomocí AI"):
606
  if prompt_text:
607
  p_low = prompt_text.lower()
608
  if "předžal" in p_low or "naléh" in p_low or "soud" in p_low:
609
- result_sms = "PŘEDŽALOBNÍ VÝZVA: Vážený kliente {jmeno}. Upozorňujeme Vás, že faktura č. {cislo} na částku {castka} je dlouhodobě po splatnosti. Žádáme o okamžitou úhradu, abychom předešli soudnímu vymáhání."
610
  else:
611
- result_sms = "Dobrý den {jmeno}, připomínáme zapomenutou fakturu č. {cislo} na částku {castka}. Prosíme o co nejrychlejší proplacení. Děkujeme, Tým TradeFlow AI."
612
  st.session_state["ai_sms_output_buffer"] = result_sms
613
- st.success("AI text úspěšně vygenerován do pole níže!")
614
 
615
- new_preset_title = st.text_input("Název nové šablony:", value="")
616
  default_body = st.session_state.get("ai_sms_output_buffer", "Vážený pane/paní {jmeno}, ...")
617
- new_preset_body = st.text_area("Text těla zprávy (používejte značky {jmeno}, {cislo}, {castka})", value=default_body)
618
 
619
- if st.button("Uložit šablonu do trvalého seznamu"):
620
  if new_preset_title and new_preset_body:
621
  st.session_state.sms_presets.append({"název": new_preset_title, "text": new_preset_body})
622
- st.success("Šablona úspěšně uložena!")
623
  time.sleep(0.3)
624
  st.rerun()
625
 
626
  st.markdown("---")
627
- st.markdown("### 📱 3. Finální vygenerovaná zpráva pro klienta")
628
 
629
  if active_debtor:
630
  raw_template = active_preset["text"]
@@ -638,14 +1119,14 @@ with tab_pro_features:
638
  border: 1px solid #dee2e6;
639
  padding: 18px;
640
  border-radius: 8px;
641
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
642
  font-size: 15px;
643
  color: #212529;
644
  white-space: pre-wrap;
645
  word-wrap: break-word;
646
  line-height: 1.6;
647
  margin-bottom: 12px;
648
- ;" id="sms_display_box">{final_compiled_sms}</div>
649
  """, unsafe_allow_html=True)
650
 
651
  safe_encoded_sms = urllib.parse.quote(final_compiled_sms)
@@ -654,9 +1135,9 @@ with tab_pro_features:
654
  <button onclick="
655
  navigator.clipboard.writeText(decodeURIComponent('{safe_encoded_sms}'));
656
  let btn = document.getElementById('js_copy_btn');
657
- btn.innerText = '✅ Zkopírováno do schránky!';
658
  btn.style.backgroundColor = '#28a745';
659
- setTimeout(() => {{ btn.innerText = '📋 Kopírovat text zprávy'; btn.style.backgroundColor = '#007bff'; }}, 2000);
660
  " id="js_copy_btn" style="
661
  background-color: #007bff;
662
  color: white;
@@ -665,13 +1146,10 @@ with tab_pro_features:
665
  font-size: 14px;
666
  border-radius: 6px;
667
  cursor: pointer;
668
- font-family: -apple-system, sans-serif;
669
  font-weight: bold;
670
  width: 100%;
671
- box-shadow: 0 2px 4px rgba(0,0,0,0.05);
672
- transition: background-color 0.2s;
673
- ">📋 Kopírovat text zprávy</button>
674
  """
675
  components.html(copy_button_html, height=50)
676
  else:
677
- st.info("Vyberte dlužníka pro zobrazení a zkopírování SMS zprávy.")
 
2
  import pandas as pd
3
  import time
4
  import urllib.parse
5
+ import stripe # Připraveno pro splnění požadavků v requirements.txt
6
  import streamlit.components.v1 as components # Bezpečný import pro HTML komponenty na Hugging Face
7
 
8
  # -----------------------------------------------------------------------------
9
+ # KOMPLETNÍ JAZYKOVÉ LOKALIZACE PRO VŠECHNY PRVKY NA STRÁNCE (CZ, SK, EN)
10
  # -----------------------------------------------------------------------------
11
  TRANSLATIONS = {
12
  "CZ": {
13
+ # Obecné a záložky
14
+ "title": "🏆 TradeFlow AI",
15
+ "caption": "Chytrý operační systém pro novou generaci řemeslníků.",
16
+ "tab_dash": "📈 Přehled & Metriky",
17
+ "tab_gen": "📝 AI Generátor Nabídek",
18
+ "tab_team": "👥 Správa Týmu",
19
+ "tab_crm": "👥 Adresář (CRM)",
20
+ "tab_inv": "🧾 Fakturace jedním klikem",
21
+ "tab_pro": "🚀 PRO Exkluzivní Funkce",
22
+
23
+ # Sidebar & Admin Panel
24
+ "sb_settings": "🌐 Nastavení",
25
+ "sb_lang": "Jazyk aplikace",
26
+ "sb_curr": "Aktivní měna",
27
+ "sb_dev_panel": "🛡️ Dev Admin Panel",
28
+ "sb_pwd_label": "Zadejte administrátorské heslo",
29
+ "sb_pwd_hint": "Heslo pro testování je: admin123",
30
+ "sb_unlock_btn": "Odemknout Admin Panel",
31
+ "sb_auth_ok": "Přístup povolen!",
32
+ "sb_auth_err": "Nesprávné heslo!",
33
+ "sb_auth_status": "🔓 Vývojářská autorizace",
34
+ "sb_pro_toggle": "Uživatel má aktivní PRO tarif",
35
+ "sb_quotes_count": "Počet vytvořených nabídek",
36
+ "sb_demo_section": "🧪 Správa ukázkových dat",
37
+ "sb_import_demo": "📥 Importovat komplexní Demo Data",
38
+ "sb_clear_demo": "🗑️ Kompletní vyčištění databází",
39
+ "sb_logout": "Zamknout panel & Odhlásit se",
40
+ "toast_demo_ok": "Systém byl úspěšně naplněn cvičnými daty!",
41
+ "toast_clear_ok": "Aplikace byla kompletně vyčištěna.",
42
+
43
+ # Dashboard Metriky
44
+ "profit": "Skutečný zisk",
45
+ "pending": "Čekající peníze",
46
+ "potential": "Otevřené AI Nabídky",
47
+ "limit": "Využití Free limitu",
48
+ "dash_help_potential": "Počítá pouze Přesné fixní nabídky.",
49
+ "dash_limit_warn": "⚠️ LIMIT UKONČEN",
50
+
51
+ # Dashboard Analýza
52
+ "dash_analysis_title": "📊 Pokročilá analýza a finanční filtrování sekcí",
53
+ "dash_analysis_desc": "Sledujte finanční toky seskupené podle konkrétních nákladových sekcí napříč celou firmou.",
54
+ "dash_filter_cat": "Zvolte nákladovou kategorii k odfiltrování:",
55
+ "dash_filter_type": "Filtrovat typ zakázky:",
56
+ "dash_filter_all_cats": "Zobrazit vše",
57
+ "dash_type_all": "Všechny rozpočty",
58
+ "dash_type_fixed": "Pouze závazné přesné nabídky",
59
+ "dash_type_estimated": "Pouze orientační odhady",
60
+ "dash_aggregated_spend": "💰 Agregovaná útrata ve zvoleném filtru:",
61
+ "dash_no_records": "Pro zadaná kritéria a filtry nebyly nalezeny žádné záznamy.",
62
+ "dash_no_costs_yet": "V systému zatím nejsou evidovány žádné podrobné položky nákladů.",
63
+ "dash_book_title": "Kniha vytvořených kalkulací a nabídek",
64
+ "dash_book_empty": "Kniha nabídek je prázdná.",
65
+
66
+ # Sloupce tabulek v Dashboardu
67
+ "col_project": "Projekt / Klient",
68
+ "col_section": "Sekce nákladu",
69
+ "col_item_name": "Název položky / Popis",
70
+ "col_amount": "Částka",
71
+ "col_type": "Typ rozpočtu",
72
+ "col_date": "Datum",
73
+
74
+ # AI Generátor Nabídek
75
+ "gen_type_label": "Vyberte povahu výstupu a nacenění:",
76
+ "gen_type_est": "🔮 Orientační odhad od AI (Čistě orientační kalkulace, nepropisuje se do dlužníků)",
77
+ "gen_type_fix": "🎯 Přesná fixní nabídka (Závazný finanční planar, slouží jako podklad pro fakturu)",
78
+ "gen_client_section": "### 👤 Přiřazení klienta",
79
+ "gen_client_mode": "Způsob zadání zákazníka:",
80
+ "gen_client_crm": "Vybrat z adresáře CRM",
81
+ "gen_client_manual": "✍️ Zadat úplně nového klienta ručně",
82
+ "gen_select_client": "Zvolte klienta:",
83
+ "gen_input_client_name": "Zadejte jméno a příjmení nového klienta:",
84
+ "gen_client_placeholder": "Např. František Novotný",
85
+ "gen_spec_label": "Detailní specifikace projektu a zadání pro AI",
86
+ "gen_spec_default": "Kompletní rekonstrukce rodinného domu, rozvody vody a finální zapojení sanitární techniky.",
87
+ "gen_team_alloc": "### 🕒 Personální alokace (Odpracované hodiny týmu)",
88
+ "gen_no_team_warn": "ℹ️ V interní databázi zatím nejsou žádní pracovníci. Přidejte je v záložce 'Správa Týmu' nebo zapněte Demo Data v boční liště.",
89
+ "gen_ai_btn": "🪄 Spustit AI simulaci a předvyplnit položky i hodiny",
90
+ "gen_ai_toast": "AI úspěšně zanalyzovala text a navrhla optimální strukturu nákladů!",
91
+ "gen_direct_costs": "### 🚚 Přímé materiálové a vedlejší náklady",
92
+ "gen_table_hint": "💡 Nové řádky přidáte symbolem `+` vespod tabulky. Řádek smažete označením a stiskem klávesy `Delete` na klávesnici.",
93
+ "gen_csv_title": "### 📂 Hromadný import externích nákladů přes CSV",
94
+ "gen_csv_uploader": "Nahrajte soubor .csv (Struktura sloupců: Sekce nákladu, Název položky / Popis, Částka)",
95
+ "gen_csv_success": "CSV data byla úspěšně nahrána do tabulky výše!",
96
+ "gen_csv_err_headers": "Chyba: CSV neodpovídá požadované struktuře záhlaví.",
97
+ "gen_csv_err_read": "Soubor nelze přečíst:",
98
+ "gen_total_calc": "### 📈 Celková kalkulace rozpočtu:",
99
+ "gen_save_btn": "💾 Schválit a uložit rozpočet do systému",
100
+ "gen_err_no_client": "Chyba: Před uložením musíte uvést nebo vybrat jméno klienta!",
101
+ "gen_save_success": "Rozpočet byl úspěšně zanesen do firemní databáze.",
102
+
103
+ # Správa týmu
104
+ "team_title": "👥 Správa firemního týmu a mzdových sazeb",
105
+ "team_desc": "Editujte profily a interní hodinové sazby svých pracovníků.",
106
+ "team_sub_list": "Evidovaní pracovníci a specialisté",
107
+ "team_col_id": "Interní ID",
108
+ "team_col_role": "Jméno člena / Profesní role",
109
+ "team_col_rate": "Hodinová mzdová sazba",
110
+ "team_empty": "V týmu se aktuálně nenachází žádný pracovník. Použijte formulář vpravo nebo načtěte Demo data.",
111
+ "team_remove_section": "**❌ Odstranění pracovníka z týmu**",
112
+ "team_remove_select": "Vyberte osobu k odebrání:",
113
+ "team_remove_btn": "Definitivně odebrat člena",
114
+ "team_remove_toast": "Pracovník byl vyřazen z interní databáze.",
115
+ "team_add_section": "### ➕ Přidat / Upravit kartu pracovníka",
116
+ "team_input_name": "Jméno příjmení / Pracovní zařazení",
117
+ "team_input_placeholder": "Např. Kamil (Zedník)",
118
+ "team_input_rate": "Hodinová sazba v aktivní měně",
119
+ "team_submit_btn": "Uložit profil",
120
+ "team_update_toast": "Sazba u pracovníka byla zaktualizována.",
121
+ "team_add_toast": "Nový člen byl úspěšně zaevidován.",
122
+
123
+ # CRM Adresář
124
+ "crm_title": "👥 Adresář zákazníků (CRM)",
125
+ "crm_empty": "Adresář klientů je momentálně prázdný.",
126
+ "crm_add_section": "### ➕ Rychlé ruční založení klienta",
127
+ "cust_name": "Jméno a příjmení",
128
+ "company": "Firma (Volitelné)",
129
+ "phone": "Telefonní číslo",
130
+ "save_cust": "Uložit klienta",
131
+ "crm_err_name": "Jméno klienta je povinné pole.",
132
+ "crm_success_toast": "Klient byl bezpečně uložen do CRM.",
133
+
134
+ # Fakturace
135
+ "inv_title": "🧾 Fakturace jedním klikem",
136
+ "inv_select_quote": "Zvolte schválený pevný rozpočet k okamžité fakturaci:",
137
+ "invoice_btn": "🧾 VYSTAVIT FAKTURU JEDNÍM KLIKNUTÍM",
138
+ "inv_success_toast": "Faktura byla vystavena a uložena do knihy!",
139
+ "inv_no_fixed_quotes": "V systému se nenachází žádná schválená pevná nabídka (Orientační odhady nelze fakturovat).",
140
+ "inv_book_title": "Kompletní přehled vydaných faktur a stavu plateb",
141
+ "inv_status_paid": "🟢 Uhrazeno",
142
+ "inv_status_sent": "🔵 Odesláno (Po splatnosti)",
143
+ "inv_mark_paid": "Označit jako ZAPLACENO",
144
+ "inv_empty": "Kniha faktur je prázdná.",
145
+
146
+ # PRO Funkce
147
  "paywall_msg": "🔒 Dosáhli jste limitu 3 bezplatných nabídek. Pro neomezený přístup aktivujte tarif PRO.",
148
+ "pro_router": "📍 AI Plánovač tras a logistiky",
149
+ "pro_sms": "📲 Inteligentní SMS Centrum upomínek",
150
+ "pro_locked_warn": "🔒 Tento panel obsahuje pokročilé moduly TradeFlow PRO. Aktivujte zkušebn�� verzi v Dev Admin Panelu na boční liště.",
151
+ "pro_route_title": "📍 AI Geografický optimalizátor tras výjezdů",
152
+ "pro_route_label": "Seznam adres plánovaných zásahů",
153
+ "pro_route_btn": "🗺️ Spočítat matematicky nejkratší trasu",
154
+ "pro_route_result": "🚀 AI Výsledek kalkulace: Výjezd z Plzně -> Beroun -> Praha. Celková úspora paliva a logistických nákladů činí: 42 km.",
155
+ "pro_sms_title": "📱 Inteligentní manažerské SMS centrum pohledávek",
156
+ "pro_sms_step1": "### 🔍 1. Vyhledání dlužníka v systému",
157
+ "pro_sms_filter_name": "Filtrovat neplatiče dle jména:",
158
+ "pro_sms_sort_label": "Seřadit dlužníky dle kritéria:",
159
+ "pro_sms_sort_high": "Od nejvyšší dlužné částky",
160
+ "pro_sms_sort_low": "Od nejnižší dlužné částky",
161
+ "pro_sms_no_debts": "V systému aktuálně neevidujeme žádné neuhrazené faktury odpovídající filtrům.",
162
+ "pro_sms_select_debtor": "🎯 Vyberte cílového dlužníka k obeslání:",
163
+ "pro_sms_step2": "### 📋 2. Výběr textové šablony",
164
+ "pro_sms_template_label": "Zvolte aktivní komunikační šablonu:",
165
+ "pro_sms_expander": "⚙️ Pokročilý editor šablon & Generátor nových textů přes AI",
166
+ "pro_sms_ai_assistant": "🤖 **AI Asistent pro tvorbu tónu textu**",
167
+ "pro_sms_ai_prompt": "Zadejte zadání pro AI (např. 'Napiš velmi naléhavou předžalobní výzvu')",
168
+ "pro_sms_ai_btn": "🪄 Sestavit novou šablonu pomocí AI",
169
+ "pro_sms_ai_success": "AI text úspěšně vygenerován do pole níže!",
170
+ "pro_sms_new_title": "Název nové šablony:",
171
+ "pro_sms_new_body": "Text těla zprávy (používejte značky {jmeno}, {cislo}, {castka})",
172
+ "pro_sms_save_template_btn": "Uložit šablonu do trvalého seznamu",
173
+ "pro_sms_template_success": "Šablona úspěšně uložena!",
174
+ "pro_sms_step3": "### 📱 3. Finální vygenerovaná zpráva pro klienta",
175
+ "pro_sms_copy_btn": "📋 Kopírovat text zprávy",
176
+ "pro_sms_copy_ok": "✅ Zkopírováno do schránky!",
177
+ "pro_sms_select_info": "Vyberte dlužníka pro zobrazení a zkopírování SMS zprávy."
178
  },
179
  "SK": {
180
+ # Obecné a záložky
181
+ "title": "🏆 TradeFlow AI",
182
+ "caption": "Chytrý operačný systém pre novú generáciu remeselníkov.",
183
+ "tab_dash": "📈 Prehľad & Metriky",
184
+ "tab_gen": "📝 AI Generátor Ponúk",
185
+ "tab_team": "👥 Správa Tímu",
186
+ "tab_crm": "👥 Adresár (CRM)",
187
+ "tab_inv": "🧾 Fakturácia jedným klikom",
188
+ "tab_pro": "🚀 PRO Exkluzívne Funkcie",
189
+
190
+ # Sidebar & Admin Panel
191
+ "sb_settings": "🌐 Nastavenia",
192
+ "sb_lang": "Jazyk aplikácie",
193
+ "sb_curr": "Aktívna mena",
194
+ "sb_dev_panel": "🛡️ Dev Admin Panel",
195
+ "sb_pwd_label": "Zadajte administrátorské heslo",
196
+ "sb_pwd_hint": "Heslo pre testovanie je: admin123",
197
+ "sb_unlock_btn": "Odomknúť Admin Panel",
198
+ "sb_auth_ok": "Prístup povolený!",
199
+ "sb_auth_err": "Nesprávne heslo!",
200
+ "sb_auth_status": "🔓 Vývojárska autorizácia",
201
+ "sb_pro_toggle": "Užívateľ má aktívny PRO tarif",
202
+ "sb_quotes_count": "Počet vytvorených ponúk",
203
+ "sb_demo_section": "🧪 Správa ukážkových dát",
204
+ "sb_import_demo": "📥 Importovať komplexné Demo Dáta",
205
+ "sb_clear_demo": "🗑️ Kompletné vyčistenie databáz",
206
+ "sb_logout": "Zamknúť panel & Odhlásiť sa",
207
+ "toast_demo_ok": "Systém bol úspešne naplnený cvičnými dátami!",
208
+ "toast_clear_ok": "Aplikácia bola kompletne vyčistená.",
209
+
210
+ # Dashboard Metriky
211
+ "profit": "Skutočný zisk",
212
+ "pending": "Čakajúce peniaze",
213
+ "potential": "Otvorené AI Ponuky",
214
+ "limit": "Využitie Free limitu",
215
+ "dash_help_potential": "Počíta iba Presné fixné ponuky.",
216
+ "dash_limit_warn": "⚠️ LIMIT UKONČENÝ",
217
+
218
+ # Dashboard Analýza
219
+ "dash_analysis_title": "📊 Pokročilá analýza a finančné filtrovanie sekcií",
220
+ "dash_analysis_desc": "Sledujte finančné toky zoskupené podľa konkrétnych nákladových sekcií naprieč celou firmou.",
221
+ "dash_filter_cat": "Zvoľte nákladovú kategóriu na odfiltrovanie:",
222
+ "dash_filter_type": "Filtrovať typ zákazky:",
223
+ "dash_filter_all_cats": "Zobraziť všetko",
224
+ "dash_type_all": "Všetky rozpočty",
225
+ "dash_type_fixed": "Iba záväzné presné ponuky",
226
+ "dash_type_estimated": "Iba orientačné odhady",
227
+ "dash_aggregated_spend": "💰 Agregovaná útrata vo zvolenom filtri:",
228
+ "dash_no_records": "Pre zadané kritériá a filtre neboli nájdené žiadne záznamy.",
229
+ "dash_no_costs_yet": "V systéme zatiaľ nie sú evidované žiadne podrobné položky nákladov.",
230
+ "dash_book_title": "Kniha vytvorených kalkulácií a ponúk",
231
+ "dash_book_empty": "Kniha ponúk je prázdna.",
232
+
233
+ # Sloupce tabulek v Dashboardu
234
+ "col_project": "Projekt / Klient",
235
+ "col_section": "Sekcia nákladu",
236
+ "col_item_name": "Názov položky / Popis",
237
+ "col_amount": "Čiastka",
238
+ "col_type": "Typ rozpočtu",
239
+ "col_date": "Dátum",
240
+
241
+ # AI Generátor Nabídek
242
+ "gen_type_label": "Vyberte povahu výstupu a nacenenie:",
243
+ "gen_type_est": "🔮 Orientačný odhad od AI (Čisto orientačná kalkulácia, neprepisuje sa do dlžníkov)",
244
+ "gen_type_fix": "🎯 Presná fixná ponuka (Záväzný finančný plán, slúži ako podklad pre faktúru)",
245
+ "gen_client_section": "### 👤 Priradenie klienta",
246
+ "gen_client_mode": "Spôsob zadania zákazníka:",
247
+ "gen_client_crm": "Vybrať z adresára CRM",
248
+ "gen_client_manual": "✍️ Zadať úplne nového klienta ručne",
249
+ "gen_select_client": "Zvoľte klienta:",
250
+ "gen_input_client_name": "Zadajte meno a priezvisko nového klienta:",
251
+ "gen_client_placeholder": "Napr. František Novotný",
252
+ "gen_spec_label": "Detailná špecifikácia projektu a zadanie pre AI",
253
+ "gen_spec_default": "Kompletná rekonštrukcia rodinného domu, rozvody vody a finálne zapojenie sanitárnej techniky.",
254
+ "gen_team_alloc": "### 🕒 Personálna alokácia (Odpracované hodiny tímu)",
255
+ "gen_no_team_warn": "ℹ️ V internej databáze zatiaľ nie sú žiadni pracovníci. Pridajte ich v záložke 'Správa Tímu' alebo zapnite Demo Data v bočnej lište.",
256
+ "gen_ai_btn": "🪄 Spustiť AI simuláciu a predvyplniť položky aj hodiny",
257
+ "gen_ai_toast": "AI úspešne zanalyzovala text a navrhla optimálnu štruktúru nákladov!",
258
+ "gen_direct_costs": "### 🚚 Priame materiálové a vedľajšie náklady",
259
+ "gen_table_hint": "💡 Nové riadky pridáte symbolom `+` naspodku tabuľky. Riadok smažete označením a stlačením klávesy `Delete` na klávesnici.",
260
+ "gen_csv_title": "### 📂 Hromadný import externých nákladov cez CSV",
261
+ "gen_csv_uploader": "Nahrajte súbor .csv (Štruktúra stĺpcov: Sekcia nákladu, Název položky / Popis, Částka)",
262
+ "gen_csv_success": "CSV dáta boli úspešne nahrané do tabuľky vyššie!",
263
+ "gen_csv_err_headers": "Chyba: CSV neodpovedá požadovanej štruktúre záhlavia.",
264
+ "gen_csv_err_read": "Súbor nie je možné prečítať:",
265
+ "gen_total_calc": "### 📈 Celková kalkulácia rozpočtu:",
266
+ "gen_save_btn": "💾 Schváliť a uložiť rozpočet do systému",
267
+ "gen_err_no_client": "Chyba: Pred uložením musíte uviesť alebo vybrať meno klienta!",
268
+ "gen_save_success": "Rozpočet bol úspešne zanesený do firemnej databázy.",
269
+
270
+ # Správa týmu
271
+ "team_title": "👥 Správa firemného tímu a mzdových sadzieb",
272
+ "team_desc": "Editujte profily a interné hodinové sadzby svojich pracovníkov.",
273
+ "team_sub_list": "Evidovaní pracovníci a špecialisti",
274
+ "team_col_id": "Interné ID",
275
+ "team_col_role": "Meno člena / Profesijná rola",
276
+ "team_col_rate": "Hodinová mzdová sadzba",
277
+ "team_empty": "V tíme sa aktuálne nenachádza žiadny pracovník. Použite formulár vpravo alebo načítajte Demo dáta.",
278
+ "team_remove_section": "**❌ Odstránenie pracovníka z tímu**",
279
+ "team_remove_select": "Vyberte osobu na odobratie:",
280
+ "team_remove_btn": "Definitívne odobrať člena",
281
+ "team_remove_toast": "Pracovník bol vyradený z internej databázy.",
282
+ "team_add_section": "### ➕ Pridať / Upraviť kartu pracovníka",
283
+ "team_input_name": "Meno priezvisko / Pracovné zaradenie",
284
+ "team_input_placeholder": "Napr. Kamil (Murár)",
285
+ "team_input_rate": "Hodinová sadzba v aktívnej mene",
286
+ "team_submit_btn": "Uložiť profil",
287
+ "team_update_toast": "Sadzba u pracovníka bola zaktualizovaná.",
288
+ "team_add_toast": "Nový člen bol úspešne zaevidovaný.",
289
+
290
+ # CRM Adresář
291
+ "crm_title": "👥 Adresár zákazníkov (CRM)",
292
+ "crm_empty": "Adresár klientov je momentálne prázdny.",
293
+ "crm_add_section": "### ➕ Rýchle ručné založenie klienta",
294
+ "cust_name": "Meno a priezvisko",
295
+ "company": "Firma (Voliteľné)",
296
+ "phone": "Telefónne číslo",
297
+ "save_cust": "Uložiť klienta",
298
+ "crm_err_name": "Meno klienta je povinné pole.",
299
+ "crm_success_toast": "Klient bol bezpečne uložený do CRM.",
300
+
301
+ # Fakturace
302
+ "inv_title": "🧾 Fakturácia jedným klikom",
303
+ "inv_select_quote": "Zvoľte schválený pevný rozpočet k okamžitej fakturácii:",
304
+ "invoice_btn": "🧾 VYSTAVIŤ FAKTÚRU JEDNÝM KLIKNUTÍM",
305
+ "inv_success_toast": "Faktúra bola vystavená a uložená do knihy!",
306
+ "inv_no_fixed_quotes": "V systéme sa nenachádza žiadna schválená pevná ponuka (Orientačné odhady nie je možné fakturovať).",
307
+ "inv_book_title": "Kompletný prehľad vydaných faktúr a stavu platieb",
308
+ "inv_status_paid": "🟢 Uhradené",
309
+ "inv_status_sent": "🔵 Odoslané (Po splatnosti)",
310
+ "inv_mark_paid": "Označiť ako ZAPLATENÉ",
311
+ "inv_empty": "Kniha faktúr je prázdna.",
312
+
313
+ # PRO Funkce
314
  "paywall_msg": "🔒 Dosiahli ste limitu 3 bezplatných ponúk. Pre neobmedzený prístup aktivujte tarif PRO.",
315
+ "pro_router": "📍 AI Plánovač trás a logistiky",
316
+ "pro_sms": "📲 Inteligentné SMS Centrum upomienok",
317
+ "pro_locked_warn": "🔒 Tento panel obsahuje pokročilé moduly TradeFlow PRO. Aktivujte skúšobnú verziu v Dev Admin Panelu na bočnej lište.",
318
+ "pro_route_title": "📍 AI Geografický optimalizátor trás výjazdov",
319
+ "pro_route_label": "Zoznam adries plánovaných zásahov",
320
+ "pro_route_btn": "🗺️ Spočítať matematicky najkratšiu trasu",
321
+ "pro_route_result": "🚀 AI Výsledok kalkulácie: Výjazd z Plzne -> Beroun -> Praha. Celková úspora paliva a logistických nákladov činí: 42 km.",
322
+ "pro_sms_title": "📱 Inteligentné manažérske SMS centrum pohľadávok",
323
+ "pro_sms_step1": "### 🔍 1. Vyhľadanie dlžníka v systéme",
324
+ "pro_sms_filter_name": "Filtrovať neplatičov podľa mena:",
325
+ "pro_sms_sort_label": "Zoradiť dlžníkov podľa kritéria:",
326
+ "pro_sms_sort_high": "Od najvyššej dlžnej sumy",
327
+ "pro_sms_sort_low": "Od najnižšej dlžnej sumy",
328
+ "pro_sms_no_debts": "V systéme aktuálne neevidujeme žiadne neuhradené faktúry zodpovedajúce filtrom.",
329
+ "pro_sms_select_debtor": "🎯 Vyberte cieľového dlžníka na obeslanie:",
330
+ "pro_sms_step2": "### 📋 2. Výber textovej šablóny",
331
+ "pro_sms_template_label": "Zvoľte aktívnu komunikačnú šablónu:",
332
+ "pro_sms_expander": "⚙️ Pokročilý editor šablón & Generátor nových textov cez AI",
333
+ "pro_sms_ai_assistant": "🤖 **AI Asistent pre tvorbu tónu textu**",
334
+ "pro_sms_ai_prompt": "Zadajte zadanie pre AI (napr. 'Napíš veľmi naliehavú predžalobnú výzvu')",
335
+ "pro_sms_ai_btn": "🪄 Zostaviť novú šablónu pomocou AI",
336
+ "pro_sms_ai_success": "AI text úspešne vygenerovaný do poľa nižšie!",
337
+ "pro_sms_new_title": "Názov novej šablóny:",
338
+ "pro_sms_new_body": "Text tela správy (používajte značky {jmeno}, {cislo}, {castka})",
339
+ "pro_sms_save_template_btn": "Uložiť šablónu do trvalého zoznamu",
340
+ "pro_sms_template_success": "Šablóna úspešne uložená!",
341
+ "pro_sms_step3": "### 📱 3. Finálna vygenerovaná správa pre klienta",
342
+ "pro_sms_copy_btn": "📋 Kopírovať text správy",
343
+ "pro_sms_copy_ok": "✅ Skopírované do schránky!",
344
+ "pro_sms_select_info": "Vyberte dlžníka pre zobrazenie a skopírovanie SMS správy."
345
  },
346
  "EN": {
347
+ # Obecné a záložky
348
+ "title": "🏆 TradeFlow AI",
349
+ "caption": "Smart operating system for the next-gen skilled trades.",
350
+ "tab_dash": "📈 Dashboard & Metrics",
351
+ "tab_gen": "📝 AI Quote Generator",
352
+ "tab_team": "👥 Team Management",
353
+ "tab_crm": "👥 Contacts (CRM)",
354
+ "tab_inv": "🧾 1-Click Invoicing",
355
+ "tab_pro": "🚀 PRO Features",
356
+
357
+ # Sidebar & Admin Panel
358
+ "sb_settings": "🌐 Settings",
359
+ "sb_lang": "App Language",
360
+ "sb_curr": "Active Currency",
361
+ "sb_dev_panel": "🛡️ Dev Admin Panel",
362
+ "sb_pwd_label": "Enter Admin Password",
363
+ "sb_pwd_hint": "Testing password is: admin123",
364
+ "sb_unlock_btn": "Unlock Admin Panel",
365
+ "sb_auth_ok": "Access Granted!",
366
+ "sb_auth_err": "Incorrect Password!",
367
+ "sb_auth_status": "🔓 Developer Authorization",
368
+ "sb_pro_toggle": "User has active PRO tier",
369
+ "sb_quotes_count": "Created quotes counter",
370
+ "sb_demo_section": "🧪 Sample Data Management",
371
+ "sb_import_demo": "📥 Import Complex Demo Data",
372
+ "sb_clear_demo": "🗑️ Complete Databases Wipe",
373
+ "sb_logout": "Lock Panel & Log Out",
374
+ "toast_demo_ok": "System filled with sample data successfully!",
375
+ "toast_clear_ok": "Application databases wiped completely.",
376
+
377
+ # Dashboard Metriky
378
+ "profit": "Actual Profit",
379
+ "pending": "Pending Revenue",
380
+ "potential": "Open AI Quotes",
381
+ "limit": "Free Tier Usage",
382
+ "dash_help_potential": "Counts strict Fixed Price quotes only.",
383
+ "dash_limit_warn": "⚠️ LIMIT EXCEEDED",
384
+
385
+ # Dashboard Analýza
386
+ "dash_analysis_title": "📊 Advanced Section Analysis & Financial Filtering",
387
+ "dash_analysis_desc": "Monitor financial flows grouped by specific cost sections across the entire company.",
388
+ "dash_filter_cat": "Select cost category to filter by:",
389
+ "dash_filter_type": "Filter project type:",
390
+ "dash_filter_all_cats": "Show all",
391
+ "dash_type_all": "All budgets",
392
+ "dash_type_fixed": "Binding fixed quotes only",
393
+ "dash_type_estimated": "Rough estimates only",
394
+ "dash_aggregated_spend": "💰 Aggregated spend in selected filter:",
395
+ "dash_no_records": "No records found matching the given criteria and filters.",
396
+ "dash_no_costs_yet": "No itemized cost entries are registered in the system yet.",
397
+ "dash_book_title": "Log of Created Calculations & Quotes",
398
+ "dash_book_empty": "The quotes logbook is currently empty.",
399
+
400
+ # Sloupce tabulek v Dashboardu
401
+ "col_project": "Project / Client",
402
+ "col_section": "Cost Section",
403
+ "col_item_name": "Item Name / Description",
404
+ "col_amount": "Amount",
405
+ "col_type": "Budget Type",
406
+ "col_date": "Date",
407
+
408
+ # AI Generátor Nabídek
409
+ "gen_type_label": "Select output scope and pricing strategy:",
410
+ "gen_type_est": "🔮 Rough AI Estimate (Purely indicative calculation, does not map into debtors ledger)",
411
+ "gen_type_fix": "🎯 Binding Fixed Quote (Strict financial plan, serves as an invoice baseline)",
412
+ "gen_client_section": "### 👤 Client Assignment",
413
+ "gen_client_mode": "Customer input method:",
414
+ "gen_client_crm": "Select from CRM address book",
415
+ "gen_client_manual": "✍️ Enter brand new client manually",
416
+ "gen_select_client": "Choose client:",
417
+ "gen_input_client_name": "Enter full name of the new client:",
418
+ "gen_client_placeholder": "E.g., John Smith",
419
+ "gen_spec_label": "Detailed project specifications and instructions for AI",
420
+ "gen_spec_default": "Complete residential house reconstruction, plumbing pipelines layout and final sanitary alignment.",
421
+ "gen_team_alloc": "### 🕒 Workforce Allocation (Team hours worked)",
422
+ "gen_no_team_warn": "ℹ️ There are no workers registered in the internal database yet. Add them in 'Team Management' or seed Demo Data via sidebar.",
423
+ "gen_ai_btn": "🪄 Execute AI Simulation and pre-fill rows & hours",
424
+ "gen_ai_toast": "AI successfully analyzed text and proposed optimal cost structure!",
425
+ "gen_direct_costs": "### 🚚 Direct Material & Secondary Expenses",
426
+ "gen_table_hint": "💡 Add new rows using the `+` icon at the bottom. Delete rows by selecting them and pressing `Delete` on your keyboard.",
427
+ "gen_csv_title": "### 📂 Bulk External Costs CSV Import",
428
+ "gen_csv_uploader": "Upload a .csv file (Required column layout: Sekce nákladu, Název položky / Popis, Částka)",
429
+ "gen_csv_success": "CSV data successfully mapped into the table above!",
430
+ "gen_csv_err_headers": "Error: CSV does not match the required headers layout.",
431
+ "gen_csv_err_read": "File cannot be parsed:",
432
+ "gen_total_calc": "### 📈 Total Budget Calculation:",
433
+ "gen_save_btn": "💾 Approve and record budget into the system",
434
+ "gen_err_no_client": "Error: You must state or select a client name before saving!",
435
+ "gen_save_success": "Budget was successfully recorded into company database.",
436
+
437
+ # Správa týmu
438
+ "team_title": "👥 Internal Team & Hourly Sazby Management",
439
+ "team_desc": "Edit profiles and corporate hourly rates of your specialists.",
440
+ "team_sub_list": "Registered Workers & Specialists",
441
+ "team_col_id": "Internal ID",
442
+ "team_col_role": "Member Name / Professional Role",
443
+ "team_col_rate": "Hourly Wage Rate",
444
+ "team_empty": "No workers inside the team database. Use the form on the right or turn on Demo Data.",
445
+ "team_remove_section": "**❌ Exclude worker from team**",
446
+ "team_remove_select": "Select person to remove:",
447
+ "team_remove_btn": "Permanently remove member",
448
+ "team_remove_toast": "Worker was removed from internal database.",
449
+ "team_add_section": "### ➕ Add / Modify Worker Card",
450
+ "team_input_name": "Full Name / Job Position",
451
+ "team_input_placeholder": "E.g., Carl (Mason)",
452
+ "team_input_rate": "Hourly rate in active currency",
453
+ "team_submit_btn": "Save Profile",
454
+ "team_update_toast": "Worker wage rate updated.",
455
+ "team_add_toast": "New member successfully registered.",
456
+
457
+ # CRM Adresář
458
+ "crm_title": "👥 Client Address Book (CRM)",
459
+ "crm_empty": "Client book is empty at the moment.",
460
+ "crm_add_section": "### ➕ Quick Manual Client Entry",
461
+ "cust_name": "Full Name",
462
+ "company": "Company (Optional)",
463
+ "phone": "Phone Number",
464
+ "save_cust": "Save Client",
465
+ "crm_err_name": "Client name is a mandatory field.",
466
+ "crm_success_toast": "Client records safely saved to CRM.",
467
+
468
+ # Fakturace
469
+ "inv_title": "🧾 1-Click Invoicing System",
470
+ "inv_select_quote": "Choose an approved fixed budget for immediate invoicing:",
471
+ "invoice_btn": "🧾 ISSUE 1-CLICK INVOICE NOW",
472
+ "inv_success_toast": "Invoice issued and stored into ledger successfully!",
473
+ "inv_no_fixed_quotes": "No approved fixed quotes available (Rough estimations cannot be converted into an invoice).",
474
+ "inv_book_title": "Complete Issued Invoices Overview & Payments Status",
475
+ "inv_status_paid": "🟢 Settled & Paid",
476
+ "inv_status_sent": "🔵 Sent (Overdue)",
477
+ "inv_mark_paid": "Mark as PAID / SETTLED",
478
+ "inv_empty": "The invoice ledger is empty.",
479
+
480
+ # PRO Funkce
481
  "paywall_msg": "🔒 You have reached the 3 free quotes limit. Unlock PRO tier for unlimited access.",
482
+ "pro_router": "📍 AI Route & Logistics Planner",
483
+ "pro_sms": "📲 Intelligent SMS Reminder Center",
484
+ "pro_locked_warn": "🔒 This panel contains advanced TradeFlow PRO modules. Activate evaluation license in Dev Admin Panel via sidebar.",
485
+ "pro_route_title": "📍 AI Geographical Route Optimization Engine",
486
+ "pro_route_label": "Target list of planned on-site addresses",
487
+ "pro_route_btn": "🗺️ Compute Mathematical Shortest Path",
488
+ "pro_route_result": "🚀 AI Optimization outcome: Departure Plzeň -> Beroun -> Praha. Total fuel and logistics distance saved: 42 km.",
489
+ "pro_sms_title": "📱 Smart Corporate Debt Collection SMS Center",
490
+ "pro_sms_step1": "### 🔍 1. Locating Delinquent Accounts in System",
491
+ "pro_sms_filter_name": "Filter debtors by name lookup:",
492
+ "pro_sms_sort_label": "Sort debtors by criteria order:",
493
+ "pro_sms_sort_high": "Highest outstanding debt first",
494
+ "pro_sms_sort_low": "Lowest outstanding debt first",
495
+ "pro_sms_no_debts": "No outstanding overdue invoices matching current filters discovered.",
496
+ "pro_sms_select_debtor": "🎯 Choose target account to contact:",
497
+ "pro_sms_step2": "### 📋 2. Template Preset Assignment",
498
+ "pro_sms_template_label": "Choose active communications preset:",
499
+ "pro_sms_expander": "⚙️ Advanced Presets Editor & AI Content Generator Engine",
500
+ "pro_sms_ai_assistant": "🤖 **AI Tone Modulation Assistant**",
501
+ "pro_sms_ai_prompt": "State assignment for AI (e.g., 'Write highly urgent pre-legal warning letter')",
502
+ "pro_sms_ai_btn": "🪄 Compose New Template via AI",
503
+ "pro_sms_ai_success": "AI copy successfully synthesized into the area below!",
504
+ "pro_sms_new_title": "New template label name:",
505
+ "pro_sms_new_body": "Message body code (use placeholder tags {jmeno}, {cislo}, {castka})",
506
+ "pro_sms_save_template_btn": "Save Preset into permanent storage",
507
+ "pro_sms_template_success": "Template preset recorded!",
508
+ "pro_sms_step3": "### 📱 3. Final Compiled Client Text Copy",
509
+ "pro_sms_copy_btn": "📋 Copy Message Text",
510
+ "pro_sms_copy_ok": "✅ Copy to clipboard successful!",
511
+ "pro_sms_select_info": "Select an overdue account to preview and extract SMS dispatch copy."
512
  }
513
  }
514
 
 
521
  }
522
 
523
  # -----------------------------------------------------------------------------
524
+ # INICIALIZACE STAVU
525
  # -----------------------------------------------------------------------------
526
+ VERSION = "2.1.2-ENTERPRISE"
527
 
528
  st.set_page_config(page_title="TradeFlow AI", page_icon="🏆", layout="wide", initial_sidebar_state="expanded")
529
 
 
533
  if "user_subscribed" not in st.session_state: st.session_state.user_subscribed = False
534
  if "quotes_count" not in st.session_state: st.session_state.quotes_count = 0
535
 
 
536
  if "db_team" not in st.session_state: st.session_state.db_team = []
537
  if "db_customers" not in st.session_state: st.session_state.db_customers = []
538
  if "db_quotes" not in st.session_state: st.session_state.db_quotes = []
 
545
  {"název": "Kamarádská připomínka", "text": "Ahoj {jmeno}, jen rychlá připomínka, že faktura {cislo} na částku {castka} už čeká na proplacení. Díky moc!"}
546
  ]
547
 
548
+ t = TRANSLATIONS[st.session_state.lang if st.session_state.lang in TRANSLATIONS else "CZ"]
549
+
550
  if "cost_items_df" not in st.session_state:
551
  st.session_state.cost_items_df = pd.DataFrame([
552
  {"Sekce nákladu": "Materiál", "Název položky / Popis": "", "Částka": 0.0}
 
559
  return f"{converted:,.2f} {c_info['symbol']}"
560
  return f"{c_info['symbol']}{converted:,.2f}"
561
 
 
 
562
  # -----------------------------------------------------------------------------
563
+ # SIDEBAR: PANEL S NASTAVENÍM & VÝVOJÁŘSKÝM PUSH SEEDINGEM
564
  # -----------------------------------------------------------------------------
565
  with st.sidebar:
566
+ st.header(t["sb_settings"])
567
+ st.session_state.lang = st.selectbox(t["sb_lang"], list(TRANSLATIONS.keys()), index=list(TRANSLATIONS.keys()).index(st.session_state.lang))
568
+
569
+ # Po změně jazyka musíme hned aktualizovat překladový slovník, aby zbytek kódu běžel v novém kontextu
570
+ t = TRANSLATIONS[st.session_state.lang]
571
+
572
+ st.session_state.curr = st.selectbox(t["sb_curr"], list(CURRENCIES.keys()), index=list(CURRENCIES.keys()).index(st.session_state.curr))
573
 
574
  st.markdown("---")
575
+ st.header(t["sb_dev_panel"])
576
 
577
  if not st.session_state.admin_authenticated:
578
+ pwd_input = st.text_input(t["sb_pwd_label"], type="password", help=t["sb_pwd_hint"])
579
+ if st.button(t["sb_unlock_btn"]):
580
  if pwd_input == "admin123":
581
  st.session_state.admin_authenticated = True
582
+ st.success(t["sb_auth_ok"])
583
  st.rerun()
584
  else:
585
+ st.error(t["sb_auth_err"])
586
  else:
587
+ st.info(t["sb_auth_status"])
588
+ st.session_state.user_subscribed = st.toggle(t["sb_pro_toggle"], value=st.session_state.user_subscribed)
589
+ st.session_state.quotes_count = st.number_input(t["sb_quotes_count"], min_value=0, max_value=20, value=st.session_state.quotes_count)
590
 
591
+ st.markdown(f"### {t['sb_demo_section']}")
592
+ if st.button(t["sb_import_demo"], use_container_width=True):
 
593
  st.session_state.db_team = [
594
  {"id": 1, "jmeno": "Šimon (Director)", "sazba": 45.0},
595
  {"id": 2, "jmeno": "Nikola (Prezentace & Podpora)", "sazba": 30.0},
 
622
  {"id": 4, "cislo": "20260004", "zakaznik": "Jan Novák", "castka": 5010.00, "stav": "Odesláno", "datum": "2026-06-15"}
623
  ]
624
  st.session_state.quotes_count = 1
625
+ st.toast(t["toast_demo_ok"], icon="📥")
626
  time.sleep(0.3)
627
  st.rerun()
628
 
629
+ if st.button(t["sb_clear_demo"], use_container_width=True):
630
  st.session_state.db_team = []
631
  st.session_state.db_customers = []
632
  st.session_state.db_quotes = []
633
  st.session_state.db_invoices = []
634
  st.session_state.quotes_count = 0
635
  st.session_state.cost_items_df = pd.DataFrame([{"Sekce nákladu": "Materiál", "Název položky / Popis": "", "Částka": 0.0}])
636
+ st.toast(t["toast_clear_ok"], icon="🗑️")
637
  time.sleep(0.3)
638
  st.rerun()
639
 
640
  st.markdown("---")
641
+ if st.button(t["sb_logout"], use_container_width=True):
642
  st.session_state.admin_authenticated = False
643
  st.rerun()
644
 
 
652
  ])
653
 
654
  # -----------------------------------------------------------------------------
655
+ # TAB 1: DASHBOARD & METRIKY
656
  # -----------------------------------------------------------------------------
657
  with tab_dashboard:
658
  st.header(t["tab_dash"])
 
663
  c1, c2, c3, c4 = st.columns(4)
664
  c1.metric(t["profit"], format_price(total_revenue))
665
  c2.metric(t["pending"], format_price(pending_revenue))
666
+ c3.metric(t["potential"], format_price(potencial), help=t["dash_help_potential"])
667
+ c4.metric(t["limit"], f"{st.session_state.quotes_count} / 3", delta=t["dash_limit_warn"] if is_locked else None)
668
 
669
  st.markdown("---")
670
+ st.subheader(t["dash_analysis_title"])
671
+ st.write(t["dash_analysis_desc"])
672
 
673
  flat_costs = []
674
  for q in st.session_state.db_quotes:
675
  if q.get("labor_eur", 0) > 0:
676
  flat_costs.append({
677
+ t["col_project"]: f"{q['zakaznik']} ({q['datum']})",
678
+ t["col_section"]: "Výplaty zaměstnancům" if st.session_state.lang != "EN" else "Employee Wages",
679
+ t["col_item_name"]: "Mzda týmu na projektu (odpracované hodiny)" if st.session_state.lang != "EN" else "Team payroll on project (hours worked)",
680
  "Částka (Základ v EUR)": q["labor_eur"],
681
+ t["col_type"]: "🎯 Přesná fixní" if q["typ"] == "fixni" else "🔮 Orientační odhad"
682
  })
683
  for ec in q.get("extra_costs", []):
684
  flat_costs.append({
685
+ t["col_project"]: f"{q['zakaznik']} ({q['datum']})",
686
+ t["col_section"]: ec["kategorie"],
687
+ t["col_item_name"]: ec["nazev"] if ec["nazev"] else "Přímý výdaj",
688
  "Částka (Základ v EUR)": ec["eur"],
689
+ t["col_type"]: "🎯 Přesná fixní" if q["typ"] == "fixni" else "🔮 Orientační odhad"
690
  })
691
 
692
  if flat_costs:
 
694
 
695
  f_col1, f_col2 = st.columns(2)
696
  with f_col1:
697
+ available_cats = [t["dash_filter_all_cats"]] + list(df_costs[t["col_section"]].unique())
698
+ selected_cat_filter = st.selectbox(t["dash_filter_cat"], available_cats)
699
  with f_col2:
700
+ selected_type_filter = st.radio(t["dash_filter_type"], [t["dash_type_all"], t["dash_type_fixed"], t["dash_type_estimated"]], horizontal=True)
701
 
702
+ if selected_cat_filter != t["dash_filter_all_cats"]:
703
+ df_costs = df_costs[df_costs[t["col_section"]] == selected_cat_filter]
704
 
705
+ if selected_type_filter == t["dash_type_fixed"]:
706
+ df_costs = df_costs[df_costs[t["col_type"]] == "🎯 Přesná fixní"]
707
+ elif selected_type_filter == t["dash_type_estimated"]:
708
+ df_costs = df_costs[df_costs[t["col_type"]] == "🔮 Orientační odhad"]
709
 
710
  if not df_costs.empty:
711
  total_filtered_eur = df_costs["Částka (Základ v EUR)"].sum()
712
+ st.markdown(f"#### {t['dash_aggregated_spend']} **{format_price(total_filtered_eur)}**")
713
 
714
  df_costs_display = df_costs.copy()
715
+ df_costs_display[t["col_amount"]] = df_costs_display["Částka (Základ v EUR)"].apply(format_price)
716
  df_costs_display = df_costs_display.drop(columns=["Částka (Základ v EUR)"])
717
  st.dataframe(df_costs_display, use_container_width=True, hide_index=True)
718
  else:
719
+ st.info(t["dash_no_records"])
720
  else:
721
+ st.info(t["dash_no_costs_yet"])
722
 
723
  st.markdown("---")
724
+ st.subheader(t["dash_book_title"])
725
  if st.session_state.db_quotes:
726
  df_raw = pd.DataFrame(st.session_state.db_quotes)
727
  df_display = df_raw.copy()
728
+
729
+ # Přejmenování klíčů a lokalizace pro tabulku
730
+ df_display[t["col_date"]] = df_display["datum"]
731
+ df_display[t["col_project"]] = df_display["zakaznik"]
732
+ df_display[t["col_item_name"]] = df_display["popis"]
733
+ df_display[t["col_amount"]] = df_display["celkem"].apply(format_price)
734
+ df_display[t["col_type"]] = df_display["typ"].map({"odhad": "🔮 " + t["dash_type_estimated"], "fixni": "🎯 " + t["dash_type_fixed"]})
735
+
736
+ cols_to_show = [t["col_date"], t["col_project"], t["col_type"], t["col_item_name"], t["col_amount"]]
737
+ st.dataframe(df_display[cols_to_show], use_container_width=True, hide_index=True)
738
  else:
739
+ st.info(t["dash_book_empty"])
740
 
741
  # -----------------------------------------------------------------------------
742
+ # TAB 2: AI GENERÁTOR NABÍDEK (OPRAVENÝ & S PŘELOŽENÝM SOUBOREM NÁKLADŮ)
743
  # -----------------------------------------------------------------------------
744
  with tab_generator:
745
  st.header(t["tab_gen"])
746
  if is_locked:
747
  st.error(t["paywall_msg"])
748
  else:
749
+ quote_type = st.radio(t["gen_type_label"], [t["gen_type_est"], t["gen_type_fix"]])
750
+ is_fixed = t["gen_type_fix"] in quote_type
 
 
 
 
751
 
752
+ st.markdown(t["gen_client_section"])
753
  cust_names = [c["jmeno"] for c in st.session_state.db_customers]
754
 
755
+ client_mode = st.radio(t["gen_client_mode"], [t["gen_client_crm"], t["gen_client_manual"]], horizontal=True)
756
+ if client_mode == t["gen_client_crm"] and cust_names:
757
+ selected_cust = st.selectbox(t["gen_select_client"], cust_names)
758
  else:
759
+ selected_cust = st.text_input(t["gen_input_client_name"], value="", placeholder=t["gen_client_placeholder"])
760
 
761
+ input_task = st.text_area(t["gen_spec_label"], value=t["gen_spec_default"])
762
 
763
+ st.markdown(t["gen_team_alloc"])
764
 
765
  if not st.session_state.db_team:
766
+ st.info(t["gen_no_team_warn"])
767
  team_hours = {}
768
  else:
769
+ if st.button(t["gen_ai_btn"]):
770
  st.session_state["ai_mock_active"] = True
771
  st.session_state.cost_items_df = pd.DataFrame([
772
  {"Sekce nákladu": "Materiál", "Název položky / Popis": "AI Odhad: Potrubí, ventily a fitinky", "Částka": 480.0},
773
+ {"Sekce nákladu": "Benzín / Doprava", "Název položky / Popis": "AI Odhad: Nákladní závoz materiálu", "Částka": 65.0},
774
  {"Sekce nákladu": "Pronájem vybavení", "Název položky / Popis": "AI Odhad: Zapůjčení tlakové zkoušečky", "Částka": 110.0}
775
  ])
776
+ st.toast(t["gen_ai_toast"], icon="🪄")
777
 
778
  team_hours = {}
779
  cols_team = st.columns(len(st.session_state.db_team))
 
792
  )
793
  team_hours[member["id"]] = hours_input
794
 
795
+ st.markdown(t["gen_direct_costs"])
796
+ st.caption(t["gen_table_hint"])
797
 
 
798
  curr_rate = CURRENCIES[st.session_state.curr]["rate"]
799
 
800
+ # Úprava konfigurace tabulky - odstraněna měna ze záhlaví sloupce (pouze t["col_amount"])
801
  column_configuration = {
802
  "Sekce nákladu": st.column_config.SelectboxColumn(
803
+ t["col_section"],
804
+ options=["Materiál", "Benzín / Doprava", "Pronájem vybavení", "Ostatní režie"] if st.session_state.lang != "EN" else ["Material", "Logistics / Fuel", "Equipment Rental", "Other Overhead"],
805
  required=True
806
  ),
807
+ "Název položky / Popis": st.column_config.TextColumn(t["col_item_name"], required=True),
808
+ "Částka": st.column_config.NumberColumn(t["col_amount"], min_value=0.0, required=True, format="%.2f")
809
  }
810
 
811
  edited_df = st.data_editor(
 
817
  )
818
  st.session_state.cost_items_df = edited_df
819
 
820
+ st.markdown(t["gen_csv_title"])
 
821
 
822
  def zpracuj_csv_import():
823
  if st.session_state.csv_uploader_key is not None:
 
828
  sanitized_df = uploaded_df[needed_headers].copy()
829
  sanitized_df["Částka"] = pd.to_numeric(sanitized_df["Částka"], errors='coerce').fillna(0.0)
830
 
 
831
  st.session_state.cost_items_df = pd.concat([st.session_state.cost_items_df, sanitized_df], ignore_index=True)
832
+ st.toast(t["gen_csv_success"], icon="📥")
833
  else:
834
+ st.error(t["gen_csv_err_headers"])
835
  except Exception as e:
836
+ st.error(f"{t['gen_csv_err_read']} {e}")
837
 
838
  st.file_uploader(
839
+ t["gen_csv_uploader"],
840
  type=["csv"],
841
  key="csv_uploader_key",
842
+ on_change=zprocessed_csv_import if 'zprocessed_csv_import' in locals() else zpracuj_csv_import
843
  )
 
844
 
845
  st.markdown("---")
846
 
 
852
  total_extra_eur = total_extra_local / curr_rate
853
  total_project_eur = total_labor_eur + total_extra_eur
854
 
855
+ st.markdown(f"{t['gen_total_calc']} **{format_price(total_project_eur)}**")
856
 
857
+ if st.button(t["gen_save_btn"], type="primary"):
858
  if not selected_cust or selected_cust.strip() == "":
859
+ st.error(t["gen_err_no_client"])
860
  else:
861
  if selected_cust not in cust_names:
862
  st.session_state.db_customers.append({
863
  "id": len(st.session_state.db_customers) + 1,
864
  "jmeno": selected_cust.strip(),
865
+ "firma": "Fyzická osoba" if st.session_state.lang != "EN" else "Individual Client",
866
+ "telefon": "Nezadáno" if st.session_state.lang != "EN" else "N/A"
867
  })
868
 
869
  saved_extra_costs = []
 
890
  st.session_state["ai_mock_active"] = False
891
 
892
  st.session_state.cost_items_df = pd.DataFrame([{"Sekce nákladu": "Materiál", "Název položky / Popis": "", "Částka": 0.0}])
893
+ st.success(t["gen_save_success"])
894
  time.sleep(0.4)
895
  st.rerun()
896
 
 
898
  # TAB 3: SPRÁVA TÝMU
899
  # -----------------------------------------------------------------------------
900
  with tab_team:
901
+ st.header(t["team_title"])
902
+ st.write(t["team_desc"])
903
 
904
  t_col1, t_col2 = st.columns([2, 1])
905
 
906
  with t_col1:
907
+ st.markdown(f"### {t['team_sub_list']}")
908
  team_display_list = []
909
  for m in st.session_state.db_team:
910
  team_display_list.append({
911
+ t["team_col_id"]: m["id"],
912
+ t["team_col_role"]: m["jmeno"],
913
+ t["team_col_rate"]: format_price(m["sazba"])
914
  })
915
  if team_display_list:
916
  st.dataframe(pd.DataFrame(team_display_list), use_container_width=True, hide_index=True)
917
  else:
918
+ st.info(t["team_empty"])
919
 
920
+ st.markdown(t["team_remove_section"])
921
  if st.session_state.db_team:
922
+ member_to_remove = st.selectbox(t["team_remove_select"], [m["jmeno"] for m in st.session_state.db_team])
923
+ if st.button(t["team_remove_btn"]):
924
  st.session_state.db_team = [m for m in st.session_state.db_team if m["jmeno"] != member_to_remove]
925
+ st.success(f"{t['team_remove_toast']} ({member_to_remove})")
926
  time.sleep(0.3)
927
  st.rerun()
928
 
929
  with t_col2:
930
+ st.markdown(t["team_add_section"])
931
  with st.form("team_management_form"):
932
+ new_tm_name = st.text_input(t["team_input_name"], placeholder=t["team_input_placeholder"])
933
+ new_tm_rate = st.number_input(t["team_input_rate"], min_value=1, value=400 if st.session_state.curr == "CZK" else 20)
 
934
 
935
+ if st.form_submit_button(t["team_submit_btn"]):
936
  if new_tm_name:
937
  rate_in_eur = new_tm_rate / CURRENCIES[st.session_state.curr]["rate"]
938
  matched_member = next((m for m in st.session_state.db_team if m["jmeno"].lower() == new_tm_name.lower()), None)
939
  if matched_member:
940
  matched_member["sazba"] = rate_in_eur
941
+ st.success(t["team_update_toast"])
942
  else:
943
  next_id = max([m["id"] for m in st.session_state.db_team]) + 1 if st.session_state.db_team else 1
944
  st.session_state.db_team.append({"id": next_id, "jmeno": new_tm_name, "sazba": rate_in_eur})
945
+ st.success(t["team_add_toast"])
946
  time.sleep(0.3)
947
  st.rerun()
948
 
 
950
  # TAB 4: CRM ADRESÁŘ
951
  # -----------------------------------------------------------------------------
952
  with tab_crm:
953
+ st.header(t["crm_title"])
954
  col_crm1, col_crm2 = st.columns([2, 1])
955
  with col_crm1:
956
  if st.session_state.db_customers:
957
+ df_crm = pd.DataFrame(st.session_state.db_customers)
958
+ df_crm_display = pd.DataFrame({
959
+ t["cust_name"]: df_crm["jmeno"],
960
+ t["company"]: df_crm["firma"],
961
+ t["phone"]: df_crm["telefon"]
962
+ })
963
+ st.dataframe(df_crm_display, use_container_width=True, hide_index=True)
964
  else:
965
+ st.info(t["crm_empty"])
966
  with col_crm2:
967
+ st.markdown(t["crm_add_section"])
968
  with st.form("manual_crm_form"):
969
  c_n = st.text_input(t["cust_name"], placeholder="Jan Novák")
970
  c_f = st.text_input(t["company"], placeholder="Stavebniny s.r.o.")
 
974
  st.session_state.db_customers.append({
975
  "id": len(st.session_state.db_customers) + 1,
976
  "jmeno": c_n,
977
+ "firma": c_f if c_f else ("Fyzická osoba" if st.session_state.lang != "EN" else "Individual"),
978
  "telefon": c_p
979
  })
980
+ st.success(t["crm_success_toast"])
981
+ time.sleep(0.3)
982
+ st.rerun()
983
  else:
984
+ st.error(t["crm_err_name"])
985
 
986
  # -----------------------------------------------------------------------------
987
  # TAB 5: FAKTURACE
988
  # -----------------------------------------------------------------------------
989
  with tab_fakturace:
990
+ st.header(t["inv_title"])
991
  only_fixni_quotes = [q for q in st.session_state.db_quotes if q.get("typ") == "fixni"]
992
 
993
  if only_fixni_quotes:
994
+ invoice_options = {f"ID: {q['id']} | {q['zakaznik']} — {format_price(q['celkem'])}": q for q in only_fixni_quotes}
995
+ selected_quote_to_inv = st.selectbox(t["inv_select_quote"], list(invoice_options.keys()))
996
  targeted_quote = invoice_options[selected_quote_to_inv]
997
 
998
  if st.button(t["invoice_btn"], type="primary", use_container_width=True):
 
1004
  "stav": "Odesláno",
1005
  "datum": time.strftime("%Y-%m-%d")
1006
  })
1007
+ st.success(t["inv_success_toast"])
1008
  time.sleep(0.3)
1009
  st.rerun()
1010
  else:
1011
+ st.info(t["inv_no_fixed_quotes"])
1012
 
1013
  st.markdown("---")
1014
+ st.subheader(t["inv_book_title"])
1015
 
1016
  if st.session_state.db_invoices:
1017
  for index_inv, invoice_obj in enumerate(st.session_state.db_invoices):
1018
  div_c1, div_c2, div_c3 = st.columns([2, 1, 1])
1019
+ div_c1.write(f"🧾 **{t['col_amount']} č. {invoice_obj['cislo']}** — {invoice_obj['zakaznik']} [**{format_price(invoice_obj['castka'])}**]")
1020
 
1021
  if invoice_obj["stav"] == "Zaplaceno":
1022
+ div_c2.markdown(f"<span style='color:green; font-weight:bold;'>{t['inv_status_paid']}</span>", unsafe_allow_html=True)
1023
  div_c3.write("")
1024
  else:
1025
+ div_c2.markdown(f"<span style='color:orange; font-weight:bold;'>{t['inv_status_sent']}</span>", unsafe_allow_html=True)
1026
+ if div_c3.button(t["inv_mark_paid"], key=f"payment_trigger_key_{index_inv}", use_container_width=True):
1027
  invoice_obj["stav"] = "Zaplaceno"
1028
  st.rerun()
1029
  else:
1030
+ st.info(t["inv_empty"])
1031
 
1032
  # -----------------------------------------------------------------------------
1033
  # TAB 6: PRO BONUSY
 
1035
  with tab_pro_features:
1036
  st.header(t["tab_pro"])
1037
  if not st.session_state.user_subscribed:
1038
+ st.warning(t["pro_locked_warn"])
1039
  else:
1040
  pro_sub1, pro_sub2 = st.tabs([t["pro_router"], t["pro_sms"]])
1041
 
1042
  with pro_sub1:
1043
+ st.subheader(t["pro_route_title"])
1044
+ st.text_area(t["pro_route_label"], value="Plzeňská 15, Praha\nPrůmyslová 4, Beroun\nSukova 12, Plzeň")
1045
+ if st.button(t["pro_route_btn"], use_container_width=True):
1046
+ st.info(t["pro_route_result"])
1047
 
1048
  with pro_sub2:
1049
+ st.subheader(t["pro_sms_title"])
1050
+ st.markdown(t["pro_sms_step1"])
 
1051
  open_debts = [invoice for invoice in st.session_state.db_invoices if invoice["stav"] == "Odesláno"]
1052
 
1053
  search_col1, search_col2 = st.columns([1, 1])
1054
  with search_col1:
1055
+ query_name = st.text_input(t["pro_sms_filter_name"], value="")
1056
  with search_col2:
1057
+ sorting_order = st.selectbox(t["pro_sms_sort_label"], [t["pro_sms_sort_high"], t["pro_sms_sort_low"]])
1058
 
1059
  if query_name:
1060
  open_debts = [d for d in open_debts if query_name.lower() in d["zakaznik"].lower()]
1061
 
1062
+ if sorting_order == t["pro_sms_sort_high"]:
1063
  open_debts.sort(key=lambda x: x["castka"], reverse=True)
1064
  else:
1065
  open_debts.sort(key=lambda x: x["castka"])
1066
 
1067
  if not open_debts:
1068
+ st.info(t["pro_sms_no_debts"])
1069
  active_debtor = None
1070
  else:
1071
  debtor_mapping = {f"👤 {d['zakaznik']} (Faktura č. {d['cislo']} | Dluh: {format_price(d['castka'])})": d for d in open_debts}
1072
+ selected_debtor_string = st.selectbox(t["pro_sms_select_debtor"], list(debtor_mapping.keys()))
1073
  active_debtor = debtor_mapping[selected_debtor_string]
1074
 
1075
  st.markdown("---")
1076
+ st.markdown(t["pro_sms_step2"])
1077
 
1078
  preset_labels = [p["název"] for p in st.session_state.sms_presets]
1079
+ chosen_preset_label = st.selectbox(t["pro_sms_template_label"], preset_labels)
1080
  active_preset = next(p for p in st.session_state.sms_presets if p["název"] == chosen_preset_label)
1081
 
1082
+ with st.expander(t["pro_sms_expander"]):
1083
+ st.markdown(t["pro_sms_ai_assistant"])
1084
+ prompt_text = st.text_input(t["pro_sms_ai_prompt"], placeholder="...")
1085
 
1086
+ if st.button(t["pro_sms_ai_btn"]):
1087
  if prompt_text:
1088
  p_low = prompt_text.lower()
1089
  if "předžal" in p_low or "naléh" in p_low or "soud" in p_low:
1090
+ result_sms = "PŘEDŽALOBNÍ VÝZVA: Vážený kliente {jmeno}. Upozorňujeme Vás, že faktura č. {cislo} na částku {castka} je dlouhodobě po splatnosti. Žádáme o okamžitou úhradu."
1091
  else:
1092
+ result_sms = "Dobrý den {jmeno}, připomínáme zapomenutou fakturu č. {cislo} na částku {castka}. Prosíme o co nejrychlejší proplacení."
1093
  st.session_state["ai_sms_output_buffer"] = result_sms
1094
+ st.success(t["pro_sms_ai_success"])
1095
 
1096
+ new_preset_title = st.text_input(t["pro_sms_new_title"], value="")
1097
  default_body = st.session_state.get("ai_sms_output_buffer", "Vážený pane/paní {jmeno}, ...")
1098
+ new_preset_body = st.text_area(t["pro_sms_new_body"], value=default_body)
1099
 
1100
+ if st.button(t["pro_sms_save_template_btn"]):
1101
  if new_preset_title and new_preset_body:
1102
  st.session_state.sms_presets.append({"název": new_preset_title, "text": new_preset_body})
1103
+ st.success(t["pro_sms_template_success"])
1104
  time.sleep(0.3)
1105
  st.rerun()
1106
 
1107
  st.markdown("---")
1108
+ st.markdown(t["pro_sms_step3"])
1109
 
1110
  if active_debtor:
1111
  raw_template = active_preset["text"]
 
1119
  border: 1px solid #dee2e6;
1120
  padding: 18px;
1121
  border-radius: 8px;
1122
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
1123
  font-size: 15px;
1124
  color: #212529;
1125
  white-space: pre-wrap;
1126
  word-wrap: break-word;
1127
  line-height: 1.6;
1128
  margin-bottom: 12px;
1129
+ ;">{final_compiled_sms}</div>
1130
  """, unsafe_allow_html=True)
1131
 
1132
  safe_encoded_sms = urllib.parse.quote(final_compiled_sms)
 
1135
  <button onclick="
1136
  navigator.clipboard.writeText(decodeURIComponent('{safe_encoded_sms}'));
1137
  let btn = document.getElementById('js_copy_btn');
1138
+ btn.innerText = '{t['pro_sms_copy_ok']}';
1139
  btn.style.backgroundColor = '#28a745';
1140
+ setTimeout(() => {{ btn.innerText = '{t['pro_sms_copy_btn']}'; btn.style.backgroundColor = '#007bff'; }}, 2000);
1141
  " id="js_copy_btn" style="
1142
  background-color: #007bff;
1143
  color: white;
 
1146
  font-size: 14px;
1147
  border-radius: 6px;
1148
  cursor: pointer;
 
1149
  font-weight: bold;
1150
  width: 100%;
1151
+ ">{t['pro_sms_copy_btn']}</button>
 
 
1152
  """
1153
  components.html(copy_button_html, height=50)
1154
  else:
1155
+ st.info(t["pro_sms_select_info"])