import streamlit as st import pandas as pd import time import urllib.parse import stripe # Připraveno pro splnění požadavků v requirements.txt import streamlit.components.v1 as components # Bezpečný import pro HTML komponenty na Hugging Face # ----------------------------------------------------------------------------- # KOMPLETNÍ JAZYKOVÉ LOKALIZACE PRO VŠECHNY PRVKY NA STRÁNCE (CZ, SK, EN) # ----------------------------------------------------------------------------- TRANSLATIONS = { "CZ": { # Obecné a záložky "title": "🏆 TradeFlow AI", "caption": "Chytrý operační systém pro novou generaci řemeslníků.", "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", # Sidebar & Admin Panel "sb_settings": "🌐 Nastavení", "sb_lang": "Jazyk aplikace", "sb_curr": "Aktivní měna", "sb_dev_panel": "🛡️ Dev Admin Panel", "sb_pwd_label": "Zadejte administrátorské heslo", "sb_pwd_hint": "Heslo pro testování je: admin123", "sb_unlock_btn": "Odemknout Admin Panel", "sb_auth_ok": "Přístup povolen!", "sb_auth_err": "Nesprávné heslo!", "sb_auth_status": "🔓 Vývojářská autorizace", "sb_pro_toggle": "Uživatel má aktivní PRO tarif", "sb_quotes_count": "Počet vytvořených nabídek", "sb_demo_section": "🧪 Správa ukázkových dat", "sb_import_demo": "📥 Importovat komplexní Demo Data", "sb_clear_demo": "🗑️ Kompletní vyčištění databází", "sb_logout": "Zamknout panel & Odhlásit se", "toast_demo_ok": "Systém byl úspěšně naplněn cvičnými daty!", "toast_clear_ok": "Aplikace byla kompletně vyčištěna.", # Dashboard Metriky "profit": "Skutečný zisk", "pending": "Čekající peníze", "potential": "Otevřené AI Nabídky", "limit": "Využití Free limitu", "dash_help_potential": "Počítá pouze Přesné fixní nabídky.", "dash_limit_warn": "⚠️ LIMIT UKONČEN", # Dashboard Analýza "dash_analysis_title": "📊 Pokročilá analýza a finanční filtrování sekcí", "dash_analysis_desc": "Sledujte finanční toky seskupené podle konkrétních nákladových sekcí napříč celou firmou.", "dash_filter_cat": "Zvolte nákladovou kategorii k odfiltrování:", "dash_filter_type": "Filtrovat typ zakázky:", "dash_filter_all_cats": "Zobrazit vše", "dash_type_all": "Všechny rozpočty", "dash_type_fixed": "Pouze závazné přesné nabídky", "dash_type_estimated": "Pouze orientační odhady", "dash_aggregated_spend": "💰 Agregovaná útrata ve zvoleném filtru:", "dash_no_records": "Pro zadaná kritéria a filtry nebyly nalezeny žádné záznamy.", "dash_no_costs_yet": "V systému zatím nejsou evidovány žádné podrobné položky nákladů.", "dash_book_title": "Kniha vytvořených kalkulací a nabídek", "dash_book_empty": "Kniha nabídek je prázdná.", # Sloupce tabulek v Dashboardu "col_project": "Projekt / Klient", "col_section": "Sekce nákladu", "col_item_name": "Název položky / Popis", "col_amount": "Částka", "col_type": "Typ rozpočtu", "col_date": "Datum", # AI Generátor Nabídek "gen_type_label": "Vyberte povahu výstupu a nacenění:", "gen_type_est": "🔮 Orientační odhad od AI (Čistě orientační kalkulace, nepropisuje se do dlužníků)", "gen_type_fix": "🎯 Přesná fixní nabídka (Závazný finanční planar, slouží jako podklad pro fakturu)", "gen_client_section": "### 👤 Přiřazení klienta", "gen_client_mode": "Způsob zadání zákazníka:", "gen_client_crm": "Vybrat z adresáře CRM", "gen_client_manual": "✍️ Zadat úplně nového klienta ručně", "gen_select_client": "Zvolte klienta:", "gen_input_client_name": "Zadejte jméno a příjmení nového klienta:", "gen_client_placeholder": "Např. František Novotný", "gen_spec_label": "Detailní specifikace projektu a zadání pro AI", "gen_spec_default": "Kompletní rekonstrukce rodinného domu, rozvody vody a finální zapojení sanitární techniky.", "gen_team_alloc": "### 🕒 Personální alokace (Odpracované hodiny týmu)", "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ě.", "gen_ai_btn": "🪄 Spustit AI simulaci a předvyplnit položky i hodiny", "gen_ai_toast": "AI úspěšně zanalyzovala text a navrhla optimální strukturu nákladů!", "gen_direct_costs": "### 🚚 Přímé materiálové a vedlejší náklady", "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.", "gen_csv_title": "### 📂 Hromadný import externích nákladů přes CSV", "gen_csv_uploader": "Nahrajte soubor .csv (Struktura sloupců: Sekce nákladu, Název položky / Popis, Částka)", "gen_csv_success": "CSV data byla úspěšně nahrána do tabulky výše!", "gen_csv_err_headers": "Chyba: CSV neodpovídá požadované struktuře záhlaví.", "gen_csv_err_read": "Soubor nelze přečíst:", "gen_total_calc": "### 📈 Celková kalkulace rozpočtu:", "gen_save_btn": "💾 Schválit a uložit rozpočet do systému", "gen_err_no_client": "Chyba: Před uložením musíte uvést nebo vybrat jméno klienta!", "gen_save_success": "Rozpočet byl úspěšně zanesen do firemní databáze.", # Správa týmu "team_title": "👥 Správa firemního týmu a mzdových sazeb", "team_desc": "Editujte profily a interní hodinové sazby svých pracovníků.", "team_sub_list": "Evidovaní pracovníci a specialisté", "team_col_id": "Interní ID", "team_col_role": "Jméno člena / Profesní role", "team_col_rate": "Hodinová mzdová sazba", "team_empty": "V týmu se aktuálně nenachází žádný pracovník. Použijte formulář vpravo nebo načtěte Demo data.", "team_remove_section": "**❌ Odstranění pracovníka z týmu**", "team_remove_select": "Vyberte osobu k odebrání:", "team_remove_btn": "Definitivně odebrat člena", "team_remove_toast": "Pracovník byl vyřazen z interní databáze.", "team_add_section": "### ➕ Přidat / Upravit kartu pracovníka", "team_input_name": "Jméno příjmení / Pracovní zařazení", "team_input_placeholder": "Např. Kamil (Zedník)", "team_input_rate": "Hodinová sazba v aktivní měně", "team_submit_btn": "Uložit profil", "team_update_toast": "Sazba u pracovníka byla zaktualizována.", "team_add_toast": "Nový člen byl úspěšně zaevidován.", # CRM Adresář "crm_title": "👥 Adresář zákazníků (CRM)", "crm_empty": "Adresář klientů je momentálně prázdný.", "crm_add_section": "### ➕ Rychlé ruční založení klienta", "cust_name": "Jméno a příjmení", "company": "Firma (Volitelné)", "phone": "Telefonní číslo", "save_cust": "Uložit klienta", "crm_err_name": "Jméno klienta je povinné pole.", "crm_success_toast": "Klient byl bezpečně uložen do CRM.", # Fakturace "inv_title": "🧾 Fakturace jedním klikem", "inv_select_quote": "Zvolte schválený pevný rozpočet k okamžité fakturaci:", "invoice_btn": "🧾 VYSTAVIT FAKTURU JEDNÍM KLIKNUTÍM", "inv_success_toast": "Faktura byla vystavena a uložena do knihy!", "inv_no_fixed_quotes": "V systému se nenachází žádná schválená pevná nabídka (Orientační odhady nelze fakturovat).", "inv_book_title": "Kompletní přehled vydaných faktur a stavu plateb", "inv_status_paid": "🟢 Uhrazeno", "inv_status_sent": "🔵 Odesláno (Po splatnosti)", "inv_mark_paid": "Označit jako ZAPLACENO", "inv_empty": "Kniha faktur je prázdná.", # PRO Funkce "paywall_msg": "🔒 Dosáhli jste limitu 3 bezplatných nabídek. Pro neomezený přístup aktivujte tarif PRO.", "pro_router": "📍 AI Plánovač tras a logistiky", "pro_sms": "📲 Inteligentní SMS Centrum upomínek", "pro_locked_warn": "🔒 Tento panel obsahuje pokročilé moduly TradeFlow PRO. Aktivujte zkušební verzi v Dev Admin Panelu na boční liště.", "pro_route_title": "📍 AI Geografický optimalizátor tras výjezdů", "pro_route_label": "Seznam adres plánovaných zásahů", "pro_route_btn": "🗺️ Spočítat matematicky nejkratší trasu", "pro_route_result": "🚀 AI Výsledek kalkulace: Výjezd z Plzně -> Beroun -> Praha. Celková úspora paliva a logistických nákladů činí: 42 km.", "pro_sms_title": "📱 Inteligentní manažerské SMS centrum pohledávek", "pro_sms_step1": "### 🔍 1. Vyhledání dlužníka v systému", "pro_sms_filter_name": "Filtrovat neplatiče dle jména:", "pro_sms_sort_label": "Seřadit dlužníky dle kritéria:", "pro_sms_sort_high": "Od nejvyšší dlužné částky", "pro_sms_sort_low": "Od nejnižší dlužné částky", "pro_sms_no_debts": "V systému aktuálně neevidujeme žádné neuhrazené faktury odpovídající filtrům.", "pro_sms_select_debtor": "🎯 Vyberte cílového dlužníka k obeslání:", "pro_sms_step2": "### 📋 2. Výběr textové šablony", "pro_sms_template_label": "Zvolte aktivní komunikační šablonu:", "pro_sms_expander": "⚙️ Pokročilý editor šablon & Generátor nových textů přes AI", "pro_sms_ai_assistant": "🤖 **AI Asistent pro tvorbu tónu textu**", "pro_sms_ai_prompt": "Zadejte zadání pro AI (např. 'Napiš velmi naléhavou předžalobní výzvu')", "pro_sms_ai_btn": "🪄 Sestavit novou šablonu pomocí AI", "pro_sms_ai_success": "AI text úspěšně vygenerován do pole níže!", "pro_sms_new_title": "Název nové šablony:", "pro_sms_new_body": "Text těla zprávy (používejte značky {jmeno}, {cislo}, {castka})", "pro_sms_save_template_btn": "Uložit šablonu do trvalého seznamu", "pro_sms_template_success": "Šablona úspěšně uložena!", "pro_sms_step3": "### 📱 3. Finální vygenerovaná zpráva pro klienta", "pro_sms_copy_btn": "📋 Kopírovat text zprávy", "pro_sms_copy_ok": "✅ Zkopírováno do schránky!", "pro_sms_select_info": "Vyberte dlužníka pro zobrazení a zkopírování SMS zprávy." }, "SK": { # Obecné a záložky "title": "🏆 TradeFlow AI", "caption": "Chytrý operačný systém pre novú generáciu remeselníkov.", "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", # Sidebar & Admin Panel "sb_settings": "🌐 Nastavenia", "sb_lang": "Jazyk aplikácie", "sb_curr": "Aktívna mena", "sb_dev_panel": "🛡️ Dev Admin Panel", "sb_pwd_label": "Zadajte administrátorské heslo", "sb_pwd_hint": "Heslo pre testovanie je: admin123", "sb_unlock_btn": "Odomknúť Admin Panel", "sb_auth_ok": "Prístup povolený!", "sb_auth_err": "Nesprávne heslo!", "sb_auth_status": "🔓 Vývojárska autorizácia", "sb_pro_toggle": "Užívateľ má aktívny PRO tarif", "sb_quotes_count": "Počet vytvorených ponúk", "sb_demo_section": "🧪 Správa ukážkových dát", "sb_import_demo": "📥 Importovať komplexné Demo Dáta", "sb_clear_demo": "🗑️ Kompletné vyčistenie databáz", "sb_logout": "Zamknúť panel & Odhlásiť sa", "toast_demo_ok": "Systém bol úspešne naplnený cvičnými dátami!", "toast_clear_ok": "Aplikácia bola kompletne vyčistená.", # Dashboard Metriky "profit": "Skutočný zisk", "pending": "Čakajúce peniaze", "potential": "Otvorené AI Ponuky", "limit": "Využitie Free limitu", "dash_help_potential": "Počíta iba Presné fixné ponuky.", "dash_limit_warn": "⚠️ LIMIT UKONČENÝ", # Dashboard Analýza "dash_analysis_title": "📊 Pokročilá analýza a finančné filtrovanie sekcií", "dash_analysis_desc": "Sledujte finančné toky zoskupené podľa konkrétnych nákladových sekcií naprieč celou firmou.", "dash_filter_cat": "Zvoľte nákladovú kategóriu na odfiltrovanie:", "dash_filter_type": "Filtrovať typ zákazky:", "dash_filter_all_cats": "Zobraziť všetko", "dash_type_all": "Všetky rozpočty", "dash_type_fixed": "Iba záväzné presné ponuky", "dash_type_estimated": "Iba orientačné odhady", "dash_aggregated_spend": "💰 Agregovaná útrata vo zvolenom filtri:", "dash_no_records": "Pre zadané kritériá a filtre neboli nájdené žiadne záznamy.", "dash_no_costs_yet": "V systéme zatiaľ nie sú evidované žiadne podrobné položky nákladov.", "dash_book_title": "Kniha vytvorených kalkulácií a ponúk", "dash_book_empty": "Kniha ponúk je prázdna.", # Sloupce tabulek v Dashboardu "col_project": "Projekt / Klient", "col_section": "Sekcia nákladu", "col_item_name": "Názov položky / Popis", "col_amount": "Čiastka", "col_type": "Typ rozpočtu", "col_date": "Dátum", # AI Generátor Nabídek "gen_type_label": "Vyberte povahu výstupu a nacenenie:", "gen_type_est": "🔮 Orientačný odhad od AI (Čisto orientačná kalkulácia, neprepisuje sa do dlžníkov)", "gen_type_fix": "🎯 Presná fixná ponuka (Záväzný finančný plán, slúži ako podklad pre faktúru)", "gen_client_section": "### 👤 Priradenie klienta", "gen_client_mode": "Spôsob zadania zákazníka:", "gen_client_crm": "Vybrať z adresára CRM", "gen_client_manual": "✍️ Zadať úplne nového klienta ručne", "gen_select_client": "Zvoľte klienta:", "gen_input_client_name": "Zadajte meno a priezvisko nového klienta:", "gen_client_placeholder": "Napr. František Novotný", "gen_spec_label": "Detailná špecifikácia projektu a zadanie pre AI", "gen_spec_default": "Kompletná rekonštrukcia rodinného domu, rozvody vody a finálne zapojenie sanitárnej techniky.", "gen_team_alloc": "### 🕒 Personálna alokácia (Odpracované hodiny tímu)", "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.", "gen_ai_btn": "🪄 Spustiť AI simuláciu a predvyplniť položky aj hodiny", "gen_ai_toast": "AI úspešne zanalyzovala text a navrhla optimálnu štruktúru nákladov!", "gen_direct_costs": "### 🚚 Priame materiálové a vedľajšie náklady", "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.", "gen_csv_title": "### 📂 Hromadný import externých nákladov cez CSV", "gen_csv_uploader": "Nahrajte súbor .csv (Štruktúra stĺpcov: Sekcia nákladu, Název položky / Popis, Částka)", "gen_csv_success": "CSV dáta boli úspešne nahrané do tabuľky vyššie!", "gen_csv_err_headers": "Chyba: CSV neodpovedá požadovanej štruktúre záhlavia.", "gen_csv_err_read": "Súbor nie je možné prečítať:", "gen_total_calc": "### 📈 Celková kalkulácia rozpočtu:", "gen_save_btn": "💾 Schváliť a uložiť rozpočet do systému", "gen_err_no_client": "Chyba: Pred uložením musíte uviesť alebo vybrať meno klienta!", "gen_save_success": "Rozpočet bol úspešne zanesený do firemnej databázy.", # Správa týmu "team_title": "👥 Správa firemného tímu a mzdových sadzieb", "team_desc": "Editujte profily a interné hodinové sadzby svojich pracovníkov.", "team_sub_list": "Evidovaní pracovníci a špecialisti", "team_col_id": "Interné ID", "team_col_role": "Meno člena / Profesijná rola", "team_col_rate": "Hodinová mzdová sadzba", "team_empty": "V tíme sa aktuálne nenachádza žiadny pracovník. Použite formulár vpravo alebo načítajte Demo dáta.", "team_remove_section": "**❌ Odstránenie pracovníka z tímu**", "team_remove_select": "Vyberte osobu na odobratie:", "team_remove_btn": "Definitívne odobrať člena", "team_remove_toast": "Pracovník bol vyradený z internej databázy.", "team_add_section": "### ➕ Pridať / Upraviť kartu pracovníka", "team_input_name": "Meno priezvisko / Pracovné zaradenie", "team_input_placeholder": "Napr. Kamil (Murár)", "team_input_rate": "Hodinová sadzba v aktívnej mene", "team_submit_btn": "Uložiť profil", "team_update_toast": "Sadzba u pracovníka bola zaktualizovaná.", "team_add_toast": "Nový člen bol úspešne zaevidovaný.", # CRM Adresář "crm_title": "👥 Adresár zákazníkov (CRM)", "crm_empty": "Adresár klientov je momentálne prázdny.", "crm_add_section": "### ➕ Rýchle ručné založenie klienta", "cust_name": "Meno a priezvisko", "company": "Firma (Voliteľné)", "phone": "Telefónne číslo", "save_cust": "Uložiť klienta", "crm_err_name": "Meno klienta je povinné pole.", "crm_success_toast": "Klient bol bezpečne uložený do CRM.", # Fakturace "inv_title": "🧾 Fakturácia jedným klikom", "inv_select_quote": "Zvoľte schválený pevný rozpočet k okamžitej fakturácii:", "invoice_btn": "🧾 VYSTAVIŤ FAKTÚRU JEDNÝM KLIKNUTÍM", "inv_success_toast": "Faktúra bola vystavená a uložená do knihy!", "inv_no_fixed_quotes": "V systéme sa nenachádza žiadna schválená pevná ponuka (Orientačné odhady nie je možné fakturovať).", "inv_book_title": "Kompletný prehľad vydaných faktúr a stavu platieb", "inv_status_paid": "🟢 Uhradené", "inv_status_sent": "🔵 Odoslané (Po splatnosti)", "inv_mark_paid": "Označiť ako ZAPLATENÉ", "inv_empty": "Kniha faktúr je prázdna.", # PRO Funkce "paywall_msg": "🔒 Dosiahli ste limitu 3 bezplatných ponúk. Pre neobmedzený prístup aktivujte tarif PRO.", "pro_router": "📍 AI Plánovač trás a logistiky", "pro_sms": "📲 Inteligentné SMS Centrum upomienok", "pro_locked_warn": "🔒 Tento panel obsahuje pokročilé moduly TradeFlow PRO. Aktivujte skúšobnú verziu v Dev Admin Panelu na bočnej lište.", "pro_route_title": "📍 AI Geografický optimalizátor trás výjazdov", "pro_route_label": "Zoznam adries plánovaných zásahov", "pro_route_btn": "🗺️ Spočítať matematicky najkratšiu trasu", "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.", "pro_sms_title": "📱 Inteligentné manažérske SMS centrum pohľadávok", "pro_sms_step1": "### 🔍 1. Vyhľadanie dlžníka v systéme", "pro_sms_filter_name": "Filtrovať neplatičov podľa mena:", "pro_sms_sort_label": "Zoradiť dlžníkov podľa kritéria:", "pro_sms_sort_high": "Od najvyššej dlžnej sumy", "pro_sms_sort_low": "Od najnižšej dlžnej sumy", "pro_sms_no_debts": "V systéme aktuálne neevidujeme žiadne neuhradené faktúry zodpovedajúce filtrom.", "pro_sms_select_debtor": "🎯 Vyberte cieľového dlžníka na obeslanie:", "pro_sms_step2": "### 📋 2. Výber textovej šablóny", "pro_sms_template_label": "Zvoľte aktívnu komunikačnú šablónu:", "pro_sms_expander": "⚙️ Pokročilý editor šablón & Generátor nových textov cez AI", "pro_sms_ai_assistant": "🤖 **AI Asistent pre tvorbu tónu textu**", "pro_sms_ai_prompt": "Zadajte zadanie pre AI (napr. 'Napíš veľmi naliehavú predžalobnú výzvu')", "pro_sms_ai_btn": "🪄 Zostaviť novú šablónu pomocou AI", "pro_sms_ai_success": "AI text úspešne vygenerovaný do poľa nižšie!", "pro_sms_new_title": "Názov novej šablóny:", "pro_sms_new_body": "Text tela správy (používajte značky {jmeno}, {cislo}, {castka})", "pro_sms_save_template_btn": "Uložiť šablónu do trvalého zoznamu", "pro_sms_template_success": "Šablóna úspešne uložená!", "pro_sms_step3": "### 📱 3. Finálna vygenerovaná správa pre klienta", "pro_sms_copy_btn": "📋 Kopírovať text správy", "pro_sms_copy_ok": "✅ Skopírované do schránky!", "pro_sms_select_info": "Vyberte dlžníka pre zobrazenie a skopírovanie SMS správy." }, "EN": { # Obecné a záložky "title": "🏆 TradeFlow AI", "caption": "Smart operating system for the next-gen skilled trades.", "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", # Sidebar & Admin Panel "sb_settings": "🌐 Settings", "sb_lang": "App Language", "sb_curr": "Active Currency", "sb_dev_panel": "🛡️ Dev Admin Panel", "sb_pwd_label": "Enter Admin Password", "sb_pwd_hint": "Testing password is: admin123", "sb_unlock_btn": "Unlock Admin Panel", "sb_auth_ok": "Access Granted!", "sb_auth_err": "Incorrect Password!", "sb_auth_status": "🔓 Developer Authorization", "sb_pro_toggle": "User has active PRO tier", "sb_quotes_count": "Created quotes counter", "sb_demo_section": "🧪 Sample Data Management", "sb_import_demo": "📥 Import Complex Demo Data", "sb_clear_demo": "🗑️ Complete Databases Wipe", "sb_logout": "Lock Panel & Log Out", "toast_demo_ok": "System filled with sample data successfully!", "toast_clear_ok": "Application databases wiped completely.", # Dashboard Metriky "profit": "Actual Profit", "pending": "Pending Revenue", "potential": "Open AI Quotes", "limit": "Free Tier Usage", "dash_help_potential": "Counts strict Fixed Price quotes only.", "dash_limit_warn": "⚠️ LIMIT EXCEEDED", # Dashboard Analýza "dash_analysis_title": "📊 Advanced Section Analysis & Financial Filtering", "dash_analysis_desc": "Monitor financial flows grouped by specific cost sections across the entire company.", "dash_filter_cat": "Select cost category to filter by:", "dash_filter_type": "Filter project type:", "dash_filter_all_cats": "Show all", "dash_type_all": "All budgets", "dash_type_fixed": "Binding fixed quotes only", "dash_type_estimated": "Rough estimates only", "dash_aggregated_spend": "💰 Aggregated spend in selected filter:", "dash_no_records": "No records found matching the given criteria and filters.", "dash_no_costs_yet": "No itemized cost entries are registered in the system yet.", "dash_book_title": "Log of Created Calculations & Quotes", "dash_book_empty": "The quotes logbook is currently empty.", # Sloupce tabulek v Dashboardu "col_project": "Project / Client", "col_section": "Cost Section", "col_item_name": "Item Name / Description", "col_amount": "Amount", "col_type": "Budget Type", "col_date": "Date", # AI Generátor Nabídek "gen_type_label": "Select output scope and pricing strategy:", "gen_type_est": "🔮 Rough AI Estimate (Purely indicative calculation, does not map into debtors ledger)", "gen_type_fix": "🎯 Binding Fixed Quote (Strict financial plan, serves as an invoice baseline)", "gen_client_section": "### 👤 Client Assignment", "gen_client_mode": "Customer input method:", "gen_client_crm": "Select from CRM address book", "gen_client_manual": "✍️ Enter brand new client manually", "gen_select_client": "Choose client:", "gen_input_client_name": "Enter full name of the new client:", "gen_client_placeholder": "E.g., John Smith", "gen_spec_label": "Detailed project specifications and instructions for AI", "gen_spec_default": "Complete residential house reconstruction, plumbing pipelines layout and final sanitary alignment.", "gen_team_alloc": "### 🕒 Workforce Allocation (Team hours worked)", "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.", "gen_ai_btn": "🪄 Execute AI Simulation and pre-fill rows & hours", "gen_ai_toast": "AI successfully analyzed text and proposed optimal cost structure!", "gen_direct_costs": "### 🚚 Direct Material & Secondary Expenses", "gen_table_hint": "💡 Add new rows using the `+` icon at the bottom. Delete rows by selecting them and pressing `Delete` on your keyboard.", "gen_csv_title": "### 📂 Bulk External Costs CSV Import", "gen_csv_uploader": "Upload a .csv file (Required column layout: Sekce nákladu, Název položky / Popis, Částka)", "gen_csv_success": "CSV data successfully mapped into the table above!", "gen_csv_err_headers": "Error: CSV does not match the required headers layout.", "gen_csv_err_read": "File cannot be parsed:", "gen_total_calc": "### 📈 Total Budget Calculation:", "gen_save_btn": "💾 Approve and record budget into the system", "gen_err_no_client": "Error: You must state or select a client name before saving!", "gen_save_success": "Budget was successfully recorded into company database.", # Správa týmu "team_title": "👥 Internal Team & Hourly Sazby Management", "team_desc": "Edit profiles and corporate hourly rates of your specialists.", "team_sub_list": "Registered Workers & Specialists", "team_col_id": "Internal ID", "team_col_role": "Member Name / Professional Role", "team_col_rate": "Hourly Wage Rate", "team_empty": "No workers inside the team database. Use the form on the right or turn on Demo Data.", "team_remove_section": "**❌ Exclude worker from team**", "team_remove_select": "Select person to remove:", "team_remove_btn": "Permanently remove member", "team_remove_toast": "Worker was removed from internal database.", "team_add_section": "### ➕ Add / Modify Worker Card", "team_input_name": "Full Name / Job Position", "team_input_placeholder": "E.g., Carl (Mason)", "team_input_rate": "Hourly rate in active currency", "team_submit_btn": "Save Profile", "team_update_toast": "Worker wage rate updated.", "team_add_toast": "New member successfully registered.", # CRM Adresář "crm_title": "👥 Client Address Book (CRM)", "crm_empty": "Client book is empty at the moment.", "crm_add_section": "### ➕ Quick Manual Client Entry", "cust_name": "Full Name", "company": "Company (Optional)", "phone": "Phone Number", "save_cust": "Save Client", "crm_err_name": "Client name is a mandatory field.", "crm_success_toast": "Client records safely saved to CRM.", # Fakturace "inv_title": "🧾 1-Click Invoicing System", "inv_select_quote": "Choose an approved fixed budget for immediate invoicing:", "invoice_btn": "🧾 ISSUE 1-CLICK INVOICE NOW", "inv_success_toast": "Invoice issued and stored into ledger successfully!", "inv_no_fixed_quotes": "No approved fixed quotes available (Rough estimations cannot be converted into an invoice).", "inv_book_title": "Complete Issued Invoices Overview & Payments Status", "inv_status_paid": "🟢 Settled & Paid", "inv_status_sent": "🔵 Sent (Overdue)", "inv_mark_paid": "Mark as PAID / SETTLED", "inv_empty": "The invoice ledger is empty.", # PRO Funkce "paywall_msg": "🔒 You have reached the 3 free quotes limit. Unlock PRO tier for unlimited access.", "pro_router": "📍 AI Route & Logistics Planner", "pro_sms": "📲 Intelligent SMS Reminder Center", "pro_locked_warn": "🔒 This panel contains advanced TradeFlow PRO modules. Activate evaluation license in Dev Admin Panel via sidebar.", "pro_route_title": "📍 AI Geographical Route Optimization Engine", "pro_route_label": "Target list of planned on-site addresses", "pro_route_btn": "🗺️ Compute Mathematical Shortest Path", "pro_route_result": "🚀 AI Optimization outcome: Departure Plzeň -> Beroun -> Praha. Total fuel and logistics distance saved: 42 km.", "pro_sms_title": "📱 Smart Corporate Debt Collection SMS Center", "pro_sms_step1": "### 🔍 1. Locating Delinquent Accounts in System", "pro_sms_filter_name": "Filter debtors by name lookup:", "pro_sms_sort_label": "Sort debtors by criteria order:", "pro_sms_sort_high": "Highest outstanding debt first", "pro_sms_sort_low": "Lowest outstanding debt first", "pro_sms_no_debts": "No outstanding overdue invoices matching current filters discovered.", "pro_sms_select_debtor": "🎯 Choose target account to contact:", "pro_sms_step2": "### 📋 2. Template Preset Assignment", "pro_sms_template_label": "Choose active communications preset:", "pro_sms_expander": "⚙️ Advanced Presets Editor & AI Content Generator Engine", "pro_sms_ai_assistant": "🤖 **AI Tone Modulation Assistant**", "pro_sms_ai_prompt": "State assignment for AI (e.g., 'Write highly urgent pre-legal warning letter')", "pro_sms_ai_btn": "🪄 Compose New Template via AI", "pro_sms_ai_success": "AI copy successfully synthesized into the area below!", "pro_sms_new_title": "New template label name:", "pro_sms_new_body": "Message body code (use placeholder tags {jmeno}, {cislo}, {castka})", "pro_sms_save_template_btn": "Save Preset into permanent storage", "pro_sms_template_success": "Template preset recorded!", "pro_sms_step3": "### 📱 3. Final Compiled Client Text Copy", "pro_sms_copy_btn": "📋 Copy Message Text", "pro_sms_copy_ok": "✅ Copy to clipboard successful!", "pro_sms_select_info": "Select an overdue account to preview and extract SMS dispatch copy." } } CURRENCIES = { "EUR": {"symbol": "EUR", "rate": 1.0}, "CZK": {"symbol": "Kč", "rate": 25.12}, "USD": {"symbol": "$", "rate": 1.09}, "GBP": {"symbol": "£", "rate": 0.84}, "CHF": {"symbol": "CHF", "rate": 0.96} } # ----------------------------------------------------------------------------- # INICIALIZACE STAVU # ----------------------------------------------------------------------------- VERSION = "2.1.2-ENTERPRISE" st.set_page_config(page_title="TradeFlow AI", page_icon="🏆", layout="wide", initial_sidebar_state="expanded") if "lang" not in st.session_state: st.session_state.lang = "CZ" if "curr" not in st.session_state: st.session_state.curr = "EUR" if "admin_authenticated" not in st.session_state: st.session_state.admin_authenticated = False if "user_subscribed" not in st.session_state: st.session_state.user_subscribed = False if "quotes_count" not in st.session_state: st.session_state.quotes_count = 0 if "db_team" not in st.session_state: st.session_state.db_team = [] if "db_customers" not in st.session_state: st.session_state.db_customers = [] if "db_quotes" not in st.session_state: st.session_state.db_quotes = [] if "db_invoices" not in st.session_state: st.session_state.db_invoices = [] if "sms_presets" not in st.session_state: st.session_state.sms_presets = [ {"název": "Standardní upomínka", "text": "Dobrý den, {jmeno}, evidujeme, že faktura číslo {cislo} na částku {castka} je po splatnosti. Prosíme o neprodlené uhrazení. Tým TradeFlow AI."}, {"název": "Důrazná výzva", "text": "Upozornění pro: {jmeno}. Faktura {cislo} ({castka}) je po splatnosti. Žádáme o okamžitou úhradu na náš bankovní účet, abychom předešli dalším krokům."}, {"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!"} ] t = TRANSLATIONS[st.session_state.lang if st.session_state.lang in TRANSLATIONS else "CZ"] if "cost_items_df" not in st.session_state: st.session_state.cost_items_df = pd.DataFrame([ {"Sekce nákladu": "Materiál", "Název položky / Popis": "", "Částka": 0.0} ]) def format_price(amount_in_eur): c_info = CURRENCIES[st.session_state.curr] converted = amount_in_eur * c_info["rate"] if c_info["symbol"] in ["Kč", "CHF"]: return f"{converted:,.2f} {c_info['symbol']}" return f"{c_info['symbol']}{converted:,.2f}" # ----------------------------------------------------------------------------- # SIDEBAR: PANEL S NASTAVENÍM & VÝVOJÁŘSKÝM PUSH SEEDINGEM # ----------------------------------------------------------------------------- with st.sidebar: st.header(t["sb_settings"]) st.session_state.lang = st.selectbox(t["sb_lang"], list(TRANSLATIONS.keys()), index=list(TRANSLATIONS.keys()).index(st.session_state.lang)) # Po změně jazyka musíme hned aktualizovat překladový slovník, aby zbytek kódu běžel v novém kontextu t = TRANSLATIONS[st.session_state.lang] st.session_state.curr = st.selectbox(t["sb_curr"], list(CURRENCIES.keys()), index=list(CURRENCIES.keys()).index(st.session_state.curr)) st.markdown("---") st.header(t["sb_dev_panel"]) if not st.session_state.admin_authenticated: pwd_input = st.text_input(t["sb_pwd_label"], type="password", help=t["sb_pwd_hint"]) if st.button(t["sb_unlock_btn"]): if pwd_input == "admin123": st.session_state.admin_authenticated = True st.success(t["sb_auth_ok"]) st.rerun() else: st.error(t["sb_auth_err"]) else: st.info(t["sb_auth_status"]) st.session_state.user_subscribed = st.toggle(t["sb_pro_toggle"], value=st.session_state.user_subscribed) st.session_state.quotes_count = st.number_input(t["sb_quotes_count"], min_value=0, max_value=20, value=st.session_state.quotes_count) st.markdown(f"### {t['sb_demo_section']}") if st.button(t["sb_import_demo"], use_container_width=True): st.session_state.db_team = [ {"id": 1, "jmeno": "Šimon (Director)", "sazba": 45.0}, {"id": 2, "jmeno": "Nikola (Prezentace & Podpora)", "sazba": 30.0}, {"id": 3, "jmeno": "Matěj (Technik)", "sazba": 25.0} ] st.session_state.db_customers = [ {"id": 1, "jmeno": "Jan Novák", "firma": "Stavebniny s.r.o.", "telefon": "+420 777 123 456"}, {"id": 2, "jmeno": "Marie Podlahová", "firma": "Bytové družstvo", "telefon": "+420 602 987 654"}, {"id": 3, "jmeno": "Zdeněk Urban", "firma": "Instalatérství Urban", "telefon": "+420 731 555 999"} ] st.session_state.db_quotes = [ { "id": 1, "zakaznik": "Jan Novák", "popis": "Kompletní instalace rozvodů vody a topení v novostavbě", "celkem": 1250.0, "typ": "fixni", "datum": "2026-06-05", "labor_eur": 750.0, "extra_costs": [ {"kategorie": "Materiál", "nazev": "Měděné potrubí a fitinky", "eur": 400.0}, {"kategorie": "Benzín / Doprava", "nazev": "Doprava techniky a závoz materiálu", "eur": 100.0} ] } ] st.session_state.db_invoices = [ {"id": 1, "cislo": "20260001", "zakaznik": "Jan Novák", "castka": 1250.00, "stav": "Zaplaceno", "datum": "2026-06-05"}, {"id": 2, "cislo": "20260002", "zakaznik": "Marie Podlahová", "castka": 1580.50, "stav": "Odesláno", "datum": "2026-06-10"}, {"id": 3, "cislo": "20260003", "zakaznik": "Zdeněk Urban", "castka": 290.00, "stav": "Zaplaceno", "datum": "2026-06-12"}, {"id": 4, "cislo": "20260004", "zakaznik": "Jan Novák", "castka": 5010.00, "stav": "Odesláno", "datum": "2026-06-15"} ] st.session_state.quotes_count = 1 st.toast(t["toast_demo_ok"], icon="📥") time.sleep(0.3) st.rerun() if st.button(t["sb_clear_demo"], use_container_width=True): st.session_state.db_team = [] st.session_state.db_customers = [] st.session_state.db_quotes = [] st.session_state.db_invoices = [] st.session_state.quotes_count = 0 st.session_state.cost_items_df = pd.DataFrame([{"Sekce nákladu": "Materiál", "Název položky / Popis": "", "Částka": 0.0}]) st.toast(t["toast_clear_ok"], icon="🗑️") time.sleep(0.3) st.rerun() st.markdown("---") if st.button(t["sb_logout"], use_container_width=True): st.session_state.admin_authenticated = False st.rerun() st.title(t["title"]) st.caption(f"{t['caption']} | Engine Verze: {VERSION}") is_locked = st.session_state.quotes_count >= 3 and not st.session_state.user_subscribed tab_dashboard, tab_generator, tab_team, tab_crm, tab_fakturace, tab_pro_features = st.tabs([ t["tab_dash"], t["tab_gen"], t["tab_team"], t["tab_crm"], t["tab_inv"], t["tab_pro"] ]) # ----------------------------------------------------------------------------- # TAB 1: DASHBOARD & METRIKY # ----------------------------------------------------------------------------- with tab_dashboard: st.header(t["tab_dash"]) total_revenue = sum(inv["castka"] for inv in st.session_state.db_invoices if inv["stav"] == "Zaplaceno") pending_revenue = sum(inv["castka"] for inv in st.session_state.db_invoices if inv["stav"] == "Odesláno") potencial = sum(q["celkem"] for q in st.session_state.db_quotes if q.get("typ") == "fixni") c1, c2, c3, c4 = st.columns(4) c1.metric(t["profit"], format_price(total_revenue)) c2.metric(t["pending"], format_price(pending_revenue)) c3.metric(t["potential"], format_price(potencial), help=t["dash_help_potential"]) c4.metric(t["limit"], f"{st.session_state.quotes_count} / 3", delta=t["dash_limit_warn"] if is_locked else None) st.markdown("---") st.subheader(t["dash_analysis_title"]) st.write(t["dash_analysis_desc"]) flat_costs = [] for q in st.session_state.db_quotes: if q.get("labor_eur", 0) > 0: flat_costs.append({ t["col_project"]: f"{q['zakaznik']} ({q['datum']})", t["col_section"]: "Výplaty zaměstnancům" if st.session_state.lang != "EN" else "Employee Wages", t["col_item_name"]: "Mzda týmu na projektu (odpracované hodiny)" if st.session_state.lang != "EN" else "Team payroll on project (hours worked)", "Částka (Základ v EUR)": q["labor_eur"], t["col_type"]: "🎯 Přesná fixní" if q["typ"] == "fixni" else "🔮 Orientační odhad" }) for ec in q.get("extra_costs", []): flat_costs.append({ t["col_project"]: f"{q['zakaznik']} ({q['datum']})", t["col_section"]: ec["kategorie"], t["col_item_name"]: ec["nazev"] if ec["nazev"] else "Přímý výdaj", "Částka (Základ v EUR)": ec["eur"], t["col_type"]: "🎯 Přesná fixní" if q["typ"] == "fixni" else "🔮 Orientační odhad" }) if flat_costs: df_costs = pd.DataFrame(flat_costs) f_col1, f_col2 = st.columns(2) with f_col1: available_cats = [t["dash_filter_all_cats"]] + list(df_costs[t["col_section"]].unique()) selected_cat_filter = st.selectbox(t["dash_filter_cat"], available_cats) with f_col2: selected_type_filter = st.radio(t["dash_filter_type"], [t["dash_type_all"], t["dash_type_fixed"], t["dash_type_estimated"]], horizontal=True) if selected_cat_filter != t["dash_filter_all_cats"]: df_costs = df_costs[df_costs[t["col_section"]] == selected_cat_filter] if selected_type_filter == t["dash_type_fixed"]: df_costs = df_costs[df_costs[t["col_type"]] == "🎯 Přesná fixní"] elif selected_type_filter == t["dash_type_estimated"]: df_costs = df_costs[df_costs[t["col_type"]] == "🔮 Orientační odhad"] if not df_costs.empty: total_filtered_eur = df_costs["Částka (Základ v EUR)"].sum() st.markdown(f"#### {t['dash_aggregated_spend']} **{format_price(total_filtered_eur)}**") df_costs_display = df_costs.copy() df_costs_display[t["col_amount"]] = df_costs_display["Částka (Základ v EUR)"].apply(format_price) df_costs_display = df_costs_display.drop(columns=["Částka (Základ v EUR)"]) st.dataframe(df_costs_display, use_container_width=True, hide_index=True) else: st.info(t["dash_no_records"]) else: st.info(t["dash_no_costs_yet"]) st.markdown("---") st.subheader(t["dash_book_title"]) if st.session_state.db_quotes: df_raw = pd.DataFrame(st.session_state.db_quotes) df_display = df_raw.copy() # Přejmenování klíčů a lokalizace pro tabulku df_display[t["col_date"]] = df_display["datum"] df_display[t["col_project"]] = df_display["zakaznik"] df_display[t["col_item_name"]] = df_display["popis"] df_display[t["col_amount"]] = df_display["celkem"].apply(format_price) df_display[t["col_type"]] = df_display["typ"].map({"odhad": "🔮 " + t["dash_type_estimated"], "fixni": "🎯 " + t["dash_type_fixed"]}) cols_to_show = [t["col_date"], t["col_project"], t["col_type"], t["col_item_name"], t["col_amount"]] st.dataframe(df_display[cols_to_show], use_container_width=True, hide_index=True) else: st.info(t["dash_book_empty"]) # ----------------------------------------------------------------------------- # TAB 2: AI GENERÁTOR NABÍDEK (OPRAVENÝ & S PŘELOŽENÝM SOUBOREM NÁKLADŮ) # ----------------------------------------------------------------------------- with tab_generator: st.header(t["tab_gen"]) if is_locked: st.error(t["paywall_msg"]) else: quote_type = st.radio(t["gen_type_label"], [t["gen_type_est"], t["gen_type_fix"]]) is_fixed = t["gen_type_fix"] in quote_type st.markdown(t["gen_client_section"]) cust_names = [c["jmeno"] for c in st.session_state.db_customers] client_mode = st.radio(t["gen_client_mode"], [t["gen_client_crm"], t["gen_client_manual"]], horizontal=True) if client_mode == t["gen_client_crm"] and cust_names: selected_cust = st.selectbox(t["gen_select_client"], cust_names) else: selected_cust = st.text_input(t["gen_input_client_name"], value="", placeholder=t["gen_client_placeholder"]) input_task = st.text_area(t["gen_spec_label"], value=t["gen_spec_default"]) st.markdown(t["gen_team_alloc"]) if not st.session_state.db_team: st.info(t["gen_no_team_warn"]) team_hours = {} else: if st.button(t["gen_ai_btn"]): st.session_state["ai_mock_active"] = True st.session_state.cost_items_df = pd.DataFrame([ {"Sekce nákladu": "Materiál", "Název položky / Popis": "AI Odhad: Potrubí, ventily a fitinky", "Částka": 480.0}, {"Sekce nákladu": "Benzín / Doprava", "Název položky / Popis": "AI Odhad: Nákladní závoz materiálu", "Částka": 65.0}, {"Sekce nákladu": "Pronájem vybavení", "Název položky / Popis": "AI Odhad: Zapůjčení tlakové zkoušečky", "Částka": 110.0} ]) st.toast(t["gen_ai_toast"], icon="🪄") team_hours = {} cols_team = st.columns(len(st.session_state.db_team)) for idx, member in enumerate(st.session_state.db_team): with cols_team[idx]: default_val = 0 if st.session_state.get("ai_mock_active", False): if "director" in member["jmeno"].lower(): default_val = 10 elif "prezentace" in member["jmeno"].lower(): default_val = 6 else: default_val = 24 hours_input = st.number_input( f"{member['jmeno']} ({format_price(member['sazba'])}/h)", min_value=0, max_value=500, value=default_val, key=f"gen_hours_{member['id']}" ) team_hours[member["id"]] = hours_input st.markdown(t["gen_direct_costs"]) st.caption(t["gen_table_hint"]) curr_rate = CURRENCIES[st.session_state.curr]["rate"] # Úprava konfigurace tabulky - odstraněna měna ze záhlaví sloupce (pouze t["col_amount"]) column_configuration = { "Sekce nákladu": st.column_config.SelectboxColumn( t["col_section"], 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"], required=True ), "Název položky / Popis": st.column_config.TextColumn(t["col_item_name"], required=True), "Částka": st.column_config.NumberColumn(t["col_amount"], min_value=0.0, required=True, format="%.2f") } edited_df = st.data_editor( st.session_state.cost_items_df, column_config=column_configuration, num_rows="dynamic", use_container_width=True, key="main_cost_data_editor" ) st.session_state.cost_items_df = edited_df st.markdown(t["gen_csv_title"]) def zpracuj_csv_import(): if st.session_state.csv_uploader_key is not None: try: uploaded_df = pd.read_csv(st.session_state.csv_uploader_key) needed_headers = ["Sekce nákladu", "Název položky / Popis", "Částka"] if all(head in uploaded_df.columns for head in needed_headers): sanitized_df = uploaded_df[needed_headers].copy() sanitized_df["Částka"] = pd.to_numeric(sanitized_df["Částka"], errors='coerce').fillna(0.0) st.session_state.cost_items_df = pd.concat([st.session_state.cost_items_df, sanitized_df], ignore_index=True) st.toast(t["gen_csv_success"], icon="📥") else: st.error(t["gen_csv_err_headers"]) except Exception as e: st.error(f"{t['gen_csv_err_read']} {e}") st.file_uploader( t["gen_csv_uploader"], type=["csv"], key="csv_uploader_key", on_change=zprocessed_csv_import if 'zprocessed_csv_import' in locals() else zpracuj_csv_import ) st.markdown("---") total_labor_eur = 0.0 for member in st.session_state.db_team: total_labor_eur += team_hours.get(member["id"], 0) * member["sazba"] total_extra_local = edited_df["Částka"].sum() if not edited_df.empty else 0.0 total_extra_eur = total_extra_local / curr_rate total_project_eur = total_labor_eur + total_extra_eur st.markdown(f"{t['gen_total_calc']} **{format_price(total_project_eur)}**") if st.button(t["gen_save_btn"], type="primary"): if not selected_cust or selected_cust.strip() == "": st.error(t["gen_err_no_client"]) else: if selected_cust not in cust_names: st.session_state.db_customers.append({ "id": len(st.session_state.db_customers) + 1, "jmeno": selected_cust.strip(), "firma": "Fyzická osoba" if st.session_state.lang != "EN" else "Individual Client", "telefon": "Nezadáno" if st.session_state.lang != "EN" else "N/A" }) saved_extra_costs = [] for _, row in edited_df.iterrows(): saved_extra_costs.append({ "kategorie": row["Sekce nákladu"], "nazev": row["Název položky / Popis"], "eur": float(row["Částka"]) / curr_rate }) new_quote = { "id": len(st.session_state.db_quotes) + 1, "zakaznik": selected_cust.strip(), "popis": input_task, "celkem": total_project_eur, "typ": "fixni" if is_fixed else "odhad", "datum": time.strftime("%Y-%m-%d"), "labor_eur": total_labor_eur, "extra_costs": saved_extra_costs } st.session_state.db_quotes.append(new_quote) st.session_state.quotes_count += 1 st.session_state["ai_mock_active"] = False st.session_state.cost_items_df = pd.DataFrame([{"Sekce nákladu": "Materiál", "Název položky / Popis": "", "Částka": 0.0}]) st.success(t["gen_save_success"]) time.sleep(0.4) st.rerun() # ----------------------------------------------------------------------------- # TAB 3: SPRÁVA TÝMU # ----------------------------------------------------------------------------- with tab_team: st.header(t["team_title"]) st.write(t["team_desc"]) t_col1, t_col2 = st.columns([2, 1]) with t_col1: st.markdown(f"### {t['team_sub_list']}") team_display_list = [] for m in st.session_state.db_team: team_display_list.append({ t["team_col_id"]: m["id"], t["team_col_role"]: m["jmeno"], t["team_col_rate"]: format_price(m["sazba"]) }) if team_display_list: st.dataframe(pd.DataFrame(team_display_list), use_container_width=True, hide_index=True) else: st.info(t["team_empty"]) st.markdown(t["team_remove_section"]) if st.session_state.db_team: member_to_remove = st.selectbox(t["team_remove_select"], [m["jmeno"] for m in st.session_state.db_team]) if st.button(t["team_remove_btn"]): st.session_state.db_team = [m for m in st.session_state.db_team if m["jmeno"] != member_to_remove] st.success(f"{t['team_remove_toast']} ({member_to_remove})") time.sleep(0.3) st.rerun() with t_col2: st.markdown(t["team_add_section"]) with st.form("team_management_form"): new_tm_name = st.text_input(t["team_input_name"], placeholder=t["team_input_placeholder"]) new_tm_rate = st.number_input(t["team_input_rate"], min_value=1, value=400 if st.session_state.curr == "CZK" else 20) if st.form_submit_button(t["team_submit_btn"]): if new_tm_name: rate_in_eur = new_tm_rate / CURRENCIES[st.session_state.curr]["rate"] matched_member = next((m for m in st.session_state.db_team if m["jmeno"].lower() == new_tm_name.lower()), None) if matched_member: matched_member["sazba"] = rate_in_eur st.success(t["team_update_toast"]) else: next_id = max([m["id"] for m in st.session_state.db_team]) + 1 if st.session_state.db_team else 1 st.session_state.db_team.append({"id": next_id, "jmeno": new_tm_name, "sazba": rate_in_eur}) st.success(t["team_add_toast"]) time.sleep(0.3) st.rerun() # ----------------------------------------------------------------------------- # TAB 4: CRM ADRESÁŘ # ----------------------------------------------------------------------------- with tab_crm: st.header(t["crm_title"]) col_crm1, col_crm2 = st.columns([2, 1]) with col_crm1: if st.session_state.db_customers: df_crm = pd.DataFrame(st.session_state.db_customers) df_crm_display = pd.DataFrame({ t["cust_name"]: df_crm["jmeno"], t["company"]: df_crm["firma"], t["phone"]: df_crm["telefon"] }) st.dataframe(df_crm_display, use_container_width=True, hide_index=True) else: st.info(t["crm_empty"]) with col_crm2: st.markdown(t["crm_add_section"]) with st.form("manual_crm_form"): c_n = st.text_input(t["cust_name"], placeholder="Jan Novák") c_f = st.text_input(t["company"], placeholder="Stavebniny s.r.o.") c_p = st.text_input(t["phone"], placeholder="+420 777 888 999") if st.form_submit_button(t["save_cust"]): if c_n: st.session_state.db_customers.append({ "id": len(st.session_state.db_customers) + 1, "jmeno": c_n, "firma": c_f if c_f else ("Fyzická osoba" if st.session_state.lang != "EN" else "Individual"), "telefon": c_p }) st.success(t["crm_success_toast"]) time.sleep(0.3) st.rerun() else: st.error(t["crm_err_name"]) # ----------------------------------------------------------------------------- # TAB 5: FAKTURACE # ----------------------------------------------------------------------------- with tab_fakturace: st.header(t["inv_title"]) only_fixni_quotes = [q for q in st.session_state.db_quotes if q.get("typ") == "fixni"] if only_fixni_quotes: invoice_options = {f"ID: {q['id']} | {q['zakaznik']} — {format_price(q['celkem'])}": q for q in only_fixni_quotes} selected_quote_to_inv = st.selectbox(t["inv_select_quote"], list(invoice_options.keys())) targeted_quote = invoice_options[selected_quote_to_inv] if st.button(t["invoice_btn"], type="primary", use_container_width=True): st.session_state.db_invoices.append({ "id": len(st.session_state.db_invoices) + 1, "cislo": f"2026{len(st.session_state.db_invoices)+1:04d}", "zakaznik": targeted_quote["zakaznik"], "castka": targeted_quote["celkem"], "stav": "Odesláno", "datum": time.strftime("%Y-%m-%d") }) st.success(t["inv_success_toast"]) time.sleep(0.3) st.rerun() else: st.info(t["inv_no_fixed_quotes"]) st.markdown("---") st.subheader(t["inv_book_title"]) if st.session_state.db_invoices: for index_inv, invoice_obj in enumerate(st.session_state.db_invoices): div_c1, div_c2, div_c3 = st.columns([2, 1, 1]) div_c1.write(f"🧾 **{t['col_amount']} č. {invoice_obj['cislo']}** — {invoice_obj['zakaznik']} [**{format_price(invoice_obj['castka'])}**]") if invoice_obj["stav"] == "Zaplaceno": div_c2.markdown(f"{t['inv_status_paid']}", unsafe_allow_html=True) div_c3.write("") else: div_c2.markdown(f"{t['inv_status_sent']}", unsafe_allow_html=True) if div_c3.button(t["inv_mark_paid"], key=f"payment_trigger_key_{index_inv}", use_container_width=True): invoice_obj["stav"] = "Zaplaceno" st.rerun() else: st.info(t["inv_empty"]) # ----------------------------------------------------------------------------- # TAB 6: PRO BONUSY # ----------------------------------------------------------------------------- with tab_pro_features: st.header(t["tab_pro"]) if not st.session_state.user_subscribed: st.warning(t["pro_locked_warn"]) else: pro_sub1, pro_sub2 = st.tabs([t["pro_router"], t["pro_sms"]]) with pro_sub1: st.subheader(t["pro_route_title"]) st.text_area(t["pro_route_label"], value="Plzeňská 15, Praha\nPrůmyslová 4, Beroun\nSukova 12, Plzeň") if st.button(t["pro_route_btn"], use_container_width=True): st.info(t["pro_route_result"]) with pro_sub2: st.subheader(t["pro_sms_title"]) st.markdown(t["pro_sms_step1"]) open_debts = [invoice for invoice in st.session_state.db_invoices if invoice["stav"] == "Odesláno"] search_col1, search_col2 = st.columns([1, 1]) with search_col1: query_name = st.text_input(t["pro_sms_filter_name"], value="") with search_col2: sorting_order = st.selectbox(t["pro_sms_sort_label"], [t["pro_sms_sort_high"], t["pro_sms_sort_low"]]) if query_name: open_debts = [d for d in open_debts if query_name.lower() in d["zakaznik"].lower()] if sorting_order == t["pro_sms_sort_high"]: open_debts.sort(key=lambda x: x["castka"], reverse=True) else: open_debts.sort(key=lambda x: x["castka"]) if not open_debts: st.info(t["pro_sms_no_debts"]) active_debtor = None else: debtor_mapping = {f"👤 {d['zakaznik']} (Faktura č. {d['cislo']} | Dluh: {format_price(d['castka'])})": d for d in open_debts} selected_debtor_string = st.selectbox(t["pro_sms_select_debtor"], list(debtor_mapping.keys())) active_debtor = debtor_mapping[selected_debtor_string] st.markdown("---") st.markdown(t["pro_sms_step2"]) preset_labels = [p["název"] for p in st.session_state.sms_presets] chosen_preset_label = st.selectbox(t["pro_sms_template_label"], preset_labels) active_preset = next(p for p in st.session_state.sms_presets if p["název"] == chosen_preset_label) with st.expander(t["pro_sms_expander"]): st.markdown(t["pro_sms_ai_assistant"]) prompt_text = st.text_input(t["pro_sms_ai_prompt"], placeholder="...") if st.button(t["pro_sms_ai_btn"]): if prompt_text: p_low = prompt_text.lower() if "předžal" in p_low or "naléh" in p_low or "soud" in p_low: 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." else: result_sms = "Dobrý den {jmeno}, připomínáme zapomenutou fakturu č. {cislo} na částku {castka}. Prosíme o co nejrychlejší proplacení." st.session_state["ai_sms_output_buffer"] = result_sms st.success(t["pro_sms_ai_success"]) new_preset_title = st.text_input(t["pro_sms_new_title"], value="") default_body = st.session_state.get("ai_sms_output_buffer", "Vážený pane/paní {jmeno}, ...") new_preset_body = st.text_area(t["pro_sms_new_body"], value=default_body) if st.button(t["pro_sms_save_template_btn"]): if new_preset_title and new_preset_body: st.session_state.sms_presets.append({"název": new_preset_title, "text": new_preset_body}) st.success(t["pro_sms_template_success"]) time.sleep(0.3) st.rerun() st.markdown("---") st.markdown(t["pro_sms_step3"]) if active_debtor: raw_template = active_preset["text"] final_compiled_sms = raw_template.replace("{jmeno}", active_debtor["zakaznik"])\ .replace("{cislo}", active_debtor["cislo"])\ .replace("{castka}", format_price(active_debtor["castka"])) st.markdown(f"""
{final_compiled_sms}
""", unsafe_allow_html=True) safe_encoded_sms = urllib.parse.quote(final_compiled_sms) copy_button_html = f""" """ components.html(copy_button_html, height=50) else: st.info(t["pro_sms_select_info"])