simikkk commited on
Commit
fdf49ce
·
verified ·
1 Parent(s): 90ff4be

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +59 -66
app.py CHANGED
@@ -18,7 +18,7 @@ TRANSLATIONS = {
18
  },
19
  "SK": {
20
  "title": "🏆 TradeFlow AI", "caption": "Chytry operačný systém pre novú generáciu remeselníkov.",
21
- "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",
22
  "profit": "Skutočný zisk", "pending": "Čakajúce peniaze", "potential": "Otvorené AI Ponuky", "limit": "Využitie Free limitu",
23
  "cust_name": "Meno a priezvisko", "company": "Firma (Voliteľné)", "phone": "Telefónne číslo", "save_cust": "Uložiť klienta",
24
  "generate_btn": "⚡ Vygenerovať/Uložiť rozpočet", "invoice_btn": "🧾 VYSTAVIŤ FAKTÚRU JEDNÝM KLIKNUTÍM",
@@ -47,7 +47,7 @@ CURRENCIES = {
47
  # -----------------------------------------------------------------------------
48
  # INICIALIZACE STAVU (ČISTÉ PRODUKČNÍ PROSTŘEDÍ)
49
  # -----------------------------------------------------------------------------
50
- VERSION = "2.1.0-ENTERPRISE"
51
 
52
  st.set_page_config(page_title="TradeFlow AI", page_icon="🏆", layout="wide", initial_sidebar_state="expanded")
53
 
@@ -57,15 +57,8 @@ if "admin_authenticated" not in st.session_state: st.session_state.admin_authent
57
  if "user_subscribed" not in st.session_state: st.session_state.user_subscribed = False
58
  if "quotes_count" not in st.session_state: st.session_state.quotes_count = 0
59
 
60
- # Výchozí fixní struktura týmu (hodnoty uloženy v základní měně EUR)
61
- if "db_team" not in st.session_state:
62
- st.session_state.db_team = [
63
- {"id": 1, "jmeno": "Šimon (Director)", "sazba": 45.0},
64
- {"id": 2, "jmeno": "Nikola (Prezentace & Podpora)", "sazba": 30.0},
65
- {"id": 3, "jmeno": "Matěj (Technik)", "sazba": 25.0}
66
- ]
67
-
68
- # Ostré databáze začínají prázdné
69
  if "db_customers" not in st.session_state: st.session_state.db_customers = []
70
  if "db_quotes" not in st.session_state: st.session_state.db_quotes = []
71
  if "db_invoices" not in st.session_state: st.session_state.db_invoices = []
@@ -77,10 +70,10 @@ if "sms_presets" not in st.session_state:
77
  {"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!"}
78
  ]
79
 
80
- # Nativní DataFrame pro bezrefreshový editor přímých nákladů v Generátoru
81
  if "cost_items_df" not in st.session_state:
82
  st.session_state.cost_items_df = pd.DataFrame([
83
- {"Sekce nákladu": "Materiál", "Název položky / Popis": "Spojovací materiál & Sanitární trubky", "Částka": 250.0}
84
  ])
85
 
86
  def format_price(amount_in_eur):
@@ -119,6 +112,12 @@ with st.sidebar:
119
 
120
  st.markdown("### 🧪 Správa ukázkových dat")
121
  if st.button("📥 Importovat komplexní Demo Data", use_container_width=True):
 
 
 
 
 
 
122
  st.session_state.db_customers = [
123
  {"id": 1, "jmeno": "Jan Novák", "firma": "Stavebniny s.r.o.", "telefon": "+420 777 123 456"},
124
  {"id": 2, "jmeno": "Marie Podlahová", "firma": "Bytové družstvo", "telefon": "+420 602 987 654"},
@@ -151,6 +150,7 @@ with st.sidebar:
151
  st.rerun()
152
 
153
  if st.button("🗑️ Kompletní vyčištění databází", use_container_width=True):
 
154
  st.session_state.db_customers = []
155
  st.session_state.db_quotes = []
156
  st.session_state.db_invoices = []
@@ -193,7 +193,6 @@ with tab_dashboard:
193
  st.subheader("📊 Pokročilá analýza a finanční filtrování sekcí")
194
  st.write("Sledujte finanční toky seskupené podle konkrétních nákladových sekcí napříč celou firmou.")
195
 
196
- # Rekonstrukce ploché tabulky nákladů z databáze uložených nabídek
197
  flat_costs = []
198
  for q in st.session_state.db_quotes:
199
  if q.get("labor_eur", 0) > 0:
@@ -256,7 +255,7 @@ with tab_dashboard:
256
  st.info("Kniha nabídek je prázdná.")
257
 
258
  # -----------------------------------------------------------------------------
259
- # TAB 2: AI GENERÁTOR (DYNAMICKÝ EDITOR BEZ REFRESHŮ + CSV IMPORT + VOLNÝ TEXT KLIENTA)
260
  # -----------------------------------------------------------------------------
261
  with tab_generator:
262
  st.header(t["tab_gen"])
@@ -283,50 +282,53 @@ with tab_generator:
283
 
284
  st.markdown("### 🕒 Personální alokace (Odpracované hodiny týmu)")
285
 
286
- if st.button("🪄 Spustit AI simulaci a předvyplnit položky i hodiny"):
287
- st.session_state["ai_mock_active"] = True
288
- st.session_state.cost_items_df = pd.DataFrame([
289
- {"Sekce nákladu": "Materiál", "Název položky / Popis": "AI Odhad: Potrubí, ventily a fitinky", "Částka": 480.0},
290
- {"Sekce nákladu": "Benzín / Doprava", "Název položky / Popis": "AI Odhad: Nákladní závoz materiálu z velkoobchodu", "Částka": 65.0},
291
- {"Sekce nákladu": "Pronájem vybavení", "Název položky / Popis": "AI Odhad: Zapůjčení tlakové zkoušečky", "Částka": 110.0}
292
- ])
293
- st.toast("AI úspěšně zanalyzovala text a navrhla optimální strukturu nákladů!", icon="🪄")
294
-
295
- team_hours = {}
296
- cols_team = st.columns(max(len(st.session_state.db_team), 1))
297
-
298
- for idx, member in enumerate(st.session_state.db_team):
299
- with cols_team[idx]:
300
- default_val = 0
301
- if st.session_state.get("ai_mock_active", False):
302
- if "director" in member["jmeno"].lower(): default_val = 10
303
- elif "prezentace" in member["jmeno"].lower(): default_val = 6
304
- else: default_val = 24
305
-
306
- hours_input = st.number_input(
307
- f"{member['jmeno']} ({format_price(member['sazba'])}/h)",
308
- min_value=0, max_value=500, value=default_val, key=f"gen_hours_{member['id']}"
309
- )
310
- team_hours[member["id"]] = hours_input
311
-
312
- # 🌟 VYŘEŠENÍ REFRESHŮ: DYNAMICKÁ TABULKA S INTERAKTIVNÍM EDITORŮ BEZ RELOADŮ
 
 
 
313
  st.markdown("### 🚚 Přímé materiálové a vedlejší náklady")
314
- st.caption("💡 Nové řádky přidáte symbolem `+` vespod tabulky. Řádek smažete kliknutím na jeho okraj a stiskem klávesy `Delete` na klávesnici. Změny se aplikují bez překreslování stránky.")
315
 
316
  curr_sym = CURRENCIES[st.session_state.curr]["symbol"]
317
  curr_rate = CURRENCIES[st.session_state.curr]["rate"]
318
 
 
319
  column_configuration = {
320
  "Sekce nákladu": st.column_config.SelectboxColumn(
321
  "Sekce nákladu",
322
  options=["Materiál", "Benzín / Doprava", "Pronájem vybavení", "Ostatní režie"],
323
  required=True
324
  ),
325
- "Název položky / Popis": st.column_config.TextColumn("Název položky / Popis", placeholder="Např. Sádrokartonová deska", required=True),
326
  "Částka": st.column_config.NumberColumn(f"Částka ({curr_sym})", min_value=0.0, required=True, format="%.2f")
327
  }
328
 
329
- # Zobrazení nativního editoru
330
  edited_df = st.data_editor(
331
  st.session_state.cost_items_df,
332
  column_config=column_configuration,
@@ -336,9 +338,8 @@ with tab_generator:
336
  )
337
  st.session_state.cost_items_df = edited_df
338
 
339
- # 🌟 ROZŠÍŘENÍ: HROMADNÝ IMPORT CSV TABULKY S AUTOMATICKÝM ROZTŘÍDĚNÍM
340
  st.markdown("### 📂 Hromadný import externích nákladů přes CSV")
341
- csv_file = st.file_uploader("Nahrajte soubor .csv (Struktura sloupců musí přesně být: Sekce nákladu, Název položky / Popis, Částka)", type=["csv"])
342
  if csv_file is not None:
343
  try:
344
  uploaded_df = pd.read_csv(csv_file)
@@ -347,20 +348,19 @@ with tab_generator:
347
  sanitized_df = uploaded_df[needed_headers].copy()
348
  sanitized_df["Částka"] = pd.to_numeric(sanitized_df["Částka"], errors='coerce').fillna(0.0)
349
  st.session_state.cost_items_df = pd.concat([st.session_state.cost_items_df, sanitized_df], ignore_index=True)
350
- st.success("Externí CSV data byla úspěšně zpracována a integrována do tabulky výše!")
351
  time.sleep(0.4)
352
  st.rerun()
353
  else:
354
- st.error("Chyba validace: CSV neodpovídá požadované struktuře záhlaví.")
355
  except Exception as e:
356
- st.error(f"Soubor se nepodařilo přečíst: {e}")
357
 
358
  st.markdown("---")
359
 
360
- # Finální sumace z aktuálního stavu editoru a personálních polí
361
  total_labor_eur = 0.0
362
  for member in st.session_state.db_team:
363
- total_labor_eur += team_hours[member["id"]] * member["sazba"]
364
 
365
  total_extra_local = edited_df["Částka"].sum() if not edited_df.empty else 0.0
366
  total_extra_eur = total_extra_local / curr_rate
@@ -372,7 +372,6 @@ with tab_generator:
372
  if not selected_cust or selected_cust.strip() == "":
373
  st.error("Chyba: Před uložením musíte uvést nebo vybrat jméno klienta!")
374
  else:
375
- # Automatické zanesení klienta do CRM, pokud jde o nový textový záznam
376
  if selected_cust not in cust_names:
377
  st.session_state.db_customers.append({
378
  "id": len(st.session_state.db_customers) + 1,
@@ -381,7 +380,6 @@ with tab_generator:
381
  "telefon": "Nezadáno"
382
  })
383
 
384
- # Transformace dat z editoru do čisté databáze v EUR
385
  saved_extra_costs = []
386
  for _, row in edited_df.iterrows():
387
  saved_extra_costs.append({
@@ -405,7 +403,6 @@ with tab_generator:
405
  st.session_state.quotes_count += 1
406
  st.session_state["ai_mock_active"] = False
407
 
408
- # Vyčištění tabulky na výchozí prázdný stav
409
  st.session_state.cost_items_df = pd.DataFrame([{"Sekce nákladu": "Materiál", "Název položky / Popis": "", "Částka": 0.0}])
410
  st.success("Rozpočet byl úspěšně zanesen do firemní databáze.")
411
  time.sleep(0.4)
@@ -416,7 +413,7 @@ with tab_generator:
416
  # -----------------------------------------------------------------------------
417
  with tab_team:
418
  st.header("👥 Správa firemního týmu a hodinových sazeb")
419
- st.write("Editujte profily a interní hodinové sazby svých pracovníků. Změny se okamžitě projevují v kalkulačním modulu.")
420
 
421
  t_col1, t_col2 = st.columns([2, 1])
422
 
@@ -432,7 +429,7 @@ with tab_team:
432
  if team_display_list:
433
  st.dataframe(pd.DataFrame(team_display_list), use_container_width=True, hide_index=True)
434
  else:
435
- st.warning("V týmu se nenachází žádný pracovník.")
436
 
437
  st.markdown("**❌ Odstranění pracovníka z týmu**")
438
  if st.session_state.db_team:
@@ -494,7 +491,7 @@ with tab_crm:
494
  st.error("Jméno klienta je povinné pole.")
495
 
496
  # -----------------------------------------------------------------------------
497
- # TAB 5: FAKTURACE (ZÁVAZNÉ POSTUPY PRO FIXNÍ NABÍDKY)
498
  # -----------------------------------------------------------------------------
499
  with tab_fakturace:
500
  st.header(t["tab_inv"])
@@ -514,11 +511,11 @@ with tab_fakturace:
514
  "stav": "Odesláno",
515
  "datum": time.strftime("%Y-%m-%d")
516
  })
517
- st.success("Faktura byla vystavena, uložena do knihy a odeslána zákazníkovi!")
518
  time.sleep(0.3)
519
  st.rerun()
520
  else:
521
- st.info("V systému se nenachází žádná schválená pevná nabídka (Orientační odhady nelze legálně fakturovat).")
522
 
523
  st.markdown("---")
524
  st.subheader("Kompletní přehled vydaných faktur a stavu plateb")
@@ -540,7 +537,7 @@ with tab_fakturace:
540
  st.info("Kniha faktur je prázdná.")
541
 
542
  # -----------------------------------------------------------------------------
543
- # TAB 6: PRO BONUSY (AUTOMATIZOVANÉ SMS CENTRUM S JAVASCRIPT KOPÍROVÁNÍM BEZ REFRESHŮ)
544
  # -----------------------------------------------------------------------------
545
  with tab_pro_features:
546
  st.header(t["tab_pro"])
@@ -619,13 +616,11 @@ with tab_pro_features:
619
  st.markdown("### 📱 3. Finální vygenerovaná zpráva pro klienta")
620
 
621
  if active_debtor:
622
- # Sestavení finální textace
623
  raw_template = active_preset["text"]
624
  final_compiled_sms = raw_template.replace("{jmeno}", active_debtor["zakaznik"])\
625
  .replace("{cislo}", active_debtor["cislo"])\
626
  .replace("{castka}", format_price(active_debtor["castka"]))
627
 
628
- # Výpis textu v přesném vizuálním formátu dle image_dca906.png
629
  st.markdown(f"""
630
  <div style="
631
  background-color: #f8f9fa;
@@ -639,13 +634,11 @@ with tab_pro_features:
639
  word-wrap: break-word;
640
  line-height: 1.6;
641
  margin-bottom: 12px;
642
- " id="sms_display_box">{final_compiled_sms}</div>
643
  """, unsafe_allow_html=True)
644
 
645
- # Bezpečné zakódování řetězce pro předání do JavaScriptu bez syntax errorů způsobených odřádkováním
646
  safe_encoded_sms = urllib.parse.quote(final_compiled_sms)
647
 
648
- # 🌟 KOPÍROVACÍ TLAČÍTKO POMOCÍ JAVASCRIPTU - ZKOPÍRUJE TEXT OKAMŽITĚ BEZ REFRESHNUTÍ STRÁNKY
649
  copy_button_html = f"""
650
  <button onclick="
651
  navigator.clipboard.writeText(decodeURIComponent('{safe_encoded_sms}'));
 
18
  },
19
  "SK": {
20
  "title": "🏆 TradeFlow AI", "caption": "Chytry operačný systém pre novú generáciu remeselníkov.",
21
+ "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",
22
  "profit": "Skutočný zisk", "pending": "Čakajúce peniaze", "potential": "Otvorené AI Ponuky", "limit": "Využitie Free limitu",
23
  "cust_name": "Meno a priezvisko", "company": "Firma (Voliteľné)", "phone": "Telefónne číslo", "save_cust": "Uložiť klienta",
24
  "generate_btn": "⚡ Vygenerovať/Uložiť rozpočet", "invoice_btn": "🧾 VYSTAVIŤ FAKTÚRU JEDNÝM KLIKNUTÍM",
 
47
  # -----------------------------------------------------------------------------
48
  # INICIALIZACE STAVU (ČISTÉ PRODUKČNÍ PROSTŘEDÍ)
49
  # -----------------------------------------------------------------------------
50
+ VERSION = "2.1.1-ENTERPRISE"
51
 
52
  st.set_page_config(page_title="TradeFlow AI", page_icon="🏆", layout="wide", initial_sidebar_state="expanded")
53
 
 
57
  if "user_subscribed" not in st.session_state: st.session_state.user_subscribed = False
58
  if "quotes_count" not in st.session_state: st.session_state.quotes_count = 0
59
 
60
+ # Ostré databáze startují 100% čisté a prázdné
61
+ if "db_team" not in st.session_state: st.session_state.db_team = []
 
 
 
 
 
 
 
62
  if "db_customers" not in st.session_state: st.session_state.db_customers = []
63
  if "db_quotes" not in st.session_state: st.session_state.db_quotes = []
64
  if "db_invoices" not in st.session_state: st.session_state.db_invoices = []
 
70
  {"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!"}
71
  ]
72
 
73
+ # Čistý výchozí řádek editoru bez předvyplněných částek
74
  if "cost_items_df" not in st.session_state:
75
  st.session_state.cost_items_df = pd.DataFrame([
76
+ {"Sekce nákladu": "Materiál", "Název položky / Popis": "", "Částka": 0.0}
77
  ])
78
 
79
  def format_price(amount_in_eur):
 
112
 
113
  st.markdown("### 🧪 Správa ukázkových dat")
114
  if st.button("📥 Importovat komplexní Demo Data", use_container_width=True):
115
+ # Demo struktura týmu se nahraje až teď!
116
+ st.session_state.db_team = [
117
+ {"id": 1, "jmeno": "Šimon (Director)", "sazba": 45.0},
118
+ {"id": 2, "jmeno": "Nikola (Prezentace & Podpora)", "sazba": 30.0},
119
+ {"id": 3, "jmeno": "Matěj (Technik)", "sazba": 25.0}
120
+ ]
121
  st.session_state.db_customers = [
122
  {"id": 1, "jmeno": "Jan Novák", "firma": "Stavebniny s.r.o.", "telefon": "+420 777 123 456"},
123
  {"id": 2, "jmeno": "Marie Podlahová", "firma": "Bytové družstvo", "telefon": "+420 602 987 654"},
 
150
  st.rerun()
151
 
152
  if st.button("🗑️ Kompletní vyčištění databází", use_container_width=True):
153
+ st.session_state.db_team = []
154
  st.session_state.db_customers = []
155
  st.session_state.db_quotes = []
156
  st.session_state.db_invoices = []
 
193
  st.subheader("📊 Pokročilá analýza a finanční filtrování sekcí")
194
  st.write("Sledujte finanční toky seskupené podle konkrétních nákladových sekcí napříč celou firmou.")
195
 
 
196
  flat_costs = []
197
  for q in st.session_state.db_quotes:
198
  if q.get("labor_eur", 0) > 0:
 
255
  st.info("Kniha nabídek je prázdná.")
256
 
257
  # -----------------------------------------------------------------------------
258
+ # TAB 2: AI GENERÁTOR (OPRAVENÝ EDITOR BEZ CHYBY + VOLNÝ TEXT KLIENTA)
259
  # -----------------------------------------------------------------------------
260
  with tab_generator:
261
  st.header(t["tab_gen"])
 
282
 
283
  st.markdown("### 🕒 Personální alokace (Odpracované hodiny týmu)")
284
 
285
+ if not st.session_state.db_team:
286
+ 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ě.")
287
+ team_hours = {}
288
+ else:
289
+ if st.button("🪄 Spustit AI simulaci a předvyplnit položky i hodiny"):
290
+ st.session_state["ai_mock_active"] = True
291
+ st.session_state.cost_items_df = pd.DataFrame([
292
+ {"Sekce nákladu": "Materiál", "Název položky / Popis": "AI Odhad: Potrubí, ventily a fitinky", "Částka": 480.0},
293
+ {"Sekce nákladu": "Benzín / Doprava", "Název položky / Popis": "AI Odhad: Nákladní závoz materiálu z velkoobchodu", "Částka": 65.0},
294
+ {"Sekce nákladu": "Pronájem vybavení", "Název položky / Popis": "AI Odhad: Zapůjčení tlakové zkoušečky", "Částka": 110.0}
295
+ ])
296
+ st.toast("AI úspěšně zanalyzovala text a navrhla optimální strukturu nákladů!", icon="🪄")
297
+
298
+ team_hours = {}
299
+ cols_team = st.columns(len(st.session_state.db_team))
300
+
301
+ for idx, member in enumerate(st.session_state.db_team):
302
+ with cols_team[idx]:
303
+ default_val = 0
304
+ if st.session_state.get("ai_mock_active", False):
305
+ if "director" in member["jmeno"].lower(): default_val = 10
306
+ elif "prezentace" in member["jmeno"].lower(): default_val = 6
307
+ else: default_val = 24
308
+
309
+ hours_input = st.number_input(
310
+ f"{member['jmeno']} ({format_price(member['sazba'])}/h)",
311
+ min_value=0, max_value=500, value=default_val, key=f"gen_hours_{member['id']}"
312
+ )
313
+ team_hours[member["id"]] = hours_input
314
+
315
  st.markdown("### 🚚 Přímé materiálové a vedlejší náklady")
316
+ st.caption("💡 Nové řádky přidáte symbolem `+` vespod tabulky. Řádek smažete označením a stiskem klávesy `Delete` na klávesnici.")
317
 
318
  curr_sym = CURRENCIES[st.session_state.curr]["symbol"]
319
  curr_rate = CURRENCIES[st.session_state.curr]["rate"]
320
 
321
+ # 🔥 OPRAVENO: Odstraněn nepovolený parametr 'placeholder' z TextColumn, kvůli kterému aplikace padala
322
  column_configuration = {
323
  "Sekce nákladu": st.column_config.SelectboxColumn(
324
  "Sekce nákladu",
325
  options=["Materiál", "Benzín / Doprava", "Pronájem vybavení", "Ostatní režie"],
326
  required=True
327
  ),
328
+ "Název položky / Popis": st.column_config.TextColumn("Název položky / Popis", required=True),
329
  "Částka": st.column_config.NumberColumn(f"Částka ({curr_sym})", min_value=0.0, required=True, format="%.2f")
330
  }
331
 
 
332
  edited_df = st.data_editor(
333
  st.session_state.cost_items_df,
334
  column_config=column_configuration,
 
338
  )
339
  st.session_state.cost_items_df = edited_df
340
 
 
341
  st.markdown("### 📂 Hromadný import externích nákladů přes CSV")
342
+ csv_file = st.file_uploader("Nahrajte soubor .csv (Struktura sloupců: Sekce nákladu, Název položky / Popis, Částka)", type=["csv"])
343
  if csv_file is not None:
344
  try:
345
  uploaded_df = pd.read_csv(csv_file)
 
348
  sanitized_df = uploaded_df[needed_headers].copy()
349
  sanitized_df["Částka"] = pd.to_numeric(sanitized_df["Částka"], errors='coerce').fillna(0.0)
350
  st.session_state.cost_items_df = pd.concat([st.session_state.cost_items_df, sanitized_df], ignore_index=True)
351
+ st.success("CSV data byla úspěšně nahrána do tabulky výše!")
352
  time.sleep(0.4)
353
  st.rerun()
354
  else:
355
+ st.error("Chyba: CSV neodpovídá požadované struktuře záhlaví.")
356
  except Exception as e:
357
+ st.error(f"Soubor nelze přečíst: {e}")
358
 
359
  st.markdown("---")
360
 
 
361
  total_labor_eur = 0.0
362
  for member in st.session_state.db_team:
363
+ total_labor_eur += team_hours.get(member["id"], 0) * member["sazba"]
364
 
365
  total_extra_local = edited_df["Částka"].sum() if not edited_df.empty else 0.0
366
  total_extra_eur = total_extra_local / curr_rate
 
372
  if not selected_cust or selected_cust.strip() == "":
373
  st.error("Chyba: Před uložením musíte uvést nebo vybrat jméno klienta!")
374
  else:
 
375
  if selected_cust not in cust_names:
376
  st.session_state.db_customers.append({
377
  "id": len(st.session_state.db_customers) + 1,
 
380
  "telefon": "Nezadáno"
381
  })
382
 
 
383
  saved_extra_costs = []
384
  for _, row in edited_df.iterrows():
385
  saved_extra_costs.append({
 
403
  st.session_state.quotes_count += 1
404
  st.session_state["ai_mock_active"] = False
405
 
 
406
  st.session_state.cost_items_df = pd.DataFrame([{"Sekce nákladu": "Materiál", "Název položky / Popis": "", "Částka": 0.0}])
407
  st.success("Rozpočet byl úspěšně zanesen do firemní databáze.")
408
  time.sleep(0.4)
 
413
  # -----------------------------------------------------------------------------
414
  with tab_team:
415
  st.header("👥 Správa firemního týmu a hodinových sazeb")
416
+ st.write("Editujte profily a interní hodinové sazby svých pracovníků.")
417
 
418
  t_col1, t_col2 = st.columns([2, 1])
419
 
 
429
  if team_display_list:
430
  st.dataframe(pd.DataFrame(team_display_list), use_container_width=True, hide_index=True)
431
  else:
432
+ st.info("V týmu se aktuálně nenachází žádný pracovník. Použijte formulář vpravo nebo načtěte Demo data.")
433
 
434
  st.markdown("**❌ Odstranění pracovníka z týmu**")
435
  if st.session_state.db_team:
 
491
  st.error("Jméno klienta je povinné pole.")
492
 
493
  # -----------------------------------------------------------------------------
494
+ # TAB 5: FAKTURACE
495
  # -----------------------------------------------------------------------------
496
  with tab_fakturace:
497
  st.header(t["tab_inv"])
 
511
  "stav": "Odesláno",
512
  "datum": time.strftime("%Y-%m-%d")
513
  })
514
+ st.success("Faktura byla vystavena a uložena do knihy!")
515
  time.sleep(0.3)
516
  st.rerun()
517
  else:
518
+ st.info("V systému se nenachází žádná schválená pevná nabídka (Orientační odhady nelze fakturovat).")
519
 
520
  st.markdown("---")
521
  st.subheader("Kompletní přehled vydaných faktur a stavu plateb")
 
537
  st.info("Kniha faktur je prázdná.")
538
 
539
  # -----------------------------------------------------------------------------
540
+ # TAB 6: PRO BONUSY
541
  # -----------------------------------------------------------------------------
542
  with tab_pro_features:
543
  st.header(t["tab_pro"])
 
616
  st.markdown("### 📱 3. Finální vygenerovaná zpráva pro klienta")
617
 
618
  if active_debtor:
 
619
  raw_template = active_preset["text"]
620
  final_compiled_sms = raw_template.replace("{jmeno}", active_debtor["zakaznik"])\
621
  .replace("{cislo}", active_debtor["cislo"])\
622
  .replace("{castka}", format_price(active_debtor["castka"]))
623
 
 
624
  st.markdown(f"""
625
  <div style="
626
  background-color: #f8f9fa;
 
634
  word-wrap: break-word;
635
  line-height: 1.6;
636
  margin-bottom: 12px;
637
+ ;" id="sms_display_box">{final_compiled_sms}</div>
638
  """, unsafe_allow_html=True)
639
 
 
640
  safe_encoded_sms = urllib.parse.quote(final_compiled_sms)
641
 
 
642
  copy_button_html = f"""
643
  <button onclick="
644
  navigator.clipboard.writeText(decodeURIComponent('{safe_encoded_sms}'));