#include "istakip_kayit_penceresi.hpp" #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/istakip/model.hpp" #include "fiko/ui/bilesen.hpp" // buton/baslikSeridi/neonAyrac/rozet #include "fiko/ui/bilesen_ek.hpp" // toastGoster #include "fiko/ui/pencere_cerceve.hpp" #include "fiko/ui/form.hpp" // bilgiSeridi #include "fiko/ui/tema.hpp" #include "imgui.h" #include "istakip_servis.hpp" #include "istakip_sema.hpp" #include "simdjson.h" #include "uygulama_baglam.hpp" namespace fiko { namespace { using istakip::IsKayit; // ── Kayıt penceresi durumu ──────────────────────────────────────────────── bool g_kayitAcik = false; bool g_pin = false; char g_kayitTurId[32] = {}; u64 g_kayitKimlik = 0; // 0 = yeni kayıt u64 g_beklenen = 0; // iyimser kilit (seçim anındaki sürüm) char g_form[8][256] = {}; // alan sırasıyla (maks 8 alan) int g_formDurum = 0; // durum combo indeksi char g_yorumForm[256] = {}; enum class KayitSekme { Alanlar, Ekler, Olaylar }; KayitSekme g_kayitSekme = KayitSekme::Alanlar; // Benzersiz pencere kimliği: her açılış taze (yeni) pencere üretir. Statik // ###istakip_kayit kimliği, dinamik başlıkla (Yeni/Düzenle) karışıp aynı ID'ye // yeni pencere üretme / geri açılmama (sameID) hatası verir. Her açılışta // kimlik yenilenir; açıkken sabit kalır, kapatılınca düşer. int g_kayitPencereSeq = 0; int g_kayitPencereId = 0; const char* alanDeger(const IsKayit& k, const char* ad) { const char* v = istakip::alanBul(k, ad); return v ? v : ""; } void formuYukle(const TurSema& t, const IsKayit* k) { std::memset(g_form, 0, sizeof(g_form)); g_formDurum = 0; g_kayitKimlik = k ? k->kimlik : 0; g_beklenen = k ? k->surum : 0; for (size_t i = 0; i < t.alanlar.size() && i < 8; ++i) std::snprintf(g_form[i], sizeof(g_form[i]), "%s", alanDeger(*k, t.alanlar[i].ad.c_str())); if (!t.durumlar.empty() && k) { const char* d = alanDeger(*k, t.kanban_alan.c_str()); for (size_t i = 0; i < t.durumlar.size(); ++i) if (d == t.durumlar[i]) g_formDurum = (int)i; } } void formuBosalt() { g_kayitAcik = false; g_kayitKimlik = 0; g_beklenen = 0; std::memset(g_form, 0, sizeof(g_form)); g_formDurum = 0; g_kayitSekme = KayitSekme::Alanlar; } IsKayit formKayit(const TurSema& t) { IsKayit k; k.kimlik = g_kayitKimlik; k.tur = t.tur_id; for (size_t i = 0; i < t.alanlar.size() && i < 8; ++i) if (g_form[i][0]) istakip::alanAta(k, t.alanlar[i].ad, g_form[i]); if (!t.durumlar.empty()) istakip::alanAta(k, "durum", t.durumlar[(size_t)g_formDurum]); return k; } // CAD belge yolu seçici (iş emri "Belge" alanı — .fikocad/STEP bağlanır). std::string belgeYoluSec() { wchar_t yol[MAX_PATH] = {}; OPENFILENAMEW ofn{}; ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = GetActiveWindow(); ofn.lpstrFilter = L"CAD belgeleri (*.fikocad;*.stp;*.step)\0*.fikocad;*.stp;*.step\0" L"Tüm dosyalar (*.*)\0*.*\0"; ofn.lpstrFile = yol; ofn.nMaxFile = MAX_PATH; ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; if (!GetOpenFileNameW(&ofn)) return {}; const int bayt = WideCharToMultiByte(CP_UTF8, 0, yol, -1, nullptr, 0, nullptr, nullptr); if (bayt <= 1) return {}; std::string s((size_t)bayt - 1, 0); WideCharToMultiByte(CP_UTF8, 0, yol, -1, s.data(), bayt, nullptr, nullptr); return s; } // "ekler" alanı — JSON dize dizisi (şemadan bağımsız gömülü alan). std::vector eklerCoz(const std::string& json) { std::vector out; if (json.empty()) return out; simdjson::ondemand::parser parser; simdjson::padded_string dolu(json); simdjson::ondemand::document doc; if (parser.iterate(dolu).get(doc) != simdjson::SUCCESS) return out; simdjson::ondemand::array arr; if (doc.get_array().get(arr) != simdjson::SUCCESS) return out; for (auto val : arr) { std::string_view sv; if (val.get_string().get(sv) == simdjson::SUCCESS) out.emplace_back(sv); } return out; } std::string eklerSerialize(const std::vector& ekler) { std::string j = "["; for (size_t i = 0; i < ekler.size(); ++i) { if (i) j += ","; std::string e; for (char c : ekler[i]) { if (c == '"' || c == '\\') e += '\\'; e += c; } j += "\"" + e + "\""; } j += "]"; return j; } // Bir yolu aç: önce yerel disk, sonra kraldan indir + otomatik aç. bool acBelge(UygulamaBaglam& b, const std::string& yol) { static std::string g_bekleyenAcYolu; const std::string yerel = g_istakipServis.yerelYol(yol); if (!yerel.empty()) { yolAc(b, yerel); return true; } if (g_istakipServis.dosyaGetir(yol)) { g_bekleyenAcYolu = yol; ui::toastGoster("Belge kraldan getiriliyor…", ui::ToastTip::Bilgi); return true; } ui::toastGoster("Belge bulunamadı — yol/kral bağlantısını kontrol edin", ui::ToastTip::Uyari); return false; } // ── Alanlar sekmesi: form + kaydet/sil/dönüşüm ──────────────────────────── void alanlarSekme(UygulamaBaglam& b, const TurSema& t) { for (size_t i = 0; i < t.alanlar.size() && i < 8; ++i) { ImGui::PushID((int)i); const char* ad = t.alanlar[i].ad.c_str(); ui::alan(t.alanlar[i].etiket.c_str()); const bool musteriAlani = t.alanlar[i].ad == "musteri"; const bool dosyaAlani = t.alanlar[i].ad == "dosya"; char alanKimlik[32]; std::snprintf(alanKimlik, sizeof(alanKimlik), "##%s", ad); ImGui::InputText(alanKimlik, g_form[i], sizeof(g_form[i])); if (dosyaAlani) { // CAD belgesi bağla + tek tıkla aç ImGui::SameLine(); if (ImGui::Button("📂")) { const std::string yol = belgeYoluSec(); if (!yol.empty()) std::snprintf(g_form[i], sizeof(g_form[i]), "%s", yol.c_str()); } ImGui::SameLine(); if (ImGui::Button("▶ Aç") && g_form[i][0]) acBelge(b, g_form[i]); } if (musteriAlani) { // kayıtlı müşterilerden seç (serbest metin de kalır) ImGui::SameLine(); if (ImGui::Button("▾")) ImGui::OpenPopup("##musteriSec"); if (ImGui::BeginPopup("##musteriSec")) { const istakip::IsIstemci* veri = g_istakipServis.veri(); bool bosMu = true; if (veri) { auto musteriler = veri->turListe("musteri"); std::sort(musteriler.begin(), musteriler.end(), [](const IsKayit* a, const IsKayit* b) { return std::string(alanDeger(*a, "ad")) < alanDeger(*b, "ad"); }); for (const IsKayit* mk : musteriler) { const char* ad = alanDeger(*mk, "ad"); if (!ad[0]) continue; bosMu = false; if (ImGui::Selectable(ad)) std::snprintf(g_form[i], sizeof(g_form[i]), "%s", ad); } } if (bosMu) ImGui::TextDisabled("Kayıtlı müşteri yok"); ImGui::EndPopup(); } } ImGui::PopID(); } ui::alan("Durum"); if (ImGui::BeginCombo("##durum", t.durumlar[(size_t)g_formDurum].c_str())) { for (size_t i = 0; i < t.durumlar.size(); ++i) if (ImGui::Selectable(t.durumlar[i].c_str(), (int)i == g_formDurum)) g_formDurum = (int)i; ImGui::EndCombo(); } ui::neonAyrac(); const bool yazilabilir = g_istakipServis.aktifMi(); if (!yazilabilir) ImGui::BeginDisabled(); bool kayandi = ui::buton(g_kayitKimlik ? "💾 Kaydet" : "➕ Oluştur", {130, 0}); if (yazilabilir && kayandi) { if (g_istakipServis.yaz(formKayit(t), g_beklenen) == 0 && g_istakipServis.kralMi()) ui::toastGoster(g_istakipServis.sonHata().c_str(), ui::ToastTip::Uyari); else formuBosalt(); } if (g_kayitKimlik && yazilabilir) { ImGui::SameLine(); if (ui::buton("🗑 Sil", {72, 0}, ui::ButonTip::Tehlike)) { IsKayit sil = formKayit(t); sil.silindi = true; g_istakipServis.yaz(sil, g_beklenen); formuBosalt(); ui::toastGoster("Kayıt silindi", ui::ToastTip::Bilgi); } } // Dönüşüm akışları: Teklif → Sipariş · Sipariş → İş Emri. const istakip::IsIstemci* veri = g_istakipServis.veri(); const IsKayit* secili = veri ? veri->bul(g_kayitKimlik) : nullptr; if (secili && t.tur_id == "teklif") { if (ui::buton("📦 Siparişe Dönüştür", {160, 0})) { IsKayit y; y.tur = "siparis"; for (const auto& [a, d] : secili->alanlar) if (std::string(a) != "durum") istakip::alanAta(y, a, d); istakip::alanAta(y, istakip::ALAN_KAYNAK, std::to_string(secili->kimlik)); istakip::alanAta(y, "durum", "onaylandı"); if (g_istakipServis.yaz(y, 0) == 0) ui::toastGoster("Dönüşüm yazılamadı — bağlantı/yetki kontrol edin", ui::ToastTip::Uyari); else ui::toastGoster("Sipariş oluşturuldu", ui::ToastTip::Bilgi); } } else if (secili && t.tur_id == "siparis") { if (ui::buton("🛠 İş Emri Oluştur", {160, 0})) { IsKayit y; y.tur = "is_emri"; for (const auto& [a, d] : secili->alanlar) if (std::string(a) != "durum") istakip::alanAta(y, a, d); istakip::alanAta(y, istakip::ALAN_UST, std::to_string(secili->kimlik)); istakip::alanAta(y, "durum", "bekliyor"); if (g_istakipServis.yaz(y, 0) == 0) ui::toastGoster("Dönüşüm yazılamadı — bağlantı/yetki kontrol edin", ui::ToastTip::Uyari); else ui::toastGoster("İş emri oluşturuldu", ui::ToastTip::Bilgi); } } if (!yazilabilir) ImGui::EndDisabled(); ImGui::SameLine(); if (ui::buton("✖ Vazgeç", {110, 0}, ui::ButonTip::Ikincil)) formuBosalt(); } // ── Ekler sekmesi — çoklu dosya eki ─────────────────────────────────────── void eklerSekme(UygulamaBaglam& b) { const istakip::IsIstemci* veri = g_istakipServis.veri(); const IsKayit* kEk = veri ? veri->bul(g_kayitKimlik) : nullptr; if (!kEk) { ui::baslikSeridi("📎", "Ekler"); ImGui::TextDisabled("Bağlı belge yok"); return; } ui::baslikSeridi("📎", "Ekler"); std::vector ekler = eklerCoz(alanDeger(*kEk, "ekler")); if (ekler.empty()) { ImGui::Spacing(); ImGui::TextDisabled("Henüz eklenmiş belge yok"); return; } ui::rozet(std::to_string(ekler.size()).c_str(), ui::tema().vurgu); ui::neonAyrac(); ImGui::BeginChild("##ekListesi", {0, 150}, ImGuiChildFlags_Borders); int silinecek = -1; for (int i = 0; i < (int)ekler.size(); ++i) { const std::string& yol = ekler[(size_t)i]; const size_t p = yol.find_last_of("\\/"); const std::string ad = p == std::string::npos ? yol : yol.substr(p + 1); ImGui::PushID(2000 + i); ImGui::TextUnformatted("📄"); ImGui::SameLine(); if (ImGui::Selectable(ad.c_str(), false, 0, {320, 0})) acBelge(b, yol); ImGui::SameLine(); if (ImGui::SmallButton("🗑")) silinecek = i; ImGui::PopID(); } ImGui::EndChild(); if (silinecek >= 0) { ekler.erase(ekler.begin() + silinecek); IsKayit g = *kEk; istakip::alanAta(g, "ekler", eklerSerialize(ekler)); g_istakipServis.yaz(g, kEk->surum); } ImGui::SameLine(); if (ui::buton("📂 Dosya Ekle", {0, 0}, ui::ButonTip::Ikincil)) { const std::string yeniYol = belgeYoluSec(); if (!yeniYol.empty()) { ekler.push_back(yeniYol); IsKayit g = *kEk; istakip::alanAta(g, "ekler", eklerSerialize(ekler)); g_istakipServis.yaz(g, kEk->surum); } } } // ── Olaylar sekmesi — sistem diff + yorum ───────────────────────────────── void olaylarSekme() { const istakip::IsIstemci* veri = g_istakipServis.veri(); if (!veri) { ui::baslikSeridi("⚙", "Olaylar"); ImGui::TextDisabled("Olay yok"); return; } ui::baslikSeridi("⚙", "Olaylar"); const std::string ust = std::to_string(g_kayitKimlik); std::vector olaylar; for (const IsKayit* o : veri->turListe("_olay")) if (ust == alanDeger(*o, istakip::ALAN_UST)) olaylar.push_back(o); std::sort(olaylar.begin(), olaylar.end(), [](const IsKayit* a, const IsKayit* b) { return a->kimlik > b->kimlik; }); if (olaylar.empty()) { ImGui::Spacing(); ImGui::TextDisabled("Henüz olay yok"); return; } ui::neonAyrac(); ImGui::BeginChild("##olayAkisi", {0, 130}, ImGuiChildFlags_Borders); for (const IsKayit* o : olaylar) { const std::string tur = alanDeger(*o, "olay_tur"); const char* ikon = tur == "yorum" ? "💬" : (tur == "ozel" ? "📌" : "⚙"); ImGui::TextUnformatted(ikon); ImGui::SameLine(); ImGui::TextWrapped("%s", alanDeger(*o, "baslik")); } ImGui::EndChild(); ui::neonAyrac(); ImGui::SetNextItemWidth(-56.0f); ImGui::InputTextWithHint("##yorum", " yorum yaz…", g_yorumForm, sizeof(g_yorumForm)); ImGui::SameLine(); if (ui::buton("Gönder", {0, 0})) { const uint64_t simdi = (uint64_t)std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) .count(); IsKayit y; y.tur = "_olay"; istakip::alanAta(y, istakip::ALAN_UST, ust); istakip::alanAta(y, "olay_tur", "yorum"); istakip::alanAta(y, "baslik", g_yorumForm); istakip::alanAta(y, "yapan", std::to_string(veri->istemciNo())); istakip::alanAta(y, "zaman", std::to_string(simdi)); g_istakipServis.yaz(y); std::memset(g_yorumForm, 0, sizeof(g_yorumForm)); } } } // namespace void istakipKayitPencereAc(const char* tur_id, uint64_t kimlik) { g_kayitTurId[0] = '\0'; std::snprintf(g_kayitTurId, sizeof(g_kayitTurId), "%s", tur_id); g_kayitKimlik = kimlik; // 0 = yeni g_beklenen = 0; g_kayitSekme = KayitSekme::Alanlar; g_kayitPencereSeq++; g_kayitPencereId = g_kayitPencereSeq; if (kimlik == 0) { formuBosalt(); g_kayitKimlik = 0; } else { const istakip::IsIstemci* veri = g_istakipServis.veri(); if (veri) { const IsKayit* k = veri->bul(kimlik); if (k) { // Tür şemasını bul (isim/ikon için). for (const TurSema& t : turlarOku(*veri)) if (t.tur_id == g_kayitTurId) break; formuYukle(turlarOku(*veri)[0], k); } } } g_kayitAcik = true; } void istakipKayitPenceresiCiz(UygulamaBaglam& b) { if (!g_kayitAcik) return; const istakip::IsIstemci* veri = g_istakipServis.veri(); if (!veri) { char pencereKimlik[128]; std::snprintf(pencereKimlik, sizeof(pencereKimlik), "Kayıt###kayit_%d", g_kayitPencereId); ui::Pencere p{pencereKimlik, &g_kayitAcik, {.boyut = {560, 520}, .minBoyut = {440, 420}, .pinDurum = &g_pin, .skin = ui::PencereSkin::WebCam}}; if (p) ui::bilgiSeridi(0, "İş Takip kapalı — önce Ayarlar'dan bağlanın"); return; } // Tür şemasını bul. const TurSema* t = nullptr; const auto& ts = turlarOku(*veri); for (const TurSema& s : ts) if (s.tur_id == g_kayitTurId) { t = &s; break; } if (!t) { char pencereKimlik[128]; std::snprintf(pencereKimlik, sizeof(pencereKimlik), "Kayıt###kayit_%d", g_kayitPencereId); ui::Pencere p{pencereKimlik, &g_kayitAcik, {.boyut = {560, 520}, .minBoyut = {440, 420}, .pinDurum = &g_pin, .skin = ui::PencereSkin::WebCam}}; if (p) { ImGui::TextDisabled("Bu tür için şema bulunamadı"); if (ImGui::Button("Kapat")) formuBosalt(); } return; } char pencereKimlik[128]; std::snprintf(pencereKimlik, sizeof(pencereKimlik), "%s###kayit_%d", g_kayitKimlik ? (std::string("Düzenle ") + t->baslik).c_str() : (std::string("Yeni ") + t->tekil).c_str(), g_kayitPencereId); ui::Pencere p{pencereKimlik, &g_kayitAcik, {.boyut = {560, 520}, .minBoyut = {440, 420}, .pinDurum = &g_pin, .skin = ui::PencereSkin::WebCam}}; if (!p) return; // Üst band: tür ikonu + başlık. ImGui::TextColored(ui::tema().vurgu, "%s %s", t->ikon.c_str(), t->baslik); // Sekme çubuğu: Alanlar | Ekler | Olaylar. if (ImGui::BeginTabBar("###kayitSekme")) { const char* sekmeler[] = {"Alanlar", "📎 Ekler", "Olaylar"}; if (ImGui::BeginTabItem(sekmeler[0])) { g_kayitSekme = KayitSekme::Alanlar; alanlarSekme(b, *t); ImGui::EndTabItem(); } if (ImGui::BeginTabItem(sekmeler[1])) { g_kayitSekme = KayitSekme::Ekler; eklerSekme(b); ImGui::EndTabItem(); } if (ImGui::BeginTabItem(sekmeler[2])) { g_kayitSekme = KayitSekme::Olaylar; olaylarSekme(); ImGui::EndTabItem(); } ImGui::EndTabBar(); } } } // namespace fiko