diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/administration.cpp | 99 | ||||
| -rw-r--r-- | src/administration.hpp | 39 | ||||
| -rw-r--r-- | src/locales/en.cpp | 12 | ||||
| -rw-r--r-- | src/views/contacts.cpp | 72 | ||||
| -rw-r--r-- | src/views/dashboard.cpp | 23 | ||||
| -rw-r--r-- | src/views/projects.cpp | 173 | ||||
| -rw-r--r-- | src/views/views.cpp | 28 | ||||
| -rw-r--r-- | src/views/views.hpp | 13 |
8 files changed, 384 insertions, 75 deletions
diff --git a/src/administration.cpp b/src/administration.cpp index 052f007..9362b8d 100644 --- a/src/administration.cpp +++ b/src/administration.cpp @@ -1,3 +1,8 @@ +#include <string.h> +#include <stdlib.h> +#include <assert.h> +#include <time.h> + #include "administration.hpp" administration g_administration; @@ -5,12 +10,14 @@ administration g_administration; void administration_create() { list_init(&g_administration.contacts); + list_init(&g_administration.projects); strncpy(g_administration.path, "[unsaved project]", sizeof(g_administration.path)); // @localize } void administration_destroy() { list_destroy(&g_administration.contacts); + list_destroy(&g_administration.projects); } bool administration_create_contact(contact data) @@ -93,4 +100,96 @@ u32 administration_get_contacts(u32 page_index, u32 page_size, contact* buffer) char* administration_get_file_path() { return g_administration.path; +} + +u32 administration_get_project_count() +{ + return list_size(&g_administration.projects); +} + +u32 administration_get_projects(u32 page_index, u32 page_size, project* buffer) +{ + assert(buffer); + + u32 write_cursor = 0; + + u32 read_start = page_index * page_size; + + list_iterator_start(&g_administration.projects); + while (list_iterator_hasnext(&g_administration.projects)) { + project c = *(project *)list_iterator_next(&g_administration.projects); + + if (g_administration.projects.iter_pos <= read_start) continue; + + buffer[write_cursor++] = c; + if (write_cursor >= page_size) break; + } + list_iterator_stop(&g_administration.projects); + + return write_cursor; +} + +void administration_cancel_project(project data) +{ + data.end_date = time(NULL); + data.state = project_state::CANCELLED; + administration_update_project(data); +} + +char* administration_project_get_status_string(project data) +{ + switch(data.state) + { + case project_state::RUNNING: return "project.state.running"; break; + case project_state::PAUSED: return "project.state.paused"; break; + case project_state::CANCELLED: return "project.state.cancelled"; break; + } +} + +bool administration_create_project(project data) +{ + data.state = project_state::RUNNING; + data.start_date = time(NULL); + data.end_date = 0; + project* new_project = (project*)malloc(sizeof(project)); + memcpy((void*)new_project, (void*)&data, sizeof(project)); + list_append(&g_administration.projects, new_project); + + g_administration.next_id++; + + return true; +} + +bool administration_update_project(project data) +{ + list_iterator_start(&g_administration.projects); + while (list_iterator_hasnext(&g_administration.projects)) { + project* c = (project *)list_iterator_next(&g_administration.projects); + + if (strcmp(c->id, data.id) == 0) { + memcpy(c, &data, sizeof(data)); + list_iterator_stop(&g_administration.projects); + return true; + } + } + list_iterator_stop(&g_administration.projects); + + return false; +} + +bool administration_remove_project(project data) +{ + list_iterator_start(&g_administration.projects); + while (list_iterator_hasnext(&g_administration.projects)) { + project* c = (project *)list_iterator_next(&g_administration.projects); + + if (strcmp(c->id, data.id) == 0) { + list_iterator_stop(&g_administration.projects); + list_delete(&g_administration.projects, c); + return true; + } + } + list_iterator_stop(&g_administration.projects); + + return false; }
\ No newline at end of file diff --git a/src/administration.hpp b/src/administration.hpp index c20bbfb..9506246 100644 --- a/src/administration.hpp +++ b/src/administration.hpp @@ -1,10 +1,5 @@ #pragma once -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <assert.h> - #include "config.hpp" #include "simclist.h" @@ -22,6 +17,22 @@ typedef struct char bank_account[32]; } contact; +typedef enum +{ + RUNNING, + PAUSED, + CANCELLED, +} project_state; + +typedef struct +{ + char id[16]; + char description[64]; + project_state state; + time_t start_date; + time_t end_date; +} project; + typedef struct { contact company_info; @@ -30,7 +41,7 @@ typedef struct char program_version[10]; char country_code[2]; list_t contacts; - // projects + list_t projects; // invoices char ai_service[16]; char ai_key[32]; @@ -41,11 +52,19 @@ typedef struct void administration_create(); void administration_destroy(); +char* administration_get_file_path(); +s32 administration_create_id(); + bool administration_remove_contact(contact data); bool administration_create_contact(contact data); bool administration_update_contact(contact data); - -char* administration_get_file_path(); -s32 administration_create_id(); u32 administration_get_contact_count(); -u32 administration_get_contacts(u32 page_index, u32 page_size, contact* buffer); // Buffer size atleast be page_size * sizeof contact
\ No newline at end of file +u32 administration_get_contacts(u32 page_index, u32 page_size, contact* buffer); + +void administration_cancel_project(project data); +bool administration_remove_project(project data); +bool administration_create_project(project data); +bool administration_update_project(project data); +char* administration_project_get_status_string(project data); +u32 administration_get_project_count(); +u32 administration_get_projects(u32 page_index, u32 page_size, project* buffer);
\ No newline at end of file diff --git a/src/locales/en.cpp b/src/locales/en.cpp index de33655..2b2f2b6 100644 --- a/src/locales/en.cpp +++ b/src/locales/en.cpp @@ -10,7 +10,9 @@ locale_entry en_locales[] = { {"form.change", "Change"}, {"form.view", "View"}, {"form.delete", "Delete"}, + {"form.cancel", "Cancel"}, {"form.confirmDelete", "Are you sure you want to delete this item?"}, + {"form.confirmCancelProject", "Are you sure you want to cancel this Project?"}, {"form.required", "required"}, // Countries @@ -65,6 +67,16 @@ locale_entry en_locales[] = { {"contact.table.identifier", "Identifier"}, {"contact.table.name", "Name"}, {"contact.table.address", "Address"}, + + // Project strings. + {"contact.form.identifier", "Identifier"}, + {"project.form.description", "Description"}, + {"project.table.identifier", "Identifier"}, + {"project.table.status", "Status"}, + {"project.table.description", "Description"}, + {"project.state.running", "Running"}, + {"project.state.cancelled", "Cancelled"}, + {"project.state.paused", "Paused"}, }; const int en_locale_count = sizeof(en_locales) / sizeof(en_locales[0]);
\ No newline at end of file diff --git a/src/views/contacts.cpp b/src/views/contacts.cpp index 4b352bc..31f95e3 100644 --- a/src/views/contacts.cpp +++ b/src/views/contacts.cpp @@ -3,56 +3,26 @@ #include "views.hpp" #include "imgui.h" #include "../administration.hpp" +#include "../locales/locales.hpp" -typedef enum { - LIST, - EDIT, - CREATE, - VIEW, -} contact_view_state; - -static contact_view_state view_state = LIST; +static view_state current_view_state = LIST; static contact selected_for_removal; static contact active_contact; -static void 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(); -} - static void draw_contact_form() { static const char* selected_country = NULL; if (ImGui::Button(localize("form.back"))) { - view_state = contact_view_state::LIST; + current_view_state = view_state::LIST; memset(&active_contact, 0, sizeof(contact)); selected_country = 0; + return; } ImGui::Spacing(); - bool viewing_only = (view_state == contact_view_state::VIEW); + bool viewing_only = (current_view_state == view_state::VIEW); ImGui::BeginDisabled(); @@ -64,15 +34,15 @@ static void draw_contact_form() ImGui::SetNextItemWidth(widthAvailable*0.5f); ImGui::InputTextWithHint(localize("contact.form.fullname"), localize("contact.form.fullname"), active_contact.name, IM_ARRAYSIZE(active_contact.name)); - ImGui::SameLine();draw_required_tag(); + ImGui::SameLine();view_draw_required_tag(); ImGui::SetNextItemWidth(widthAvailable*0.5f); ImGui::InputTextWithHint(localize("contact.form.address1"), localize("contact.form.address1"), active_contact.address1, IM_ARRAYSIZE(active_contact.address1)); - ImGui::SameLine();draw_required_tag(); + ImGui::SameLine();view_draw_required_tag(); ImGui::SetNextItemWidth(widthAvailable*0.5f); ImGui::InputTextWithHint(localize("contact.form.address2"), localize("contact.form.address2"), active_contact.address2, IM_ARRAYSIZE(active_contact.address2)); - ImGui::SameLine();draw_required_tag(); + ImGui::SameLine();view_draw_required_tag(); ImGui::SetNextItemWidth(widthAvailable*0.5f); @@ -102,7 +72,7 @@ static void draw_contact_form() if (selected_country) { strncpy(active_contact.country, selected_country, IM_ARRAYSIZE(active_contact.country)); } - ImGui::SameLine();draw_required_tag(); + ImGui::SameLine();view_draw_required_tag(); ImGui::SetNextItemWidth(widthAvailable*0.5f); ImGui::InputTextWithHint(localize("contact.form.taxnumber"), localize("contact.form.taxnumber"), active_contact.taxid, IM_ARRAYSIZE(active_contact.taxid)); @@ -129,14 +99,14 @@ static void draw_contact_form() // Save button ImGui::Spacing(); if (ImGui::Button(localize("form.save"))) { - if (view_state == contact_view_state::CREATE) + if (current_view_state == view_state::CREATE) administration_create_contact(active_contact); - else if (view_state == contact_view_state::EDIT) + else if (current_view_state == view_state::EDIT) administration_update_contact(active_contact); memset(&active_contact, 0, sizeof(contact)); - view_state = contact_view_state::LIST; + current_view_state = view_state::LIST; selected_country = 0; } if (!can_save) ImGui::EndDisabled(); @@ -148,14 +118,14 @@ static void draw_contact_form() static void draw_contact_list() { - const u32 items_per_page = 5; + const u32 items_per_page = 50; static s32 current_page = 0; s32 max_page = (administration_get_contact_count() + items_per_page - 1) / items_per_page; if (max_page == 0) max_page = 1; if (ImGui::Button(localize("form.create"))) { - view_state = contact_view_state::CREATE; + current_view_state = view_state::CREATE; memset(&active_contact, 0, sizeof(contact)); snprintf(active_contact.id, IM_ARRAYSIZE(active_contact.id), "C/%d", administration_create_id()); } @@ -205,7 +175,7 @@ static void draw_contact_list() sprintf(btn_name, "%s##%d", localize("form.view"), i); if (ImGui::Button(btn_name)) { active_contact = c; - view_state = contact_view_state::VIEW; + current_view_state = view_state::VIEW; } ImGui::SameLine(); @@ -213,7 +183,7 @@ static void draw_contact_list() sprintf(btn_name, "%s##%d", localize("form.change"), i); if (ImGui::Button(btn_name)) { active_contact = c; - view_state = contact_view_state::EDIT; + current_view_state = view_state::EDIT; } ImGui::SameLine(); @@ -247,11 +217,11 @@ static void draw_contact_list() void views_draw_contacts() { - switch(view_state) + switch(current_view_state) { - case contact_view_state::LIST: draw_contact_list(); break; - case contact_view_state::CREATE: draw_contact_form(); break; - case contact_view_state::EDIT: draw_contact_form(); break; - case contact_view_state::VIEW: draw_contact_form(); break; + case view_state::LIST: draw_contact_list(); break; + case view_state::CREATE: draw_contact_form(); break; + case view_state::EDIT: draw_contact_form(); break; + case view_state::VIEW: draw_contact_form(); break; } }
\ No newline at end of file diff --git a/src/views/dashboard.cpp b/src/views/dashboard.cpp index 86dc18f..dcfd002 100644 --- a/src/views/dashboard.cpp +++ b/src/views/dashboard.cpp @@ -1,6 +1,7 @@ #include "views.hpp" #include "imgui.h" #include "../administration.hpp" +#include "../locales/locales.hpp" typedef enum { @@ -14,14 +15,14 @@ typedef enum END } dashboard_view_state; -static dashboard_view_state view_state = dashboard_view_state::INVOICES; +static dashboard_view_state dashboard_state = dashboard_view_state::INVOICES; void (*drawcalls[dashboard_view_state::END])(void) = { 0, 0, views_draw_contacts, 0, 0, - 0, + views_draw_projects, }; void views_draw_dashboard() @@ -58,21 +59,21 @@ void views_draw_dashboard() float buttonWidth = sidePanelWidth; - if (ImGui::Button(localize("nav.invoices"), ImVec2(buttonWidth, 24))) view_state = dashboard_view_state::INVOICES; - if (ImGui::Button(localize("nav.expenses"), ImVec2(buttonWidth, 24))) view_state = dashboard_view_state::EXPENSES; - if (ImGui::Button(localize("nav.contacts"), ImVec2(buttonWidth, 24))) view_state = dashboard_view_state::CONTACTS; + if (ImGui::Button(localize("nav.invoices"), ImVec2(buttonWidth, 24))) dashboard_state = dashboard_view_state::INVOICES; + if (ImGui::Button(localize("nav.expenses"), ImVec2(buttonWidth, 24))) dashboard_state = dashboard_view_state::EXPENSES; + if (ImGui::Button(localize("nav.contacts"), ImVec2(buttonWidth, 24))) dashboard_state = dashboard_view_state::CONTACTS; static bool reports_opened = false; if (ImGui::Button(localize("nav.reports"), ImVec2(buttonWidth, 24))) reports_opened = !reports_opened; if (reports_opened) { ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(20.0f, 0.0f)); - if (ImGui::Button(localize("nav.reports.results"), ImVec2(buttonWidth, 24))) view_state = dashboard_view_state::REPORT_RESULTS; - if (ImGui::Button(localize("nav.reports.tax"), ImVec2(buttonWidth, 24))) view_state = dashboard_view_state::REPORT_TAX; + if (ImGui::Button(localize("nav.reports.results"), ImVec2(buttonWidth, 24))) dashboard_state = dashboard_view_state::REPORT_RESULTS; + if (ImGui::Button(localize("nav.reports.tax"), ImVec2(buttonWidth, 24))) dashboard_state = dashboard_view_state::REPORT_TAX; ImGui::PopStyleVar(); } - if (ImGui::Button(localize("nav.Projects"), ImVec2(buttonWidth, 24))) view_state = dashboard_view_state::PROJECTS; + if (ImGui::Button(localize("nav.Projects"), ImVec2(buttonWidth, 24))) dashboard_state = dashboard_view_state::PROJECTS; ImGui::PopStyleColor(1); ImGui::PopStyleVar(3); @@ -85,7 +86,7 @@ void views_draw_dashboard() // Main content ImGui::Begin("AccountingMainWindow", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse); - if (drawcalls[view_state]) drawcalls[view_state](); + if (drawcalls[dashboard_state]) drawcalls[dashboard_state](); ImGui::End(); // Status bar. @@ -102,9 +103,7 @@ void views_draw_dashboard() ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoCollapse); - ImGui::Text("Working on: %s", administration_get_file_path()); - ImGui::SameLine(); - ImGui::Text("Status: []"); + ImGui::Text("Working on: %s", administration_get_file_path()); // @localize ImGui::End(); ImGui::PopStyleVar(); diff --git a/src/views/projects.cpp b/src/views/projects.cpp new file mode 100644 index 0000000..14f99c9 --- /dev/null +++ b/src/views/projects.cpp @@ -0,0 +1,173 @@ +#include <stdio.h> + +#include "views.hpp" +#include "imgui.h" +#include "../administration.hpp" +#include "../locales/locales.hpp" + +static view_state current_view_state = LIST; +static project selected_for_cancellation; + +static project active_project; + +static void draw_project_form() +{ + static const char* selected_country = NULL; + + if (ImGui::Button(localize("form.back"))) { + current_view_state = view_state::LIST; + memset(&active_project, 0, sizeof(project)); + selected_country = 0; + return; + } + ImGui::Spacing(); + + bool viewing_only = (current_view_state == view_state::VIEW); + + ImGui::BeginDisabled(); + + float widthAvailable = ImGui::GetContentRegionAvail().x; + + ImGui::SetNextItemWidth(widthAvailable*0.2f); + ImGui::InputText(localize("contact.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();view_draw_required_tag(); + + if (viewing_only) ImGui::EndDisabled(); + + if (!viewing_only) { + bool can_save = strlen(active_project.description) > 0; + + if (!can_save) ImGui::BeginDisabled(); + // Save button + ImGui::Spacing(); + if (ImGui::Button(localize("form.save"))) { + if (current_view_state == view_state::CREATE) + administration_create_project(active_project); + + else if (current_view_state == view_state::EDIT) + administration_update_project(active_project); + + memset(&active_project, 0, sizeof(project)); + current_view_state = view_state::LIST; + selected_country = 0; + } + if (!can_save) ImGui::EndDisabled(); + } + else { + // TODO list invoices connected to project. + } +} + +static void draw_project_list() +{ + const u32 items_per_page = 50; + static s32 current_page = 0; + s32 max_page = (administration_get_project_count() + items_per_page - 1) / items_per_page; + if (max_page == 0) max_page = 1; + + if (ImGui::Button(localize("form.create"))) + { + current_view_state = view_state::CREATE; + memset(&active_project, 0, sizeof(project)); + snprintf(active_project.id, IM_ARRAYSIZE(active_project.id), "P/%d", administration_create_id()); + } + + if (current_page >= max_page-1) current_page = max_page-1; + if (current_page < 0) current_page = 0; + + ImGui::SameLine(); + bool enable_prev = current_page > 0; + if (!enable_prev) ImGui::BeginDisabled(); + if (ImGui::Button("<< Prev") && current_page > 0) current_page--; + if (!enable_prev) ImGui::EndDisabled(); + + ImGui::SameLine(); + ImGui::Text("(%d/%d)", current_page+1, max_page); + + ImGui::SameLine(); + bool enable_next = current_page < max_page-1; + if (!enable_next) ImGui::BeginDisabled(); + if (ImGui::Button("Next >>") && current_page < max_page-1) current_page++; + if (!enable_next) ImGui::EndDisabled(); + + ImGui::Spacing(); + + if (ImGui::BeginTable("TableProjects", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + + ImGui::TableSetupColumn(localize("project.table.identifier"), ImGuiTableColumnFlags_WidthFixed, 80); + ImGui::TableSetupColumn(localize("project.table.status"), ImGuiTableColumnFlags_WidthFixed, 140); + ImGui::TableSetupColumn(localize("project.table.description"), ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 160); + ImGui::TableHeadersRow(); + + project project_list[items_per_page]; + u32 project_count = administration_get_projects(current_page, items_per_page, project_list); + + for (u32 i = 0; i < project_count; i++) { + project c = project_list[i]; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); ImGui::Text(c.id); + ImGui::TableSetColumnIndex(1); ImGui::Text(localize(administration_project_get_status_string(c))); + ImGui::TableSetColumnIndex(2); ImGui::Text(c.description); + + ImGui::TableSetColumnIndex(3); + + char btn_name[20]; + sprintf(btn_name, "%s##%d", localize("form.view"), i); + if (ImGui::Button(btn_name)) { + active_project = c; + current_view_state = view_state::VIEW; + } + + if (c.state == project_state::RUNNING) + { + ImGui::SameLine(); + sprintf(btn_name, "%s##%d", localize("form.change"), i); + if (ImGui::Button(btn_name)) { + active_project = c; + current_view_state = view_state::EDIT; + } + + ImGui::SameLine(); + sprintf(btn_name, "%s##%d", localize("form.cancel"), i); + if (ImGui::Button(btn_name)) { + selected_for_cancellation = c; + ImGui::OpenPopup("ConfirmCancelProject"); + } + } + } + + if (ImGui::BeginPopupModal("ConfirmCancelProject", nullptr, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoTitleBar)) { + ImGui::Text(localize("form.confirmCancelProject")); + ImGui::Separator(); + + if (ImGui::Button(localize("form.yes"), ImVec2(120, 0))) { + administration_cancel_project(selected_for_cancellation); + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button(localize("form.no"), ImVec2(120, 0))) { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + ImGui::EndTable(); + } +} + +void views_draw_projects() +{ + switch(current_view_state) + { + case view_state::LIST: draw_project_list(); break; + case view_state::CREATE: draw_project_form(); break; + case view_state::EDIT: draw_project_form(); break; + case view_state::VIEW: draw_project_form(); break; + } +}
\ No newline at end of file diff --git a/src/views/views.cpp b/src/views/views.cpp new file mode 100644 index 0000000..47eff34 --- /dev/null +++ b/src/views/views.cpp @@ -0,0 +1,28 @@ +#include "views.hpp" +#include "imgui.h" +#include "../locales/locales.hpp" + +void view_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(); +}
\ No newline at end of file diff --git a/src/views/views.hpp b/src/views/views.hpp index 2ebdedd..f47bf3a 100644 --- a/src/views/views.hpp +++ b/src/views/views.hpp @@ -1,6 +1,15 @@ #pragma once -#include "../locales/locales.hpp" +typedef enum +{ + LIST, + EDIT, + CREATE, + VIEW, +} view_state; + +void view_draw_required_tag(); void views_draw_dashboard(); -void views_draw_contacts();
\ No newline at end of file +void views_draw_contacts(); +void views_draw_projects();
\ No newline at end of file |
