#include "istakip_penceresi.hpp" #include #include #include #include #include #include #include #include #include #include #include "dosya_islemleri.hpp" // yolAc — iş emri "Belge" alanından model açma #include "fiko/istakip/istemci.hpp" #include "fiko/ui/bilesen_ek.hpp" // toastGoster #include "fiko/ui/form.hpp" // bilgiSeridi #include "fiko/ui/pencere_cerceve.hpp" #include "fiko/ui/tema.hpp" #include "imgui.h" #include "istakip_filtre.hpp" #include "istakip_gantt.hpp" #include "istakip_gorunum.hpp" #include "istakip_grafik.hpp" #include "istakip_kayit_penceresi.hpp" // Kayıt penceresi (Alanlar/Ekler/Olaylar) — inline form buraya taşındı #include "istakip_sema.hpp" #include "istakip_sema_penceresi.hpp" #include "istakip_servis.hpp" #include "simdjson.h" #include "uygulama_baglam.hpp" namespace fiko { namespace { using istakip::IsKayit; // ── Sekme şemaları — artık "_sema" kayıtlarından okunur (istakip_sema.hpp, F1) ─ // AlanSema/TurSema + turlarOku() önbellekli okuyucu orada; koda-sabit tür // listesi burada kalmadı ("_sema" kaydı hiç yoksa turlarOku() geriye dönük // uyumlu varsayılanları aynı dosyadan döner — davranış AYNI, kaynak değişti). // ── Pencere durumu (dosya-static — zeka/malzeme kalıbı) ────────────────────── bool g_acik = false; bool g_pin = false; enum class Gorunum { Tablo, Kanban, Zaman, Iliski }; // F5: Zaman/İlişki eklendi Gorunum g_gorunum = Gorunum::Tablo; int g_sekme = 1; // açılışta Teklifler char g_filtre[64] = {}; u64 g_listSurum = ~0ull; // liste önbelleği damgası int g_listSekme = -1; std::string g_listFiltre; u64 g_listFiltreSurum = ~0ull; // yapılı filtre damgası (g_filtreSurum ile karşılaştırılır) std::vector g_liste; // süzülmüş, sıralı görünüm // Form durumu artık istakip_kayit_penceresi.cpp'de. u64 g_seciliKimlik = 0; // seçili satır (gösterim + Kayıt penceresine iletilir) std::string g_bekleyenAcYolu; // kraldan getirilen belge inince otomatik açılır char g_komutMetni[64] = {}; // F4: Ctrl+K komut çubuğu arama metni // ── Dalga 2: Query/Filter Engine (plan §11) + Saved Views (plan §12) ───────── // Tür başına yapılı filtre (metin arama g_filtre'den AYRI — ikisi de uygulanır, // VE ilişkisiyle). g_filtreSurum: filtre değiştiğinde liste önbelleğini geçersiz // kılmak için (listeKur, veri sürümüyle aynı desende karşılaştırır). std::unordered_map g_filtreler; u64 g_filtreSurum = 0; FiltreGrup g_filtreTaslak; // "Filtrele" popup'ında düzenlenen kopya char g_gorunumAdTaslak[64] = {}; // "Görünüm Kaydet" popup'ı FiltreGrup& aktifFiltre(const std::string& tur_id) { return g_filtreler[tur_id]; } const char* gorunumKodu(Gorunum g) { switch (g) { case Gorunum::Tablo: return "tablo"; case Gorunum::Kanban: return "kanban"; case Gorunum::Zaman: return "zaman"; case Gorunum::Iliski: return "iliski"; } return "tablo"; } Gorunum gorunumCoz(const std::string& kod) { if (kod == "kanban") return Gorunum::Kanban; if (kod == "zaman") return Gorunum::Zaman; if (kod == "iliski") return Gorunum::Iliski; return Gorunum::Tablo; } const char* alanDeger(const IsKayit& k, const char* ad) { const char* v = istakip::alanBul(k, ad); return v ? v : ""; } void formYukle(const TurSema& t, const IsKayit* k) { // Kayıt penceresine yönlendir (inline form istakip_kayit_penceresi.cpp'ye // taşındı). k=nullptr → yeni kayıt, dolu → düzenleme. istakipKayitPencereAc(t.tur_id.c_str(), k ? k->kimlik : 0); } bool filtreGecer(const IsKayit& k, const std::string& f) { if (f.empty()) return true; for (const auto& [a, d] : k.alanlar) { auto icinde = [&](const std::string& s) { return std::search(s.begin(), s.end(), f.begin(), f.end(), [](char c1, char c2) { return std::tolower((unsigned char)c1) == std::tolower((unsigned char)c2); }) != s.end(); }; if (icinde(d)) return true; (void)a; } return false; } void listeKur(const istakip::IsIstemci& veri, const TurSema& t) { g_liste = veri.turListe(t.tur_id); const std::string f = g_filtre; const FiltreGrup& yf = aktifFiltre(t.tur_id); if (!f.empty() || !yf.bosMu()) { g_liste.erase(std::remove_if(g_liste.begin(), g_liste.end(), [&](const IsKayit* k) { if (!f.empty() && !filtreGecer(*k, f)) return true; if (!yf.bosMu() && !filtreGecerMi(yf, *k)) return true; return false; }), g_liste.end()); } std::sort(g_liste.begin(), g_liste.end(), [](const IsKayit* a, const IsKayit* b) { return a->kimlik > b->kimlik; }); g_listSurum = veri.sonSurum(); g_listSekme = g_sekme; g_listFiltre = f; g_listFiltreSurum = g_filtreSurum; } // Dönüşüm: kaynak kaydın alanlarını yeni türe kopyala + bağ alanları. void donustur(const IsKayit& kaynak, const char* hedefTur, const char* ilkDurum, const char* bagAlan) { IsKayit y; y.tur = hedefTur; for (const auto& [a, d] : kaynak.alanlar) if (std::string(a) != "durum") istakip::alanAta(y, a, d); istakip::alanAta(y, bagAlan, std::to_string(kaynak.kimlik)); istakip::alanAta(y, "durum", ilkDurum); if (g_istakipServis.yaz(y) == 0) { ui::toastGoster("Dönüştürme yazılamadı — bağlantı/yetki kontrol edin", ui::ToastTip::Uyari); } } // ── CSV dışa aktarım (TR Excel dostu: ; ayraç + UTF-8 BOM) ─────────────────── std::string csvKacir(const std::string& s) { if (s.find_first_of(";\"\n") == std::string::npos) return s; std::string c = "\""; for (char ch : s) { if (ch == '"') c += '"'; c += ch; } return c + "\""; } void csvDisaAktar(const TurSema& t) { wchar_t yol[MAX_PATH] = {}; OPENFILENAMEW ofn{}; ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = GetActiveWindow(); ofn.lpstrFilter = L"CSV dosyası (*.csv)\0*.csv\0"; ofn.lpstrFile = yol; ofn.nMaxFile = MAX_PATH; ofn.lpstrDefExt = L"csv"; ofn.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST; if (!GetSaveFileNameW(&ofn)) return; std::string icerik = "\xEF\xBB\xBF"; // BOM: Excel Türkçe karakterleri doğru açar icerik += "No"; for (const auto& a : t.alanlar) icerik += ";" + std::string(a.etiket); if (!t.durumlar.empty()) icerik += ";" + t.kanban_baslik; icerik += "\n"; for (const IsKayit* k : g_liste) { icerik += std::to_string(k->kimlik); for (const auto& a : t.alanlar) icerik += ";" + csvKacir(alanDeger(*k, a.ad.c_str())); if (!t.durumlar.empty()) icerik += ";" + csvKacir(alanDeger(*k, t.kanban_alan.c_str())); icerik += "\n"; } std::ofstream f(std::wstring(yol), std::ios::binary | std::ios::trunc); if (f && f.write(icerik.data(), (std::streamsize)icerik.size())) ui::toastGoster("CSV dışa aktarıldı", ui::ToastTip::Basari); else ui::toastGoster("CSV yazılamadı — yol/izin kontrol edin", ui::ToastTip::Hata); } void durumBadge(const TurSema& t, const IsKayit& k, ImVec4* arkaRenk = nullptr) { const char* d = alanDeger(k, t.kanban_alan.c_str()); if (!d[0]) { ImGui::TextDisabled("-"); return; } // Durum sırasına göre seviye: ilk=vurgu, son=başarı, ortalar=nötr. int seviye = 1; if (!t.durumlar.empty()) { int idx = -1; for (size_t i = 0; i < t.durumlar.size(); ++i) if (d == t.durumlar[i]) { idx = (int)i; break; } if (idx == (int)t.durumlar.size() - 1) seviye = 2; // son kolon → başarı } if (std::string(d) == "reddedildi") seviye = 4; else if (std::string(d) == "onaylandı" || std::string(d) == "tamamlandı") seviye = 2; const ui::Tema& tema = ui::tema(); ImVec4 renk = tema.vurgu; switch (seviye) { case 2: renk = tema.basari; break; case 3: renk = tema.uyari; break; case 4: renk = tema.hata; break; default: renk = tema.vurgu; break; } if (arkaRenk) *arkaRenk = ui::karisim(tema.panel, renk, 0.28f); const ImVec4 orj = ImGui::GetStyle().Colors[ImGuiCol_Button]; ImGui::GetStyle().Colors[ImGuiCol_Button] = arkaRenk ? *arkaRenk : renk; ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered] = ui::karisim(arkaRenk ? *arkaRenk : renk, renk, 0.35f); ImGui::GetStyle().Colors[ImGuiCol_ButtonActive] = renk; ImGui::SmallButton(d); ImGui::GetStyle().Colors[ImGuiCol_Button] = orj; ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered] = orj; ImGui::GetStyle().Colors[ImGuiCol_ButtonActive] = orj; } void tabloCiz(UygulamaBaglam&, const TurSema& t) { // Sütunlar: tablodaki alanlar + durum + kimlik. std::vector sutunlar; for (const auto& a : t.alanlar) if (a.tabloda) sutunlar.push_back(&a); const int nSutun = (int)sutunlar.size() + (t.durumlar.empty() ? 0 : 1) + 1; if (!ImGui::BeginTable("##istakip_tablo", nSutun, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_ScrollY | ImGuiTableFlags_Resizable, {0, ImGui::GetContentRegionAvail().y * 0.55f})) return; ImGui::TableSetupScrollFreeze(0, 1); ImGui::TableSetupColumn("No", ImGuiTableColumnFlags_WidthFixed, 44); for (const AlanSema* a : sutunlar) ImGui::TableSetupColumn(a->etiket.c_str()); if (!t.durumlar.empty()) ImGui::TableSetupColumn(t.kanban_baslik.c_str(), ImGuiTableColumnFlags_WidthFixed, 92); ImGui::TableHeadersRow(); ImGuiListClipper clipper; clipper.Begin((int)g_liste.size()); while (clipper.Step()) { for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; ++i) { const IsKayit& k = *g_liste[(size_t)i]; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); char no[24]; std::snprintf(no, sizeof(no), "%llu", (unsigned long long)k.kimlik); ImGui::PushID((int)k.kimlik); bool secili = g_seciliKimlik == k.kimlik; if (ImGui::Selectable(no, secili, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowDoubleClick)) formYukle(t, &k); if (ImGui::BeginPopupContextItem("##satirMenu")) { // sağ-tık niyet menüsü if (ImGui::MenuItem("✏ Düzenle")) formYukle(t, &k); if (t.tur_id == "teklif" && ImGui::MenuItem("📦 Siparişe Dönüştür")) { donustur(k, "siparis", "yeni", istakip::ALAN_KAYNAK); IsKayit g = k; istakip::alanAta(g, "durum", "onaylandı"); g_istakipServis.yaz(g, k.surum); } if (t.tur_id == "siparis" && ImGui::MenuItem("🛠 İş Emri Oluştur")) donustur(k, "is_emri", "bekliyor", istakip::ALAN_UST); ImGui::Separator(); if (ImGui::MenuItem("🗑 Sil")) { IsKayit sil = k; sil.silindi = true; g_istakipServis.yaz(sil, k.surum); if (g_seciliKimlik == k.kimlik) { g_seciliKimlik = 0; } } ImGui::EndPopup(); } ImGui::PopID(); int s = 1; for (const AlanSema* a : sutunlar) { ImGui::TableSetColumnIndex(s++); ImGui::TextUnformatted(alanDeger(k, a->ad.c_str())); } if (!t.durumlar.empty()) { ImGui::TableSetColumnIndex(s); durumBadge(t, k); } } } ImGui::EndTable(); } // ── Kanban pano: durum kolonları + kart + ◀▶ durum taşıma ──────────────────── void kartTasi(const TurSema& t, const IsKayit& k, int hedefDurum) { IsKayit g = k; istakip::alanAta(g, t.kanban_alan, t.durumlar[(size_t)hedefDurum]); if (g_istakipServis.yaz(g, k.surum) == 0) ui::toastGoster("Taşınamadı — bağlantı/yetki/çakışma", ui::ToastTip::Uyari); } struct KartYuk { // sürükle-bırak yükü (sürüm bırakma anında önbellekten tazelenir) u64 kimlik; }; void panoCiz(const TurSema& t) { const int n = (int)t.durumlar.size(); if (n == 0) return; const float aralik = 6.0f; const float genis = (ImGui::GetContentRegionAvail().x - aralik * (float)(n - 1)) / (float)n; const float yuk = ImGui::GetContentRegionAvail().y * 0.55f; // Kolon başına kayıtlar (g_liste zaten süzülü/sıralı — tek geçiş). std::vector> kolonlar((size_t)n); for (const IsKayit* k : g_liste) { const char* d = alanDeger(*k, t.kanban_alan.c_str()); int hedef = 0; // durumsuz/yabancı durum → ilk kolon for (int i = 0; i < n; ++i) if (d == t.durumlar[(size_t)i]) { hedef = i; break; } kolonlar[(size_t)hedef].push_back(k); } for (int d = 0; d < n; ++d) { if (d) ImGui::SameLine(0, aralik); ImGui::BeginGroup(); ImGui::TextColored(ui::tema().vurgu, "%s", t.durumlar[(size_t)d].c_str()); ImGui::SameLine(); ImGui::TextDisabled("(%d)", (int)kolonlar[(size_t)d].size()); ImGui::PushID(d); ImGui::BeginChild("##kolon", {genis, yuk}, ImGuiChildFlags_Borders); for (const IsKayit* kp : kolonlar[(size_t)d]) { const IsKayit& k = *kp; ImGui::PushID((int)k.kimlik); ImGui::BeginChild("##kart", {0, 68}, ImGuiChildFlags_Borders); const char* baslik = alanDeger(k, t.alanlar[0].ad.c_str()); if (ImGui::Selectable(baslik[0] ? baslik : "(başlıksız)", g_seciliKimlik == k.kimlik)) formYukle(t, &k); if (ImGui::BeginDragDropSource()) { // kartı kolonlar arası sürükle const KartYuk yuk{k.kimlik}; ImGui::SetDragDropPayload("ISTAKIP_KART", &yuk, sizeof(yuk)); ImGui::TextUnformatted(baslik[0] ? baslik : "(başlıksız)"); ImGui::TextDisabled("%s → bırakılan kolona taşınır", alanDeger(k, t.kanban_alan.c_str())); ImGui::EndDragDropSource(); } // Alt bilgi satırı: 2. tablodaki alan + termin + ek göstergesi (F6). const char* alt = alanDeger(k, t.alanlar[1].ad.c_str()); const char* termin = alanDeger(k, "termin"); const bool ekVar = alanDeger(k, "ekler")[0] != '\0'; ImGui::TextDisabled("%s%s%s%s", alt, (alt[0] && termin[0]) ? " · " : "", termin, ekVar ? " 📎" : ""); // Durum taşıma okları (sürükle yerine güvenli tek-tık). if (d > 0 && ImGui::SmallButton("◀")) kartTasi(t, k, d - 1); if (d > 0 && d < n - 1) ImGui::SameLine(); if (d < n - 1 && ImGui::SmallButton("▶")) kartTasi(t, k, d + 1); ImGui::EndChild(); ImGui::PopID(); } ImGui::EndChild(); if (ImGui::BeginDragDropTarget()) { // kolon = bırakma hedefi if (const ImGuiPayload* yuk = ImGui::AcceptDragDropPayload("ISTAKIP_KART")) { const u64 kimlik = ((const KartYuk*)yuk->Data)->kimlik; const istakip::IsIstemci* veri = g_istakipServis.veri(); const IsKayit* k = veri ? veri->bul(kimlik) : nullptr; if (k && alanDeger(*k, t.kanban_alan.c_str()) != t.durumlar[(size_t)d]) kartTasi(t, *k, d); // aynı kolona bırakma = no-op (boş sürüm artışı yok) } ImGui::EndDragDropTarget(); } ImGui::PopID(); ImGui::EndGroup(); } } // F6: "ekler" — her kayıtta çoklu dosya eki, şemdan bağımsız gömülü alan. // Yönetimi istakip_kayit_penceresi.cpp'deki Ekler sekmesine taşındı. void formCiz(UygulamaBaglam&, const TurSema& t) { // Inline kayıt formu artık bağımsız Kayıt penceresinde (istakip_kayit_penceresi.cpp). // Sağ panel artık salt-okunur özet + Kayıt penceresini açar. if (g_seciliKimlik == 0) { ui::bilgiSeridi(0, "Kayıt seç"); return; } const istakip::IsIstemci* veri = g_istakipServis.veri(); const IsKayit* k = veri ? veri->bul(g_seciliKimlik) : nullptr; if (!k) return; const char* baslik = alanDeger(*k, t.alanlar[0].ad.c_str()); ImGui::Text("Seçili kayıt: %s", baslik[0] ? baslik : "(başlıksız)"); ImGui::TextDisabled("No: %llu", (unsigned long long)k->kimlik); if (const char* d = alanDeger(*k, t.kanban_alan.c_str()); d[0]) ImGui::TextDisabled("%s: %s", t.kanban_baslik.c_str(), d); ImGui::Separator(); if (ImGui::Button("✏ Kayıt penceresini aç")) istakipKayitPencereAc(t.tur_id.c_str(), k->kimlik); } // ── Dalga 2: Filtre kurucu popup (plan §11) ─────────────────────────────── void filtrePopupCiz(const TurSema& t) { if (!ImGui::BeginPopup("##istakipFiltrePopup")) return; ImGui::TextDisabled("Filtre — %s", t.baslik.c_str()); ImGui::Separator(); ImGui::TextUnformatted("Koşullar arası:"); ImGui::SameLine(); if (ImGui::RadioButton("VE", g_filtreTaslak.mantik == "VE")) g_filtreTaslak.mantik = "VE"; ImGui::SameLine(); if (ImGui::RadioButton("VEYA", g_filtreTaslak.mantik == "VEYA")) g_filtreTaslak.mantik = "VEYA"; ImGui::Separator(); std::vector> alanlar; // ad, etiket for (const auto& a : t.alanlar) alanlar.emplace_back(a.ad, a.etiket); if (!t.durumlar.empty()) alanlar.emplace_back(t.kanban_alan, t.kanban_baslik); int silinecek = -1; for (size_t i = 0; i < g_filtreTaslak.kosullar.size(); ++i) { FiltreKosul& k = g_filtreTaslak.kosullar[i]; ImGui::PushID((int)i); std::string mevcutEtiket = k.alan; for (const auto& [ad, etiket] : alanlar) if (ad == k.alan) mevcutEtiket = etiket; ImGui::SetNextItemWidth(130); if (ImGui::BeginCombo("##alan", mevcutEtiket.c_str())) { for (const auto& [ad, etiket] : alanlar) if (ImGui::Selectable(etiket.c_str(), k.alan == ad)) k.alan = ad; ImGui::EndCombo(); } ImGui::SameLine(); ImGui::SetNextItemWidth(100); if (ImGui::BeginCombo("##op", filtreOpEtiket(k.op))) { for (FiltreOp op : filtreOperatorleri()) if (ImGui::Selectable(filtreOpEtiket(op), k.op == op)) k.op = op; ImGui::EndCombo(); } if (filtreOpDegerGerekir(k.op)) { ImGui::SameLine(); ImGui::SetNextItemWidth(120); char buf[128]; std::snprintf(buf, sizeof(buf), "%s", k.deger.c_str()); if (ImGui::InputText("##deger", buf, sizeof(buf))) k.deger = buf; } ImGui::SameLine(); if (ImGui::SmallButton("✕")) silinecek = (int)i; ImGui::PopID(); } if (silinecek >= 0) g_filtreTaslak.kosullar.erase(g_filtreTaslak.kosullar.begin() + silinecek); if (ImGui::SmallButton("+ Koşul") && !alanlar.empty()) g_filtreTaslak.kosullar.push_back({alanlar[0].first, FiltreOp::Esit, ""}); ImGui::Separator(); if (ImGui::Button("Uygula")) { g_filtreler[t.tur_id] = g_filtreTaslak; ++g_filtreSurum; ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Temizle")) { g_filtreTaslak = FiltreGrup{}; g_filtreler[t.tur_id] = g_filtreTaslak; ++g_filtreSurum; ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Vazgeç")) ImGui::CloseCurrentPopup(); ImGui::EndPopup(); } // ── Dalga 2: Kayıtlı Görünümler popup (plan §12) ────────────────────────── void gorunumPopupCiz(const istakip::IsIstemci& veri, const TurSema& t) { if (!ImGui::BeginPopup("##istakipGorunumPopup")) return; ImGui::TextDisabled("Kayıtlı Görünümler — %s", t.baslik.c_str()); ImGui::Separator(); const auto gorunumler = gorunumleriOku(veri, t.tur_id); if (gorunumler.empty()) ImGui::TextDisabled("Henüz kayıtlı görünüm yok"); for (const auto& g : gorunumler) { char etiket[96]; std::snprintf(etiket, sizeof(etiket), "%s (%s)", g.ad.c_str(), g.gorunum.c_str()); if (ImGui::Selectable(etiket)) { g_filtreler[t.tur_id] = g.filtre; g_gorunum = gorunumCoz(g.gorunum); ++g_filtreSurum; ImGui::CloseCurrentPopup(); } } ImGui::Separator(); ImGui::TextUnformatted("Şu anki filtre + görünümü kaydet:"); ImGui::SetNextItemWidth(200); ImGui::InputTextWithHint("##gorunumAd", "görünüm adı…", g_gorunumAdTaslak, sizeof(g_gorunumAdTaslak)); ImGui::SameLine(); if (ImGui::Button("💾 Kaydet") && g_gorunumAdTaslak[0]) { GorunumKayit yeni; yeni.tur_id = t.tur_id; yeni.ad = g_gorunumAdTaslak; yeni.gorunum = gorunumKodu(g_gorunum); yeni.filtre = aktifFiltre(t.tur_id); g_istakipServis.yaz(gorunumKayitYaz(yeni)); g_gorunumAdTaslak[0] = 0; ui::toastGoster("Görünüm kaydedildi", ui::ToastTip::Basari); ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } } // namespace void istakipPencereAc() { g_acik = true; g_listSurum = ~0ull; } void istakipPenceresiCiz(UygulamaBaglam& b) { if (!g_acik) return; ui::Pencere p{"📋 Fiko İş Takip###istakip", &g_acik, {.boyut = {640, 520}, .minBoyut = {480, 360}, .pinDurum = &g_pin, .skin = ui::PencereSkin::WebCam}}; if (!p) return; // Kraldan istenen belge indiyse otomatik aç (indirme sonucu servis olayından düşer). if (!g_bekleyenAcYolu.empty() && !g_istakipServis.dosyaBekleniyor()) { const std::string yerel = g_istakipServis.yerelYol(g_bekleyenAcYolu); g_bekleyenAcYolu.clear(); if (!yerel.empty()) yolAc(b, yerel); } // Üst şerit: bağlantı durumu + bekleyen rozeti. const bool senkron = g_istakipServis.senkronMu(); ImGui::TextColored(senkron ? ImVec4{0.35f, 0.78f, 0.42f, 1.0f} : ImVec4{0.86f, 0.36f, 0.36f, 1.0f}, senkron ? "●" : "○"); ImGui::SameLine(); ImGui::TextUnformatted(g_istakipServis.durumMetni().c_str()); const istakip::IsIstemci* veri = g_istakipServis.veri(); if (veri && !veri->bekleyenler().empty()) { ImGui::SameLine(); ImGui::TextColored(ui::tema().vurgu, "⏳ %d bekleyen yazma", (int)veri->bekleyenler().size()); } if (!g_istakipServis.aktifMi()) { ui::bilgiSeridi(1, "İş Takip kapalı — Ayarlar → İş Takip: bu makineyi Kral yap " "veya Kral adresini gir"); return; } if (!veri) return; // F1: "_sema" kaydı hiç yoksa (yeni kurulum) kral bir kere yazar — geriye dönük // uyumlu, mevcut kayıtlara dokunmaz. Her karede taramamak için tek seferlik. static bool g_ilkKurulumDenendi = false; if (!g_ilkKurulumDenendi) { g_ilkKurulumDenendi = true; ilkKurulumSemalariYaz(g_istakipServis); } // ── F4: 3 panelli kabuk (sol tür gezgini / orta liste / sağ kayıt+akış) ─── const auto& ts = turlarOku(*veri); if ((size_t)g_sekme >= ts.size()) g_sekme = 0; // Ctrl+K: tüm türler genelinde kayıt ara + zıpla (pencere odaktayken). if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_K, false)) { g_komutMetni[0] = 0; ImGui::OpenPopup("##istakipKomut"); } if (ImGui::BeginPopup("##istakipKomut")) { if (ImGui::IsWindowAppearing()) ImGui::SetKeyboardFocusHere(); ImGui::SetNextItemWidth(320); ImGui::InputTextWithHint("##komut", "kayıt ara (tüm türler)… Ctrl+K", g_komutMetni, sizeof(g_komutMetni)); if (g_komutMetni[0]) { const std::string f = g_komutMetni; int gosterilen = 0; for (size_t ti = 0; ti < ts.size() && gosterilen < 20; ++ti) { if (ts[ti].tur_id == "istemci_rol" && !g_istakipServis.kralMi()) continue; for (const IsKayit* k : veri->turListe(ts[ti].tur_id)) { if (gosterilen >= 20) break; if (!filtreGecer(*k, f)) continue; const char* ilkAlan = ts[ti].alanlar.empty() ? "" : alanDeger(*k, ts[ti].alanlar[0].ad.c_str()); char etiket[128]; std::snprintf(etiket, sizeof(etiket), "%s %s — %s###k%d_%llu", ts[ti].ikon.c_str(), ts[ti].baslik.c_str(), ilkAlan[0] ? ilkAlan : "(başlıksız)", (int)ti, (unsigned long long)k->kimlik); if (ImGui::Selectable(etiket)) { g_sekme = (int)ti; formYukle(ts[ti], k); ImGui::CloseCurrentPopup(); } ++gosterilen; } } if (!gosterilen) ImGui::TextDisabled("Sonuç yok"); } ImGui::EndPopup(); } // Sol tür sayaçları: yalnız veri sürümü değişince tazele (kare-başı tarama yok). static std::vector solSayilar; static u64 solSayilarSurum = ~0ull; if (veri->sonSurum() != solSayilarSurum) { solSayilar.assign(ts.size(), 0); for (size_t i = 0; i < ts.size(); ++i) solSayilar[i] = (int)veri->turListe(ts[i].tur_id).size(); solSayilarSurum = veri->sonSurum(); } // ── SOL: tür gezgini (kart görünümü + arama kutusu) ────────────────── ImGui::BeginChild("##istakipSol", {180, 0}, ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY); // Üstte arama kutusu. if (ui::aramaKutusu("##istakipAra", g_komutMetni, sizeof(g_komutMetni), "Ctrl+K ile ara…")) { ImGui::OpenPopup("##istakipKomut"); } ImGui::Separator(); for (size_t i = 0; i < ts.size(); ++i) { if (ts[i].tur_id == "istemci_rol" && !g_istakipServis.kralMi()) continue; char etiket[96]; std::snprintf(etiket, sizeof(etiket), "%s %s ###sol%d", ts[i].ikon.c_str(), ts[i].baslik.c_str(), (int)i); bool secili = g_sekme == (int)i; ImGui::PushID((int)i); // Kart: seçiliyse vurgu arka plan, sayaç badge sağda. const ImVec4 orj = ImGui::GetStyle().Colors[ImGuiCol_Button]; const ImVec4 orjH = ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered]; const ImVec4 orjA = ImGui::GetStyle().Colors[ImGuiCol_ButtonActive]; if (secili) { ImGui::GetStyle().Colors[ImGuiCol_Button] = ui::tema().vurgu; ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered] = ui::tema().vurgu; ImGui::GetStyle().Colors[ImGuiCol_ButtonActive] = ui::tema().vurgu; } if (ImGui::Selectable(etiket, secili, ImGuiSelectableFlags_SpanAllColumns)) { g_sekme = (int)i; g_seciliKimlik = 0; } if (secili) { ImGui::GetStyle().Colors[ImGuiCol_Button] = orj; ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered] = orjH; ImGui::GetStyle().Colors[ImGuiCol_ButtonActive] = orjA; } // Sayaç badge: seçili kartın sağına. if (secili) { char saya[24]; std::snprintf(saya, sizeof(saya), "%d", i < solSayilar.size() ? solSayilar[i] : 0); const ImVec4 orjb = ImGui::GetStyle().Colors[ImGuiCol_Button]; ImGui::GetStyle().Colors[ImGuiCol_Button] = ui::karisim(ui::tema().panel, ui::tema().vurgu, 0.22f); ImGui::Button(saya, {26, 0}); ImGui::GetStyle().Colors[ImGuiCol_Button] = orjb; } ImGui::PopID(); } ImGui::Separator(); if (ImGui::Button("⚙ Şema Tasarımcı", {-1, 0})) istakipSemaPencereAc(); ImGui::EndChild(); ImGui::SameLine(); const TurSema& t = ts[(size_t)g_sekme]; // ── ORTA: liste (tablo/kanban) ──────────────────────────────────────── ImGui::BeginChild("##istakipOrta", {-320.0f, 0}); // Erişim sekmesi: bağlı istemci kimliklerini göster (rol kaydı açarken No buradan). if (t.tur_id == "istemci_rol") { std::string kimlikler; if (auto* kk = g_istakipServis.kralKanal()) for (uint64_t no : kk->istemciKimlikleri()) kimlikler += (kimlikler.empty() ? "" : " · ") + std::to_string(no); ImGui::TextDisabled("Bağlı istemci No: %s", kimlikler.empty() ? "-" : kimlikler.c_str()); ImGui::TextDisabled("Rol kaydı yoksa genel ayar geçerli (İstemciler yazabilir: %s)", istakipIstemciYazabilir() ? "açık" : "kapalı"); } // Araç şeridi: yeni + filtre. if (ImGui::Button(("➕ Yeni " + t.tekil).c_str())) formYukle(t, nullptr); ImGui::SameLine(); ImGui::SetNextItemWidth(200); ImGui::InputTextWithHint("##filtre", "🔍 ara…", g_filtre, sizeof(g_filtre)); ImGui::SameLine(); { const bool aktif = !aktifFiltre(t.tur_id).bosMu(); if (aktif) ImGui::PushStyleColor(ImGuiCol_Text, ui::tema().vurgu); if (ImGui::SmallButton(aktif ? "🔎 Filtreli" : "🔎 Filtrele")) { g_filtreTaslak = aktifFiltre(t.tur_id); ImGui::OpenPopup("##istakipFiltrePopup"); } if (aktif) ImGui::PopStyleColor(); } filtrePopupCiz(t); ImGui::SameLine(); if (ImGui::SmallButton("💾 Görünüm")) ImGui::OpenPopup("##istakipGorunumPopup"); gorunumPopupCiz(*veri, t); ImGui::SameLine(); // Segmentli görünüm seçici (segmentli yardımcı). const char* const gorunumSecenek[4] = {"Tablo", "Kanban", "Zaman", "İlişki"}; int gorunumIdx = (int)g_gorunum; if (ui::segmentli("##gorunumSecici", gorunumSecenek, 4, &gorunumIdx)) g_gorunum = (Gorunum)gorunumIdx; if (!t.durumlar.empty()) { ImGui::SameLine(); } if (ImGui::SmallButton("⬇ CSV")) csvDisaAktar(t); ImGui::SameLine(); if (ImGui::SmallButton("⚙ Şema")) istakipSemaPencereDuzenleAc(t.tur_id.c_str()); ImGui::SameLine(); ImGui::TextDisabled("%d kayıt", (int)g_liste.size()); // Liste önbelleği: veri sürümü / sekme / metin filtre / yapılı filtre değişince kur. if (veri->sonSurum() != g_listSurum || g_sekme != g_listSekme || g_filtre != g_listFiltre || g_filtreSurum != g_listFiltreSurum) listeKur(*veri, t); // Seçili kayıt dışarıdan silinse kilidi bırak. if (g_seciliKimlik && !veri->bul(g_seciliKimlik)) g_seciliKimlik = 0; // F5: Zaman/İlişki TÜM türler üzerinde çalışır (seçili sekmeden bağımsız); // tıklanan düğüm/çubuk ilgili türe geçip sağ panelde açar. if (g_gorunum == Gorunum::Kanban && !t.durumlar.empty()) { panoCiz(t); } else if (g_gorunum == Gorunum::Zaman) { std::string hedefTur; u64 hedefKimlik = 0; if (ganttCiz(*veri, ts, hedefTur, hedefKimlik)) { for (size_t i = 0; i < ts.size(); ++i) if (ts[i].tur_id == hedefTur) { g_sekme = (int)i; formYukle(ts[i], veri->bul(hedefKimlik)); break; } } } else if (g_gorunum == Gorunum::Iliski) { std::string hedefTur; u64 hedefKimlik = 0; if (grafikCiz(*veri, ts, hedefTur, hedefKimlik)) { for (size_t i = 0; i < ts.size(); ++i) if (ts[i].tur_id == hedefTur) { g_sekme = (int)i; formYukle(ts[i], veri->bul(hedefKimlik)); break; } } } else { tabloCiz(b, t); } ImGui::EndChild(); ImGui::SameLine(); // ── SAĞ: salt-okunur özet + Kayıt penceresini açar (inline veri girişi yok) ── ImGui::BeginChild("##istakipSag", {0, 0}, ImGuiChildFlags_Borders); formCiz(b, t); ImGui::EndChild(); istakipKayitPenceresiCiz(b); // bağımsız Kayıt penceresini çiz (g_kayitAcik açıkken) } } // namespace fiko