/* * Copyright (c) 2025 Aldrik Ramaekers * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "strops.hpp" #include "ui.hpp" #include "imgui.h" #include "administration.hpp" #include "administration_writer.hpp" #include "locales.hpp" static view_state current_view_state = view_state::LIST; static contact selected_for_removal; static contact active_contact; void ui_draw_address_form(address* buffer) { const char* selected_country = NULL; float widthAvailable = ImGui::GetContentRegionAvail().x; char id[MAX_LEN_ADDRESS]; ImGui::SetNextItemWidth(widthAvailable*0.5f); snprintf(id, sizeof(id), "%s##%p", localize("contact.form.address1"), buffer); ImGui::InputTextWithHint(id, localize("contact.form.address1"), buffer->address1, IM_ARRAYSIZE(buffer->address1)); ImGui::SameLine();ui_helper_draw_required_tag(); ImGui::SetNextItemWidth(widthAvailable*0.5f); snprintf(id, sizeof(id), "%s##%p", localize("contact.form.address2"), buffer); ImGui::InputTextWithHint(id, localize("contact.form.address2"), buffer->address2, IM_ARRAYSIZE(buffer->address2)); //ImGui::SameLine();ui_helper_draw_required_tag(); ImGui::SetNextItemWidth(widthAvailable*0.5f); snprintf(id, sizeof(id), "%s##%p", localize("contact.form.city"), buffer); ImGui::InputTextWithHint(id, localize("contact.form.city"), buffer->city, IM_ARRAYSIZE(buffer->city)); ImGui::SameLine();ui_helper_draw_required_tag(); ImGui::SetNextItemWidth(widthAvailable*0.5f); snprintf(id, sizeof(id), "%s##%p", localize("contact.form.postal"), buffer); ImGui::InputTextWithHint(id, localize("contact.form.postal"), buffer->postal, IM_ARRAYSIZE(buffer->postal)); ImGui::SameLine();ui_helper_draw_required_tag(); ImGui::SetNextItemWidth(widthAvailable*0.5f); snprintf(id, sizeof(id), "%s##%p", localize("contact.form.region"), buffer); ImGui::InputTextWithHint(id, localize("contact.form.region"), buffer->region, IM_ARRAYSIZE(buffer->region)); ImGui::SameLine();ui_helper_draw_required_tag(); // 5. Country dropdown. ImGui::SetNextItemWidth(widthAvailable*0.5f); const char* countries[] = { localize("country.AT"),localize("country.BE"),localize("country.BG"),localize("country.HR"),localize("country.CY"),localize("country.CZ"),localize("country.DK"),localize("country.EE"),localize("country.FI"),localize("country.FR"),localize("country.DE"),localize("country.GR"),localize("country.HU"),localize("country.IE"),localize("country.IT"),localize("country.LV"),localize("country.LT"),localize("country.LU"),localize("country.MT"),localize("country.NL"),localize("country.PL"),localize("country.PT"),localize("country.RO"),localize("country.SK"),localize("country.SI"),localize("country.ES"),localize("country.SE") }; const char* country_codes[] = { "AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR", "DE", "GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL", "PL", "PT", "RO", "SK", "SI", "ES", "SE" }; s32 country_count = sizeof(countries) / sizeof(countries[0]); if (selected_country == 0) { for (int i = 0; i < country_count; i++) { if (strcmp(country_codes[i], buffer->country_code) == 0) { selected_country = countries[i]; break; } } } int selected_country_index = -1; snprintf(id, sizeof(id), "%s##%p", localize("contact.form.country"), buffer); if (ImGui::BeginCombo(id, selected_country)) { for (int n = 0; n < IM_ARRAYSIZE(countries); n++) { bool is_selected = (selected_country == countries[n]); if (ImGui::Selectable(countries[n], is_selected)) { selected_country = countries[n]; selected_country_index = n; } } ImGui::EndCombo(); } if (selected_country_index != -1) { strops_copy(buffer->country_code, country_codes[selected_country_index], IM_ARRAYSIZE(buffer->country_code)); } ImGui::SameLine();ui_helper_draw_required_tag(); } void ui_setup_contacts() { current_view_state = view_state::LIST; active_contact = active_contact = administration_contact_create_empty(); memset(&selected_for_removal, 0, sizeof(contact)); } void draw_contact_form_ex(contact* buffer, bool viewing_only = false, bool with_autocomplete = false, bool* on_autocomplete = 0) { ImGui::PushID(buffer); ImGui::Spacing(); float widthAvailable = ImGui::GetContentRegionAvail().x; ImGui::BeginDisabled(); // 1. Identifier //ImGui::SetNextItemWidth(widthAvailable*0.2f); //ImGui::InputText(localize("contact.form.identifier"), buffer->id, IM_ARRAYSIZE(buffer->id)); if (!viewing_only) ImGui::EndDisabled(); // 2. Full name ImGui::SetNextItemWidth(widthAvailable*0.5f); if (with_autocomplete) { contact autocomplete_list[5]; int autocomplete_count = administration_contact_get_autocompletions(autocomplete_list, 5, buffer->name); char* autocomplete_strings[5]; for (int i = 0; i < autocomplete_count; i++) { autocomplete_strings[i] = (char*)malloc(200); snprintf(autocomplete_strings[i], 200, "%s (%s %s)", autocomplete_list[i].name, autocomplete_list[i].address.address1, autocomplete_list[i].address.address2); } int autocomplete_index = TextInputWithAutocomplete(localize("contact.form.fullname"), localize("contact.form.fullname"), buffer->name, IM_ARRAYSIZE(buffer->name), (char**)autocomplete_strings, autocomplete_count); if (on_autocomplete) { *on_autocomplete = autocomplete_index != -1; } if (autocomplete_index != -1) { memcpy(buffer, &autocomplete_list[autocomplete_index], sizeof(contact)); } for (int i = 0; i < autocomplete_count; i++) { free(autocomplete_strings[i]); } } else ImGui::InputTextWithHint(localize("contact.form.fullname"), localize("contact.form.fullname"), buffer->name, IM_ARRAYSIZE(buffer->name)); ImGui::SameLine();ui_helper_draw_required_tag(); // 3. Address line 1, 4. address line 2, 5. country ui_draw_address_form(&buffer->address); // 6. Contact type dropdown. ImGui::SetNextItemWidth(widthAvailable*0.5f); const char* customer_types[2] = { localize("contact.form.type.business"), localize("contact.form.type.consumer") }; int currentItem = static_cast(buffer->type); if (ImGui::Combo(localize("contact.form.type"), ¤tItem, customer_types, IM_ARRAYSIZE(customer_types))) { buffer->type = static_cast(currentItem); } // Fields only required for businesses. if (buffer->type == contact_type::CONTACT_BUSINESS) { // 7. Tax number ImGui::SetNextItemWidth(widthAvailable*0.5f); ImGui::InputTextWithHint(localize("contact.form.taxnumber"), localize("contact.form.taxnumber"), buffer->taxid, IM_ARRAYSIZE(buffer->taxid)); // 8. Business number / Chamber of commerce ImGui::SetNextItemWidth(widthAvailable*0.5f); ImGui::InputTextWithHint(localize("contact.form.businessnumber"), localize("contact.form.businessnumber"), buffer->businessid, IM_ARRAYSIZE(buffer->businessid)); } // 9. Email ImGui::SetNextItemWidth(widthAvailable*0.5f); ImGui::InputTextWithHint(localize("contact.form.email"), localize("contact.form.email"), buffer->email, IM_ARRAYSIZE(buffer->email)); // 10. Phone number ImGui::SetNextItemWidth(widthAvailable*0.5f); ImGui::InputTextWithHint(localize("contact.form.phonenumber"), localize("contact.form.phonenumber"), buffer->phone_number, IM_ARRAYSIZE(buffer->phone_number)); // 11. Bank account. ImGui::SetNextItemWidth(widthAvailable*0.5f); ImGui::InputTextWithHint(localize("contact.form.bankaccount"), localize("contact.form.bankaccount"), buffer->bank_account, IM_ARRAYSIZE(buffer->bank_account)); if (viewing_only) ImGui::EndDisabled(); ImGui::PopID(); } void draw_contact_form(contact* buffer, bool viewing_only = false) { draw_contact_form_ex(buffer, viewing_only, false, 0); } static void draw_contact_list() { const u32 items_per_page = 50; static s32 current_page = 0; s32 max_page = (administration_contact_count() + items_per_page - 1) / items_per_page; if (max_page == 0) max_page = 1; // Table header controls: create button and pagination. if (ImGui::Button(localize("form.create"))) { current_view_state = view_state::CREATE; active_contact = administration_contact_create_empty(); } if (current_page >= max_page-1) current_page = max_page-1; if (current_page < 0) current_page = 0; // Navigate to prev page button. ImGui::SameLine(); bool enable_prev = current_page > 0; if (!enable_prev) ImGui::BeginDisabled(); if (ImGui::Button(localize("ui.prev")) && current_page > 0) current_page--; if (!enable_prev) ImGui::EndDisabled(); ImGui::SameLine(); ImGui::Text("(%d/%d)", current_page+1, max_page); // Navigate to next page button. ImGui::SameLine(); bool enable_next = current_page < max_page-1; if (!enable_next) ImGui::BeginDisabled(); if (ImGui::Button(localize("ui.next")) && current_page < max_page-1) current_page++; if (!enable_next) ImGui::EndDisabled(); ImGui::Spacing(); if (ImGui::BeginTable("TableContacts", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { ImGui::TableSetupColumn(localize("contact.table.identifier"), ImGuiTableColumnFlags_WidthFixed, 80); ImGui::TableSetupColumn(localize("contact.table.name"), ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn(localize("contact.table.address"), ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 160); ImGui::TableHeadersRow(); contact contact_list[items_per_page]; u32 contact_count = administration_contact_get_partial_list(current_page, items_per_page, contact_list); for (u32 i = 0; i < contact_count; i++) { contact c = contact_list[i]; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::Text(c.id); ImGui::TableSetColumnIndex(1); ImGui::Text(c.name); ImGui::TableSetColumnIndex(2); ImGui::Text("%s %s", c.address.address1, c.address.address2); ImGui::TableSetColumnIndex(3); char btn_name[20]; snprintf(btn_name, sizeof(btn_name), "%s##%d", localize("form.view"), i); if (ImGui::Button(btn_name)) { active_contact = c; current_view_state = view_state::VIEW; } ImGui::SameLine(); snprintf(btn_name, sizeof(btn_name), "%s##%d", localize("form.change"), i); if (ImGui::Button(btn_name)) { active_contact = c; current_view_state = view_state::EDIT; } ImGui::SameLine(); if (administration_contact_can_be_deleted(c)) { snprintf(btn_name, sizeof(btn_name), "%s##%d", localize("form.delete"), i); if (ImGui::Button(btn_name)) { selected_for_removal = c; ImGui::OpenPopup("ConfirmDeletePopup"); } } } // Confirmation popup before contact is deleted definitively. if (ImGui::BeginPopupModal("ConfirmDeletePopup", nullptr, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoTitleBar)) { ImGui::Text(localize("form.confirmDelete")); ImGui::Separator(); if (ImGui::Button(localize("form.yes"), ImVec2(120, 0))) { administration_contact_remove(selected_for_removal); ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button(localize("form.no"), ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } ImGui::EndTable(); } } static void ui_draw_contacts_create() { if (ImGui::Button(localize("form.back"))) { current_view_state = view_state::LIST; } draw_contact_form(&active_contact); bool can_save = administration_contact_is_valid(active_contact) == A_ERR_SUCCESS; if (!can_save) ImGui::BeginDisabled(); // Save button ImGui::Spacing(); if (ImGui::Button(localize("form.save"))) { administration_contact_add(active_contact); current_view_state = view_state::LIST; } if (!can_save) ImGui::EndDisabled(); } static void ui_draw_contacts_update() { if (ImGui::Button(localize("form.back"))) { current_view_state = view_state::LIST; } draw_contact_form(&active_contact); bool can_save = administration_contact_is_valid(active_contact) == A_ERR_SUCCESS; if (!can_save) ImGui::BeginDisabled(); // Save button ImGui::Spacing(); if (ImGui::Button(localize("form.save"))) { administration_contact_update(active_contact); current_view_state = view_state::LIST; } if (!can_save) ImGui::EndDisabled(); } void ui_draw_contacts() { switch(current_view_state) { case view_state::LIST: draw_contact_list(); break; case view_state::CREATE: ui_draw_contacts_create(); break; case view_state::EDIT: ui_draw_contacts_update(); break; case view_state::VIEW: if (ImGui::Button(localize("form.back"))) { current_view_state = view_state::LIST; } draw_contact_form(&active_contact, true); break; } }