summaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
authorAldrik Ramaekers <aldrikboy@gmail.com>2025-09-20 19:16:15 +0200
committerAldrik Ramaekers <aldrikboy@gmail.com>2025-09-20 19:16:15 +0200
commit214852f61cd3b9fda257044e9d822b94b4be7e5d (patch)
treeaa570a16623f1c7eae94474c80b484815ea85c88 /src/ui
parent3402cba0fe6fa1b89a029c612b622e7a4771d901 (diff)
refactor ui widgets
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/helpers.cpp74
-rw-r--r--src/ui/imgui_extensions.cpp414
-rw-r--r--src/ui/ui_contacts.cpp163
-rw-r--r--src/ui/ui_expenses.cpp123
-rw-r--r--src/ui/ui_invoices.cpp193
-rw-r--r--src/ui/ui_projects.cpp17
6 files changed, 465 insertions, 519 deletions
diff --git a/src/ui/helpers.cpp b/src/ui/helpers.cpp
index 07302b7..2447270 100644
--- a/src/ui/helpers.cpp
+++ b/src/ui/helpers.cpp
@@ -93,77 +93,3 @@ ui_status ui_get_status()
{
return current_status;
}
-
-void ui_helper_draw_required_tag()
-{
- ImDrawList* draw_list = ImGui::GetWindowDrawList();
-
- const char* text = localize("form.required");
- ImVec2 text_pos = ImGui::GetCursorScreenPos();
- ImVec2 text_size = ImGui::CalcTextSize(text);
- text_pos.y += text_size.y/4.0f;
-
- ImVec4 bg_color = ImVec4(0.9f, 0.235f, 0.235f, 0.4f); // Red background
- ImVec4 text_color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // White text
- float rounding = 2.0f;
- float padding = 2.0f;
-
- // Background rectangle
- ImVec2 bg_min = ImVec2(text_pos.x - padding, text_pos.y - padding);
- ImVec2 bg_max = ImVec2(text_pos.x + text_size.x + padding, text_pos.y + text_size.y + padding);
- draw_list->AddRectFilled(bg_min, bg_max, ImColor(bg_color), rounding);
-
- // Foreground text
- ImGui::PushStyleColor(ImGuiCol_Text, text_color);
- ImGui::TextUnformatted(text);
- ImGui::PopStyleColor();
-}
-
-int TextInputWithAutocomplete(const char* label, const char* hint, char* buffer, size_t buf_size,
- char* suggestions[], int suggestion_count)
-{
- int result = -1;
- static bool is_open = false;
- ImGui::InputTextWithHint(label, hint, buffer, buf_size);
-
- if (buffer[0] == '\0' && is_open) is_open = false;
- if (suggestion_count == 0 && is_open) is_open = false;
-
- bool is_active = ImGui::IsItemActive();
- if (is_active && buffer[0] != '\0' && suggestion_count > 0)
- {
- is_open = true;
- }
-
- if (is_open) {
- ImGui::BeginChild("autocomplete_popup", ImVec2(0, 10.0f + suggestion_count*23.0f), true);
- {
- ImVec2 win_pos = ImGui::GetWindowPos();
- ImVec2 win_size = ImGui::GetWindowSize();
- ImVec2 mouse_pos = ImGui::GetMousePos();
-
- bool mouse_clicked_outside = !is_active && ImGui::IsMouseClicked(0) &&
- (mouse_pos.x < win_pos.x || mouse_pos.x > win_pos.x + win_size.x ||
- mouse_pos.y < win_pos.y || mouse_pos.y > win_pos.y + win_size.y);
-
- if (mouse_clicked_outside) is_open = false;
-
- for (int i = 0; i < suggestion_count; ++i)
- {
- ImGui::PushID(i);
- if (ImGui::Selectable(suggestions[i]))
- {
- // Copy selected suggestion to buffer
- strops_copy(buffer, suggestions[i], buf_size);
- buffer[buf_size - 1] = '\0';
- result = i;
- is_open = false;
- }
- ImGui::PopID();
- }
-
- ImGui::EndChild();
- }
- }
- return result;
-}
diff --git a/src/ui/imgui_extensions.cpp b/src/ui/imgui_extensions.cpp
new file mode 100644
index 0000000..5fdc3a7
--- /dev/null
+++ b/src/ui/imgui_extensions.cpp
@@ -0,0 +1,414 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ui.hpp"
+#include "strops.hpp"
+#include "config.hpp"
+#include "locales.hpp"
+#include "administration.hpp"
+
+namespace ImGui
+{
+ void FormInputTextWithErrorHint(const char* hint, void* data, char* buffer, size_t buf_size, bool has_error)
+ {
+ float widthAvailable = ImGui::GetContentRegionAvail().x;
+ ImGui::SetNextItemWidth(widthAvailable*0.5f);
+
+ char id[MAX_LEN_LONG_DESC];
+ snprintf(id, sizeof(id), "%s##%p", hint, data);
+
+ if (has_error) {
+ ImGui::PushStyleColor(ImGuiCol_Border, COLOR_ERROR_OUTLINE);
+ ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.5f);
+ }
+ ImGui::InputTextWithHint(id, hint, buffer, buf_size);
+ if (has_error) {
+ ImGui::PopStyleVar();
+ ImGui::PopStyleColor();
+
+ ImGui::SameLine();
+ ImGui::SetCursorPosX(ImGui::GetCursorPosX() - ImGui::CalcTextSize("*").x);
+ ImGui::TextColored(ImVec4(1, 0, 0, 1), "*");
+ }
+ }
+
+ void FormCountryCombo(void* data, char* buffer, size_t buf_size)
+ {
+ const char* selected_country = 0;
+ char id[MAX_LEN_LONG_DESC];
+ float widthAvailable = ImGui::GetContentRegionAvail().x;
+ ImGui::SetNextItemWidth(widthAvailable*0.5f);
+
+ const char* countries[30];
+ for (int x = 0; x < country_count; x++)
+ {
+ char locale_str[20];
+ snprintf(locale_str, 20, "country.%s", country_codes[x]);
+ countries[x] = localize(locale_str);
+ }
+
+ for (int i = 0; i < country_count; i++)
+ {
+ if (strcmp(country_codes[i], buffer) == 0)
+ {
+ selected_country = countries[i];
+ break;
+ }
+ }
+ bool has_a_selection = selected_country != 0;
+
+ int selected_country_index = -1;
+ snprintf(id, sizeof(id), "%s##%p", localize("contact.form.country"), data);
+
+ if (!has_a_selection) {
+ ImGui::PushStyleColor(ImGuiCol_Border, COLOR_ERROR_OUTLINE);
+ ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.5f);
+ }
+
+ if (ImGui::BeginCombo(id, selected_country))
+ {
+ for (int n = 0; n < country_count; 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 (!has_a_selection) {
+ ImGui::PopStyleVar();
+ ImGui::PopStyleColor();
+
+ ImGui::SameLine();
+ ImGui::SetCursorPosX(ImGui::GetCursorPosX() - ImGui::CalcTextSize("*").x);
+ ImGui::TextColored(ImVec4(1, 0, 0, 1), "*");
+ }
+
+ if (selected_country_index != -1) {
+ strops_copy(buffer, country_codes[selected_country_index], buf_size);
+ }
+ }
+
+ void FormContactTypeCombo(contact_type* type)
+ {
+ float widthAvailable = ImGui::GetContentRegionAvail().x;
+ ImGui::SetNextItemWidth(widthAvailable*0.5f);
+ const char* customer_types[2] = { localize("contact.form.type.business"), localize("contact.form.type.consumer") };
+ int currentItem = static_cast<int>(*type);
+ if (ImGui::Combo(localize("contact.form.type"), &currentItem, customer_types, IM_ARRAYSIZE(customer_types)))
+ {
+ *type = static_cast<contact_type>(currentItem);
+ }
+ }
+
+
+ int TextInputWithAutocomplete(const char* hint, char* buffer, size_t buf_size, char* suggestions[], int suggestion_count, bool has_error)
+ {
+ int result = -1;
+ static bool is_open = false;
+
+ if (has_error) {
+ ImGui::PushStyleColor(ImGuiCol_Border, COLOR_ERROR_OUTLINE);
+ ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.5f);
+ }
+ ImGui::InputTextWithHint(hint, hint, buffer, buf_size);
+ if (has_error) {
+ ImGui::PopStyleVar();
+ ImGui::PopStyleColor();
+
+ ImGui::SameLine();
+ ImGui::SetCursorPosX(ImGui::GetCursorPosX() - ImGui::CalcTextSize("*").x);
+ ImGui::TextColored(ImVec4(1, 0, 0, 1), "*");
+ }
+
+
+ if (buffer[0] == '\0' && is_open) is_open = false;
+ if (suggestion_count == 0 && is_open) is_open = false;
+
+ bool is_active = ImGui::IsItemActive();
+ if (is_active && buffer[0] != '\0' && suggestion_count > 0)
+ {
+ is_open = true;
+ }
+
+ if (is_open) {
+ ImGui::BeginChild("autocomplete_popup", ImVec2(0, 10.0f + suggestion_count*23.0f), true);
+ {
+ ImVec2 win_pos = ImGui::GetWindowPos();
+ ImVec2 win_size = ImGui::GetWindowSize();
+ ImVec2 mouse_pos = ImGui::GetMousePos();
+
+ bool mouse_clicked_outside = !is_active && ImGui::IsMouseClicked(0) &&
+ (mouse_pos.x < win_pos.x || mouse_pos.x > win_pos.x + win_size.x ||
+ mouse_pos.y < win_pos.y || mouse_pos.y > win_pos.y + win_size.y);
+
+ if (mouse_clicked_outside) is_open = false;
+
+ for (int i = 0; i < suggestion_count; ++i)
+ {
+ ImGui::PushID(i);
+ if (ImGui::Selectable(suggestions[i]))
+ {
+ // Copy selected suggestion to buffer
+ strops_copy(buffer, suggestions[i], buf_size);
+ buffer[buf_size - 1] = '\0';
+ result = i;
+ is_open = false;
+ }
+ ImGui::PopID();
+ }
+
+ ImGui::EndChild();
+ }
+ }
+ return result;
+ }
+
+ void FormContactAutocomplete(contact* buffer, bool has_error)
+ {
+ float widthAvailable = ImGui::GetContentRegionAvail().x;
+ ImGui::SetNextItemWidth(widthAvailable*0.5f);
+
+ 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 = ImGui::TextInputWithAutocomplete(localize("contact.form.fullname"),
+ buffer->name, IM_ARRAYSIZE(buffer->name), (char**)autocomplete_strings, autocomplete_count, has_error);
+
+ if (autocomplete_index != -1)
+ {
+ memcpy(buffer, &autocomplete_list[autocomplete_index], sizeof(contact));
+ }
+
+ for (int i = 0; i < autocomplete_count; i++)
+ {
+ free(autocomplete_strings[i]);
+ }
+ }
+
+ void FormCostCenterCombo(char* costcenter_id)
+ {
+ u32 costcenter_count = administration_cost_center_count();
+ cost_center* buffer = (cost_center*) malloc(sizeof(cost_center) * costcenter_count);
+
+ cost_center* selected_costcenter = NULL;
+ costcenter_count = administration_cost_center_get_all(buffer);
+
+ // Select cost center by given id.
+ if (strlen(costcenter_id) > 0)
+ {
+ for (u32 i = 0; i < costcenter_count; i++)
+ {
+ if (strcmp(buffer[i].id, costcenter_id) == 0)
+ {
+ selected_costcenter = &buffer[i];
+ break;
+ }
+ }
+ }
+
+ int selected_costcenter_index = -1;
+ if (ImGui::BeginCombo(localize("invoice.form.costcenter"), selected_costcenter == NULL ? NULL : localize(selected_costcenter->description)))
+ {
+ for (u32 n = 0; n < costcenter_count; n++)
+ {
+ bool is_selected = selected_costcenter && strcmp(selected_costcenter->id, buffer[n].id) == 0;
+ if (ImGui::Selectable(localize(buffer[n].description), is_selected)) {
+ selected_costcenter_index = n;
+ }
+ }
+ ImGui::EndCombo();
+ }
+
+ if (selected_costcenter_index != -1) {
+ strops_copy(costcenter_id, buffer[selected_costcenter_index].id, MAX_LEN_ID);
+ }
+
+ free(buffer);
+ }
+
+ void FormProjectCombo(char* project_id)
+ {
+ u32 project_count = administration_project_count();
+ project* buffer = (project*) malloc(sizeof(project) * project_count);
+
+ project* selected_project = NULL;
+ project_count = administration_project_get_all(buffer);
+
+ // Select project by given id.
+ if (strlen(project_id) > 0)
+ {
+ for (u32 i = 0; i < project_count; i++)
+ {
+ if (strcmp(buffer[i].id, project_id) == 0)
+ {
+ selected_project = &buffer[i];
+ break;
+ }
+ }
+ }
+
+ int selected_project_index = -1;
+ if (ImGui::BeginCombo(localize("invoice.form.project"), selected_project == NULL ? NULL : selected_project->description))
+ {
+ for (u32 n = 0; n < project_count; n++)
+ {
+ bool is_selected = selected_project && strcmp(selected_project->id, buffer[n].id) == 0;
+ if (ImGui::Selectable(buffer[n].description, is_selected)) {
+ selected_project_index = n;
+ }
+ }
+ ImGui::EndCombo();
+ }
+
+ if (selected_project_index != -1) {
+ strops_copy(project_id, buffer[selected_project_index].id, MAX_LEN_ID);
+ }
+
+ free(buffer);
+ }
+
+ void FormTaxRateCombo(char* tax_rate_id, char* orig_country, char* dest_country)
+ {
+ u32 tax_rate_count = administration_tax_rate_count();
+ tax_rate* buffer = (tax_rate*) malloc(sizeof(tax_rate) * tax_rate_count);
+
+ tax_rate* selected_tax_rate = NULL;
+ char* tax_country_codes[2] = {orig_country, dest_country};
+ tax_rate_count = administration_tax_rate_get_by_country(buffer, strcmp(orig_country, dest_country) == 0 ? 1 : 2, tax_country_codes);
+
+ // Select tax rate by given id.
+ if (strlen(tax_rate_id) > 0)
+ {
+ for (u32 i = 0; i < tax_rate_count; i++)
+ {
+ if (strcmp(buffer[i].id, tax_rate_id) == 0)
+ {
+ selected_tax_rate = &buffer[i];
+ break;
+ }
+ }
+ }
+
+ int selected_tax_rate_index = -1;
+ char rate_str_buf[40];
+ rate_str_buf[0] = 0;
+ if (selected_tax_rate)
+ {
+ if (strcmp(selected_tax_rate->country_code, "00") == 0) {
+ char category_code_desc[MAX_LEN_LONG_DESC];
+ snprintf(category_code_desc, MAX_LEN_LONG_DESC, "taxcategory.%s", selected_tax_rate->category_code);
+ snprintf(rate_str_buf, 40, "%s", localize(category_code_desc));
+ }
+ else snprintf(rate_str_buf, 40, "%s/%.1f%%", selected_tax_rate->country_code, selected_tax_rate->rate);
+ }
+
+ if (ImGui::BeginCombo("##Tax Bracket", rate_str_buf))
+ {
+ for (u32 n = 0; n < tax_rate_count; n++)
+ {
+ bool is_selected = selected_tax_rate && strcmp(selected_tax_rate->id, buffer[n].id) == 0;
+
+ if (strcmp(buffer[n].country_code, "00") == 0) {
+ char category_code_desc[MAX_LEN_LONG_DESC];
+ snprintf(category_code_desc, MAX_LEN_LONG_DESC, "taxcategory.%s", buffer[n].category_code);
+ snprintf(rate_str_buf, 40, "%s", localize(category_code_desc));
+ }
+ else snprintf(rate_str_buf, 40, "%s/%.1f%%", buffer[n].country_code, buffer[n].rate);
+
+ if (ImGui::Selectable(rate_str_buf, is_selected)) {
+ selected_tax_rate_index = n;
+ }
+ }
+ ImGui::EndCombo();
+ }
+
+ if (selected_tax_rate_index != -1) {
+ strops_copy(tax_rate_id, buffer[selected_tax_rate_index].id, MAX_LEN_ID);
+ }
+
+ free(buffer);
+ }
+
+ bool FormCurrencyCombo(char* currency)
+ {
+ int currentCurrency = 0;
+ bool result = false;
+
+ // Top 15 most traded currencies + all EU official currencies
+ const char* currencies[] = {
+ // Top 15
+ "EUR", "USD", "JPY", "GBP", "AUD", "CAD", "CHF",
+ "CNY", "HKD", "NZD", "SEK", "KRW", "SGD", "NOK", "MXN",
+
+ // Additional EU currencies
+ "BGN", // Bulgarian Lev
+ "CZK", // Czech Koruna
+ "DKK", // Danish Krone
+ "HUF", // Hungarian Forint
+ "PLN", // Polish Zloty
+ "RON", // Romanian Leu
+ // "HRK", // Croatian Kuna (legacy, replaced by EUR in 2023)
+ };
+ int currency_count = sizeof(currencies) / sizeof(char*);
+
+ if (strlen(currency) > 0)
+ {
+ for (int i = 0; i < currency_count; i++)
+ {
+ if (strcmp(currencies[i], currency) == 0)
+ {
+ currentCurrency = i;
+ break;
+ }
+ }
+ }
+
+ ImGui::SetNextItemWidth(100.0f);
+ if (ImGui::BeginCombo("##currency", currencies[currentCurrency]))
+ {
+ for (int n = 0; n < IM_ARRAYSIZE(currencies); n++)
+ {
+ bool isSelected = (currentCurrency == n);
+ if (ImGui::Selectable(currencies[n], isSelected))
+ {
+ result = true;
+ strops_copy(currency, currencies[n], MAX_LEN_CURRENCY);
+ }
+
+ if (isSelected)
+ ImGui::SetItemDefaultFocus();
+ }
+ ImGui::EndCombo();
+ }
+
+ return result;
+ }
+
+ void FormToggleCombo(bool *buffer, char* option1, char* option2)
+ {
+ const char* items[] = { option1, option2 };
+ if (ImGui::BeginCombo("Mode", items[*buffer])) {
+ for (int n = 0; n < 2; n++) {
+ bool is_selected = (n == (int)*buffer);
+ if (ImGui::Selectable(items[n], is_selected)) {
+ *buffer = n;
+ }
+ if (is_selected) {
+ ImGui::SetItemDefaultFocus();
+ }
+ }
+ ImGui::EndCombo();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/ui/ui_contacts.cpp b/src/ui/ui_contacts.cpp
index 92dd21a..b471f58 100644
--- a/src/ui/ui_contacts.cpp
+++ b/src/ui/ui_contacts.cpp
@@ -18,6 +18,7 @@
#include <stdlib.h>
#include "strops.hpp"
+#include "config.hpp"
#include "ui.hpp"
#include "imgui.h"
#include "administration.hpp"
@@ -29,76 +30,15 @@ static contact selected_for_removal;
static contact active_contact;
-void ui_draw_address_form(address* buffer)
+void ui_draw_address_form(address* buffer, a_err last_err)
{
- 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;
- }
- }
- }
+ ImGui::FormInputTextWithErrorHint(localize("contact.form.address1"), buffer, buffer->address1, IM_ARRAYSIZE(buffer->address1), last_err & A_ERR_MISSING_ADDRESS1);
+ ImGui::FormInputTextWithErrorHint(localize("contact.form.address2"), buffer, buffer->address2, IM_ARRAYSIZE(buffer->address2), 0);
+ ImGui::FormInputTextWithErrorHint(localize("contact.form.city"), buffer, buffer->city, IM_ARRAYSIZE(buffer->city), last_err & A_ERR_MISSING_CITY);
+ ImGui::FormInputTextWithErrorHint(localize("contact.form.postal"), buffer, buffer->postal, IM_ARRAYSIZE(buffer->postal), last_err & A_ERR_MISSING_POSTAL);
+ ImGui::FormInputTextWithErrorHint(localize("contact.form.region"), buffer, buffer->region, IM_ARRAYSIZE(buffer->region), 0);
- 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();
+ ImGui::FormCountryCombo(buffer, buffer->country_code, IM_ARRAYSIZE(buffer->country_code));
}
void ui_setup_contacts()
@@ -108,89 +48,34 @@ void ui_setup_contacts()
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)
+void draw_contact_form_ex(contact* buffer, bool viewing_only = false, bool with_autocomplete = false)
{
+ a_err last_err = administration_contact_is_valid(*buffer);
+
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 (with_autocomplete) ImGui::FormContactAutocomplete(buffer, last_err & A_ERR_MISSING_NAME);
+ else ImGui::FormInputTextWithErrorHint(localize("contact.form.fullname"), buffer, buffer->name, IM_ARRAYSIZE(buffer->name), last_err & A_ERR_MISSING_NAME);
- if (autocomplete_index != -1)
- {
- memcpy(buffer, &autocomplete_list[autocomplete_index], sizeof(contact));
- }
+ ui_draw_address_form(&buffer->address, last_err);
- 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<int>(buffer->type);
- if (ImGui::Combo(localize("contact.form.type"), &currentItem, customer_types, IM_ARRAYSIZE(customer_types)))
- {
- buffer->type = static_cast<contact_type>(currentItem);
- }
+ ImGui::FormContactTypeCombo(&buffer->type);
// 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));
+ ImGui::FormInputTextWithErrorHint(localize("contact.form.taxnumber"), buffer, buffer->taxid, IM_ARRAYSIZE(buffer->taxid), last_err & A_ERR_MISSING_TAXID);
+ ImGui::FormInputTextWithErrorHint(localize("contact.form.businessnumber"), buffer, buffer->businessid, IM_ARRAYSIZE(buffer->businessid), last_err & A_ERR_MISSING_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));
+ ImGui::FormInputTextWithErrorHint(localize("contact.form.email"), buffer, buffer->email, IM_ARRAYSIZE(buffer->email), last_err & A_ERR_MISSING_EMAIL);
+ ImGui::FormInputTextWithErrorHint(localize("contact.form.phonenumber"), buffer, buffer->phone_number, IM_ARRAYSIZE(buffer->phone_number), 0);
+ ImGui::FormInputTextWithErrorHint(localize("contact.form.bankaccount"), buffer, buffer->bank_account, IM_ARRAYSIZE(buffer->bank_account), 0);
if (viewing_only) ImGui::EndDisabled();
ImGui::PopID();
@@ -198,7 +83,7 @@ void draw_contact_form_ex(contact* buffer, bool viewing_only = false, bool with_
void draw_contact_form(contact* buffer, bool viewing_only = false)
{
- draw_contact_form_ex(buffer, viewing_only, false, 0);
+ draw_contact_form_ex(buffer, viewing_only, false);
}
@@ -313,7 +198,8 @@ static void ui_draw_contacts_create()
draw_contact_form(&active_contact);
- bool can_save = administration_contact_is_valid(active_contact) == A_ERR_SUCCESS;
+ a_err contact_validation_err = administration_contact_is_valid(active_contact);
+ bool can_save = contact_validation_err == A_ERR_SUCCESS;
if (!can_save) ImGui::BeginDisabled();
// Save button
ImGui::Spacing();
@@ -332,7 +218,8 @@ static void ui_draw_contacts_update()
draw_contact_form(&active_contact);
- bool can_save = administration_contact_is_valid(active_contact) == A_ERR_SUCCESS;
+ a_err contact_validation_err = administration_contact_is_valid(active_contact);
+ bool can_save = contact_validation_err == A_ERR_SUCCESS;
if (!can_save) ImGui::BeginDisabled();
// Save button
ImGui::Spacing();
diff --git a/src/ui/ui_expenses.cpp b/src/ui/ui_expenses.cpp
index b195442..7d94eaf 100644
--- a/src/ui/ui_expenses.cpp
+++ b/src/ui/ui_expenses.cpp
@@ -32,94 +32,13 @@ static view_state current_view_state = view_state::LIST;
static invoice active_invoice = {0};
static invoice selected_for_removal = {0};
-static cost_center* cost_center_list_buffer = 0;
-static tax_rate* tax_rate_list_buffer = 0;
-static project* project_list_buffer = 0;
static billing_item* invoice_items_buffer = 0;
-void ui_draw_address_form(address* buffer);
-void draw_contact_form_ex(contact* buffer, bool viewing_only = false, bool with_autocomplete = false, bool* on_autocomplete = 0);
-void draw_tax_rate_selector(char* tax_rate_id, tax_rate* buffer, char* orig_country, char* dest_country);
-bool draw_currency_selector(char* currency);
-void draw_invoice_items_form(invoice* invoice, bool is_outgoing);
-
-void draw_costcenter_selector(char* costcenter_id, cost_center* buffer)
-{
- cost_center* selected_costcenter = NULL;
- u32 costcenter_count = administration_cost_center_get_all(buffer);
-
- // Select cost center by given id.
- if (strlen(costcenter_id) > 0)
- {
- for (u32 i = 0; i < costcenter_count; i++)
- {
- if (strcmp(buffer[i].id, costcenter_id) == 0)
- {
- selected_costcenter = &buffer[i];
- break;
- }
- }
- }
-
- int selected_costcenter_index = -1;
- if (ImGui::BeginCombo(localize("invoice.form.costcenter"), selected_costcenter == NULL ? NULL : localize(selected_costcenter->description)))
- {
- for (u32 n = 0; n < costcenter_count; n++)
- {
- bool is_selected = selected_costcenter && strcmp(selected_costcenter->id, buffer[n].id) == 0;
- if (ImGui::Selectable(localize(buffer[n].description), is_selected)) {
- selected_costcenter_index = n;
- }
- }
- ImGui::EndCombo();
- }
-
- if (selected_costcenter_index != -1) {
- strops_copy(costcenter_id, buffer[selected_costcenter_index].id, MAX_LEN_ID);
- }
-}
-
-void draw_project_selector(char* project_id, project* buffer)
-{
- project* selected_project = NULL;
- u32 project_count = administration_project_get_all(buffer);
-
- // Select project by given id.
- if (strlen(project_id) > 0)
- {
- for (u32 i = 0; i < project_count; i++)
- {
- if (strcmp(buffer[i].id, project_id) == 0)
- {
- selected_project = &buffer[i];
- break;
- }
- }
- }
-
- int selected_project_index = -1;
- if (ImGui::BeginCombo(localize("invoice.form.project"), selected_project == NULL ? NULL : selected_project->description))
- {
- for (u32 n = 0; n < project_count; n++)
- {
- bool is_selected = selected_project && strcmp(selected_project->id, buffer[n].id) == 0;
- if (ImGui::Selectable(buffer[n].description, is_selected)) {
- selected_project_index = n;
- }
- }
- ImGui::EndCombo();
- }
-
- if (selected_project_index != -1) {
- strops_copy(project_id, buffer[selected_project_index].id, MAX_LEN_ID);
- }
-}
+void draw_contact_form_ex(contact* buffer, bool viewing_only = false, bool with_autocomplete = false);
+void draw_invoice_items_form(invoice* invoice);
void ui_destroy_expenses()
{
- free(cost_center_list_buffer);
- free(tax_rate_list_buffer);
- free(project_list_buffer);
free(invoice_items_buffer);
}
@@ -128,15 +47,6 @@ void ui_setup_expenses()
current_view_state = view_state::LIST;
active_invoice = administration_invoice_create_empty();
- u32 costcenter_count = administration_cost_center_count();
- cost_center_list_buffer = (cost_center*) malloc(sizeof(cost_center) * costcenter_count);
-
- u32 tax_rate_count = administration_tax_rate_count();
- tax_rate_list_buffer = (tax_rate*) malloc(sizeof(tax_rate) * tax_rate_count);
-
- u32 project_count = administration_project_count();
- project_list_buffer = (project*) malloc(sizeof(project) * project_count);
-
u32 invoice_items_count = MAX_BILLING_ITEMS;
invoice_items_buffer = (billing_item*)malloc(sizeof(billing_item) * invoice_items_count);
}
@@ -145,17 +55,9 @@ static void draw_expense_form(invoice* buffer, bool viewing_only = false)
{
if (viewing_only) ImGui::BeginDisabled();
- // 1. Identifier
- //ImGui::SetNextItemWidth(widthAvailable*0.2f);
- //ImGui::InputText(localize("contact.form.identifier"), buffer->id, IM_ARRAYSIZE(buffer->id));
-
- // 2. Sequential number
ImGui::Text("%s: %s", localize("invoice.form.invoicenumber"), buffer->sequential_number);
-
- // 3. Billed to (you)
ImGui::Text("%s: %s", localize("invoice.form.billedTo"), buffer->customer.name);
- // 4. Invoice issued at
tm issued_at_date = *gmtime(&buffer->issued_at);
if (ImGui::DatePicker("##issuedAt", issued_at_date))
{
@@ -164,7 +66,6 @@ static void draw_expense_form(invoice* buffer, bool viewing_only = false)
ImGui::SameLine();
ImGui::Text(localize("invoice.form.issuedat"));
- // 5. Invoice expires at
tm expires_at_date = *gmtime(&buffer->expires_at);
if (ImGui::DatePicker("##expiresAt", expires_at_date))
{
@@ -173,7 +74,6 @@ static void draw_expense_form(invoice* buffer, bool viewing_only = false)
ImGui::SameLine();
ImGui::Text(localize("invoice.form.expiresat"));
- // 6. Product/service delivered at
tm delivered_at_date = *gmtime(&buffer->delivered_at);
if (ImGui::DatePicker("##deliveredAt", delivered_at_date))
{
@@ -184,31 +84,26 @@ static void draw_expense_form(invoice* buffer, bool viewing_only = false)
ImGui::Separator();
- // 7. Supplier information
ImGui::Text(localize("invoice.form.supplier"));
- draw_contact_form_ex(&buffer->supplier, false, true, 0);
+ draw_contact_form_ex(&buffer->supplier, false, true);
- // 8. (optional) shipping address.
ImGui::Checkbox(localize("invoice.form.triangulation"), &buffer->is_triangulation);
if (buffer->is_triangulation) {
ImGui::Spacing();
ImGui::Text(localize("invoice.form.shippinginformation"));
- draw_contact_form_ex(&buffer->addressee, 0,0,0);
+ draw_contact_form_ex(&buffer->addressee, 0,0);
}
ImGui::Separator();
- // 9. Project selection
- draw_project_selector(buffer->project_id, project_list_buffer);
+ ImGui::FormProjectCombo(buffer->project_id);
+ ImGui::FormCostCenterCombo(buffer->cost_center_id);
- // 10. Cost center selection
- draw_costcenter_selector(buffer->cost_center_id, cost_center_list_buffer);
ImGui::Separator();
ImGui::Spacing();
ImGui::Spacing();
ImGui::Spacing();
- // 11. New billing item button.
bool max_items_reached = administration_billing_item_count(buffer) >= MAX_BILLING_ITEMS;
if (max_items_reached) ImGui::BeginDisabled();
if (ImGui::Button(localize(localize("invoice.form.add"))))
@@ -218,17 +113,15 @@ static void draw_expense_form(invoice* buffer, bool viewing_only = false)
}
if (max_items_reached) ImGui::EndDisabled();
- // 12. Dropdown for invoice currency.
ImGui::SameLine();
ImGui::Text("| %s: ", localize("invoice.form.currency"));
ImGui::SameLine();
- if (draw_currency_selector(buffer->currency))
+ if (ImGui::FormCurrencyCombo(buffer->currency))
{
administration_invoice_set_currency(buffer, buffer->currency);
}
- // 13. Invoice items form
- draw_invoice_items_form(buffer, false);
+ draw_invoice_items_form(buffer);
if (viewing_only) ImGui::EndDisabled();
}
diff --git a/src/ui/ui_invoices.cpp b/src/ui/ui_invoices.cpp
index c3c6b8c..f061e3f 100644
--- a/src/ui/ui_invoices.cpp
+++ b/src/ui/ui_invoices.cpp
@@ -33,19 +33,13 @@ static view_state current_view_state = view_state::LIST;
static invoice active_invoice = {0};
static invoice selected_for_removal = {0};
-static tax_rate* tax_rate_list_buffer = 0;
static billing_item* invoice_items_buffer = 0;
-static project* project_list_buffer = 0;
-void ui_draw_address_form(address* buffer);
-void draw_contact_form_ex(contact* buffer, bool viewing_only = false, bool with_autocomplete = false, bool* on_autocomplete = 0);
-void draw_project_selector(char* project_id, project* buffer);
+void draw_contact_form_ex(contact* buffer, bool viewing_only = false, bool with_autocomplete = false);
void ui_destroy_invoices()
{
- free(tax_rate_list_buffer);
free(invoice_items_buffer);
- free(project_list_buffer);
}
void ui_setup_invoices()
@@ -53,129 +47,11 @@ void ui_setup_invoices()
current_view_state = view_state::LIST;
active_invoice = administration_invoice_create_empty();
- u32 tax_rate_count = administration_tax_rate_count();
- tax_rate_list_buffer = (tax_rate*) malloc(sizeof(tax_rate) * tax_rate_count);
-
u32 invoice_items_count = MAX_BILLING_ITEMS;
invoice_items_buffer = (billing_item*)malloc(sizeof(billing_item) * invoice_items_count);
-
- u32 project_count = administration_project_count();
- project_list_buffer = (project*) malloc(sizeof(project) * project_count);
-}
-
-void draw_tax_rate_selector(char* tax_rate_id, tax_rate* buffer, char* orig_country, char* dest_country)
-{
- tax_rate* selected_tax_rate = NULL;
- char* country_codes[2] = {orig_country, dest_country};
- u32 tax_rate_count = administration_tax_rate_get_by_country(buffer, 2, country_codes);
-
- // Select tax rate by given id.
- if (strlen(tax_rate_id) > 0)
- {
- for (u32 i = 0; i < tax_rate_count; i++)
- {
- if (strcmp(buffer[i].id, tax_rate_id) == 0)
- {
- selected_tax_rate = &buffer[i];
- break;
- }
- }
- }
-
- int selected_tax_rate_index = -1;
- char rate_str_buf[40];
- rate_str_buf[0] = 0;
- if (selected_tax_rate)
- {
- if (strcmp(selected_tax_rate->country_code, "00") == 0) {
- char category_code_desc[MAX_LEN_LONG_DESC];
- snprintf(category_code_desc, MAX_LEN_LONG_DESC, "taxcategory.%s", selected_tax_rate->category_code);
- snprintf(rate_str_buf, 40, "%s", localize(category_code_desc));
- }
- else snprintf(rate_str_buf, 40, "%s/%.1f%%", selected_tax_rate->country_code, selected_tax_rate->rate);
- }
-
- if (ImGui::BeginCombo("##Tax Bracket", rate_str_buf))
- {
- for (u32 n = 0; n < tax_rate_count; n++)
- {
- bool is_selected = selected_tax_rate && strcmp(selected_tax_rate->id, buffer[n].id) == 0;
-
- if (strcmp(buffer[n].country_code, "00") == 0) {
- char category_code_desc[MAX_LEN_LONG_DESC];
- snprintf(category_code_desc, MAX_LEN_LONG_DESC, "taxcategory.%s", buffer[n].category_code);
- snprintf(rate_str_buf, 40, "%s", localize(category_code_desc));
- }
- else snprintf(rate_str_buf, 40, "%s/%.1f%%", buffer[n].country_code, buffer[n].rate);
-
- if (ImGui::Selectable(rate_str_buf, is_selected)) {
- selected_tax_rate_index = n;
- }
- }
- ImGui::EndCombo();
- }
-
- if (selected_tax_rate_index != -1) {
- strops_copy(tax_rate_id, buffer[selected_tax_rate_index].id, MAX_LEN_ID);
- }
}
-bool draw_currency_selector(char* currency)
-{
- int currentCurrency = 0;
- bool result = false;
-
- // Top 15 most traded currencies + all EU official currencies
- const char* currencies[] = {
- // Top 15
- "EUR", "USD", "JPY", "GBP", "AUD", "CAD", "CHF",
- "CNY", "HKD", "NZD", "SEK", "KRW", "SGD", "NOK", "MXN",
-
- // Additional EU currencies
- "BGN", // Bulgarian Lev
- "CZK", // Czech Koruna
- "DKK", // Danish Krone
- "HUF", // Hungarian Forint
- "PLN", // Polish Zloty
- "RON", // Romanian Leu
- // "HRK", // Croatian Kuna (legacy, replaced by EUR in 2023)
- };
- int currency_count = sizeof(currencies) / sizeof(char*);
-
- if (strlen(currency) > 0)
- {
- for (int i = 0; i < currency_count; i++)
- {
- if (strcmp(currencies[i], currency) == 0)
- {
- currentCurrency = i;
- break;
- }
- }
- }
-
- ImGui::SetNextItemWidth(100.0f);
- if (ImGui::BeginCombo("##currency", currencies[currentCurrency]))
- {
- for (int n = 0; n < IM_ARRAYSIZE(currencies); n++)
- {
- bool isSelected = (currentCurrency == n);
- if (ImGui::Selectable(currencies[n], isSelected))
- {
- result = true;
- strops_copy(currency, currencies[n], MAX_LEN_CURRENCY);
- }
-
- if (isSelected)
- ImGui::SetItemDefaultFocus();
- }
- ImGui::EndCombo();
- }
-
- return result;
-}
-
-void draw_invoice_items_form(invoice* invoice, bool is_outgoing)
+void draw_invoice_items_form(invoice* invoice)
{
billing_item* buffer = invoice_items_buffer;
u32 invoice_items = administration_billing_item_get_all_for_invoice(invoice, buffer);
@@ -212,22 +88,7 @@ void draw_invoice_items_form(invoice* invoice, bool is_outgoing)
ImGui::InputFloat("##amount", &item.amount, 0.0f, 0.0f, "%.0f");
ImGui::SameLine();
- // Toggle between X and %
- {
- const char* items[] = { "X", "%" };
- if (ImGui::BeginCombo("Mode", items[item.amount_is_percentage])) {
- for (int n = 0; n < 2; n++) {
- bool is_selected = (n == (int)item.amount_is_percentage);
- if (ImGui::Selectable(items[n], is_selected)) {
- item.amount_is_percentage = n;
- }
- if (is_selected) {
- ImGui::SetItemDefaultFocus();
- }
- }
- ImGui::EndCombo();
- }
- }
+ ImGui::FormToggleCombo(&item.amount_is_percentage, "X", "%");
ImGui::TableSetColumnIndex(2);
ImGui::PushItemWidth(-1);
@@ -243,31 +104,15 @@ void draw_invoice_items_form(invoice* invoice, bool is_outgoing)
ImGui::InputFloat("##discount", &item.discount, 0.0f, 0.0f, "%.2f");
ImGui::SameLine();
- // Toggle between currency and %
- {
- const char* items[] = { item.currency, "%" };
- if (ImGui::BeginCombo("Mode##discountMode", items[item.discount_is_percentage])) {
- for (int n = 0; n < 2; n++) {
- bool is_selected = (n == (int)item.discount_is_percentage);
- if (ImGui::Selectable(items[n], is_selected)) {
- item.discount_is_percentage = n;
- }
- if (is_selected) {
- ImGui::SetItemDefaultFocus();
- }
- }
- ImGui::EndCombo();
- }
- }
+ ImGui::FormToggleCombo(&item.amount_is_percentage, item.currency, "%");
ImGui::TableSetColumnIndex(5);
ImGui::Text("%.2f %s", item.net, item.currency);
ImGui::TableSetColumnIndex(6);
ImGui::PushItemWidth(-1);
- draw_tax_rate_selector(item.tax_rate_id, tax_rate_list_buffer,
- administration_company_info_get().address.country_code,
- is_outgoing ? invoice->customer.address.country_code : invoice->supplier.address.country_code);
+ ImGui::FormTaxRateCombo(item.tax_rate_id, invoice->customer.address.country_code, invoice->supplier.address.country_code);
+
ImGui::PopItemWidth();
ImGui::TableSetColumnIndex(7);
@@ -329,17 +174,9 @@ static void draw_invoice_form(invoice* buffer, bool viewing_only = false)
{
ImGui::BeginDisabled();
- // 1. Identifier
- //ImGui::SetNextItemWidth(widthAvailable*0.2f);
- //ImGui::InputText(localize("contact.form.identifier"), buffer->id, IM_ARRAYSIZE(buffer->id));
-
- // 2. Sequential number
ImGui::Text("%s: %s", localize("invoice.form.invoicenumber"), buffer->sequential_number);
-
- // 3. Supplier (you)
ImGui::Text("%s: %s", localize("invoice.form.supplier"), buffer->supplier.name);
- // 4. Invoice issued at
tm issued_at_date = *gmtime(&buffer->issued_at);
if (ImGui::DatePicker("##issuedAt", issued_at_date))
{
@@ -348,7 +185,6 @@ static void draw_invoice_form(invoice* buffer, bool viewing_only = false)
ImGui::SameLine();
ImGui::Text(localize("invoice.form.issuedat"));
- // 5. Invoice expires at
tm expires_at_date = *gmtime(&buffer->expires_at);
if (ImGui::DatePicker("##expiresAt", expires_at_date))
{
@@ -358,7 +194,6 @@ static void draw_invoice_form(invoice* buffer, bool viewing_only = false)
ImGui::Text(localize("invoice.form.expiresat"));
if (!viewing_only) ImGui::EndDisabled();
- // 6. Product/service delivered at
tm delivered_at_date = *gmtime(&buffer->delivered_at);
if (ImGui::DatePicker("##deliveredAt", delivered_at_date))
{
@@ -369,27 +204,23 @@ static void draw_invoice_form(invoice* buffer, bool viewing_only = false)
ImGui::Separator();
- // 7. Customer information
ImGui::Text(localize("invoice.form.billinginformation"));
- draw_contact_form_ex(&buffer->customer, false, true, 0);
+ draw_contact_form_ex(&buffer->customer, false, true);
- // 8. (optional) shipping address.
ImGui::Checkbox(localize("invoice.form.triangulation"), &buffer->is_triangulation);
if (buffer->is_triangulation) {
ImGui::Spacing();
ImGui::Text(localize("invoice.form.shippinginformation"));
- draw_contact_form_ex(&buffer->addressee, 0,0,0);
+ draw_contact_form_ex(&buffer->addressee, 0,0);
}
ImGui::Separator();
- // 9. Project selection
- draw_project_selector(buffer->project_id, project_list_buffer);
+ ImGui::FormProjectCombo(buffer->project_id);
ImGui::Spacing();
ImGui::Spacing();
ImGui::Spacing();
- // 11. New billing item button.
bool max_items_reached = administration_billing_item_count(buffer) >= MAX_BILLING_ITEMS;
if (max_items_reached) ImGui::BeginDisabled();
if (ImGui::Button(localize(localize("invoice.form.add"))))
@@ -399,17 +230,15 @@ static void draw_invoice_form(invoice* buffer, bool viewing_only = false)
}
if (max_items_reached) ImGui::EndDisabled();
- // 12. Dropdown for invoice currency.
ImGui::SameLine();
ImGui::Text("| %s: ", localize("invoice.form.currency"));
ImGui::SameLine();
- if (draw_currency_selector(buffer->currency))
+ if (ImGui::FormCurrencyCombo(buffer->currency))
{
administration_invoice_set_currency(buffer, buffer->currency);
}
- // 13. Invoice items form
- draw_invoice_items_form(buffer, true);
+ draw_invoice_items_form(buffer);
if (viewing_only) ImGui::EndDisabled();
}
diff --git a/src/ui/ui_projects.cpp b/src/ui/ui_projects.cpp
index dddf881..37cf5d8 100644
--- a/src/ui/ui_projects.cpp
+++ b/src/ui/ui_projects.cpp
@@ -35,8 +35,10 @@ void ui_setup_projects()
static void draw_project_form()
{
+ float widthAvailable = ImGui::GetContentRegionAvail().x;
+ bool viewing_only = (current_view_state == view_state::VIEW);
static const char* selected_country = NULL;
-
+
if (ImGui::Button(localize("form.back"))) {
current_view_state = view_state::LIST;
active_project = administration_project_create_empty();
@@ -44,20 +46,15 @@ static void draw_project_form()
return;
}
ImGui::Spacing();
-
- bool viewing_only = (current_view_state == view_state::VIEW);
-
ImGui::BeginDisabled();
-
- float widthAvailable = ImGui::GetContentRegionAvail().x;
+
+ a_err last_err = administration_project_is_valid(active_project);
ImGui::SetNextItemWidth(widthAvailable*0.2f);
ImGui::InputText(localize("project.form.identifier"), active_project.id, IM_ARRAYSIZE(active_project.id));
if (!viewing_only) ImGui::EndDisabled();
-
- ImGui::SetNextItemWidth(widthAvailable*0.5f);
- ImGui::InputTextWithHint(localize("project.form.description"), localize("project.form.description"), active_project.description, IM_ARRAYSIZE(active_project.description));
- ImGui::SameLine();ui_helper_draw_required_tag();
+
+ ImGui::FormInputTextWithErrorHint(localize("project.form.description"), &active_project, active_project.description, IM_ARRAYSIZE(active_project.description), last_err & A_ERR_MISSING_DESCRIPTION);
if (viewing_only) ImGui::EndDisabled();