summaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
authorrtk0c <[email protected]>2021-04-09 23:17:45 -0700
committerrtk0c <[email protected]>2021-04-09 23:17:45 -0700
commit4303d0be47526b35e5bb3e3be001da227dae5d96 (patch)
tree6e79502e9966f835756d30929ea3f7127ba3080b /core/src
parentce8660cc5bfc12e6e3f75d4cce22492783ca9066 (diff)
Fix crash on load entries
- TODO format time properly - TODO add Purchases table visualization
Diffstat (limited to 'core/src')
-rw-r--r--core/src/Model/Project.cpp4
-rw-r--r--core/src/Model/Project.hpp13
-rw-r--r--core/src/Model/TransactionDatabase.cpp160
-rw-r--r--core/src/Model/TransactionsModel.cpp139
-rw-r--r--core/src/Model/TransactionsModel.hpp (renamed from core/src/Model/TransactionDatabase.hpp)23
-rw-r--r--core/src/Model/fwd.hpp2
-rw-r--r--core/src/UI/UI_DatabaseView.cpp222
7 files changed, 305 insertions, 258 deletions
diff --git a/core/src/Model/Project.cpp b/core/src/Model/Project.cpp
index 2a79e3f..e762efb 100644
--- a/core/src/Model/Project.cpp
+++ b/core/src/Model/Project.cpp
@@ -82,11 +82,11 @@ void Project::SetName(std::string name) {
mName = std::move(name);
}
-const TransactionDatabase& Project::GetDatabase() const {
+const TransactionModel& Project::GetTransactionsModel() const {
return mDb;
}
-TransactionDatabase& Project::GetDatabase() {
+TransactionModel& Project::GetTransactionsModel() {
return mDb;
}
diff --git a/core/src/Model/Project.hpp b/core/src/Model/Project.hpp
index dca10d0..748ca82 100644
--- a/core/src/Model/Project.hpp
+++ b/core/src/Model/Project.hpp
@@ -1,7 +1,7 @@
#pragma once
#include "Model/Items.hpp"
-#include "Model/TransactionDatabase.hpp"
+#include "Model/TransactionsModel.hpp"
#include <json/forwards.h>
#include <filesystem>
@@ -17,7 +17,7 @@ private:
std::filesystem::path mRootPath;
std::string mRootPathString;
std::string mName;
- TransactionDatabase mDb;
+ TransactionModel mDb;
public:
/// Load the project from a directory containing the cplt_project.json file.
@@ -27,11 +27,6 @@ public:
/// This function assumes the given directory will exist and is empty.
Project(std::filesystem::path rootPath, std::string name);
- Project(const Project&) = delete;
- Project& operator=(const Project&) = delete;
- Project(Project&&) = default;
- Project& operator=(Project&&) = default;
-
/// Path to a *directory* that contains the project file.
const std::filesystem::path& GetPath() const;
const std::string& GetPathString() const;
@@ -39,8 +34,8 @@ public:
const std::string& GetName() const;
void SetName(std::string name);
- const TransactionDatabase& GetDatabase() const;
- TransactionDatabase& GetDatabase();
+ const TransactionModel& GetTransactionsModel() const;
+ TransactionModel& GetTransactionsModel();
Json::Value Serialize();
void WriteToDisk();
diff --git a/core/src/Model/TransactionDatabase.cpp b/core/src/Model/TransactionDatabase.cpp
deleted file mode 100644
index 766727d..0000000
--- a/core/src/Model/TransactionDatabase.cpp
+++ /dev/null
@@ -1,160 +0,0 @@
-#include "TransactionDatabase.hpp"
-
-#include "Model/Project.hpp"
-
-#include <filesystem>
-#include <stdexcept>
-
-namespace fs = std::filesystem;
-
-SalesTable::SalesTable(TransactionDatabase& db)
- // language=SQLite
- : GetRowsStatement(db.GetSQLite(), R"""(
-SELECT * FROM Sales WHERE rowid >= ? AND rowid < ?
-)""")
- // language=SQLite
- // TODO
- , FilterRowsStatement(db.GetSQLite(), R"""(
-)""") {
-}
-
-int SalesTable::GetEntryCont() const {
- // TODO
- return 0;
-}
-
-DeliveryTable::DeliveryTable(TransactionDatabase& db) {
-}
-
-PurchasesTable::PurchasesTable(TransactionDatabase& db) {
-}
-
-static std::string GetDatabaseFilePath(const Project& project) {
- auto dbsDir = project.GetPath() / "databases";
- fs::create_directories(dbsDir);
-
- auto dbFile = dbsDir / "transactions.sqlite3";
- return dbFile.string();
-}
-
-TransactionDatabase::TransactionDatabase(Project& project)
- : mProject{ &project }
- , mDb(GetDatabaseFilePath(project), SQLite::OPEN_READWRITE)
- , mSales(*this)
- , mPurchases(*this)
- , mDeliveries(*this) {
- // Schema
- // - Customer: the customer item ID
- // - Deadline: unix epoch time of order deadline
- // - DeliveryTime: the time this order was completed (through a set of deliveries)
- if (!mDb.tableExists("Sales")) {
- // language=SQLite
- mDb.exec(R"""(
-CREATE TABLE Sales(
- INT PRIMARY KEY,
- Customer INT,
- Deadline DATETIME,
- DeliveryTime DATETIME
-);
-)""");
- }
-
- if (!mDb.tableExists("SalesItems")) {
- // language=SQLite
- mDb.exec(R"""(
-CREATE TABLE SalesItems(
- SaleId INT,
- ItemId INT,
- Count INT
-);
-)""");
- }
-
- // Schema
- // - Factory: the factory id,
- // - OrderTime: the time this order was made
- // - DeliveryTime: the time this order was completed (through a set of deliveries)
- if (!mDb.tableExists("Purchases")) {
- // language=SQLite
- mDb.exec(R"""(
-CREATE TABLE Purchases(
- INT PRIMARY KEY,
- Factory INT,
- OrderTime DATETIME,
- DeliveryTime DATETIME
-);
-)""");
- }
-
- if (!mDb.tableExists("PurchasesItems")) {
- // language=SQLite
- mDb.exec(R"""(
-CREATE TABLE PurchasesItems(
- PurchaseId INT,
- ItemId INT,
- Count INT
-);
-)""");
- }
-
- // Schema
- // - SendTime: unix epoch time of sending to delivery
- // - ArriveTime: unix epoch time of delivery arrived at warehouse; 0 if not arrived yet
- // - AssociatedOrder: rowid of the order that this delivery is completing (which table: Outgoing=true -> Sales, Outgoing=false -> Purchases)
- // - Outgoing: true if the delivery is from warehouse to customer; false if the delivery is from factory to warehouse
- if (!mDb.tableExists("Deliveries")) {
- // language=SQLite
- mDb.exec(R"""(
-CREATE TABLE Deliveries(
- INT PRIMARY KEY,
- SendTime DATETIME,
- ArriveTime DATETIME,
- AssociatedOrder INT,
- Outgoing BOOLEAN
-);
-)""");
- }
-
- if (!mDb.tableExists("DeliveriesItems")) {
- // language=SQLite
- mDb.exec(R"""(
-CREATE TABLE DeliveriesItems(
- DeliveryId INT,
- ItemId INT,
- Count INT
-);
-)""");
- }
-}
-
-const SQLite::Database& TransactionDatabase::GetSQLite() const {
- return mDb;
-}
-
-SQLite::Database& TransactionDatabase::GetSQLite() {
- return mDb;
-}
-
-const SalesTable& TransactionDatabase::GetSales() const {
- return mSales;
-}
-
-SalesTable& TransactionDatabase::GetSales() {
- return mSales;
-}
-
-const PurchasesTable& TransactionDatabase::GetPurchases() const {
- return mPurchases;
-}
-
-PurchasesTable& TransactionDatabase::GetPurchases() {
- return mPurchases;
-}
-
-const DeliveryTable& TransactionDatabase::GetDeliveries() const {
- return mDeliveries;
-}
-
-DeliveryTable& TransactionDatabase::GetDeliveries() {
- return mDeliveries;
-}
diff --git a/core/src/Model/TransactionsModel.cpp b/core/src/Model/TransactionsModel.cpp
new file mode 100644
index 0000000..6035528
--- /dev/null
+++ b/core/src/Model/TransactionsModel.cpp
@@ -0,0 +1,139 @@
+#include "TransactionsModel.hpp"
+
+#include "Model/Project.hpp"
+
+#include <filesystem>
+#include <stdexcept>
+
+namespace fs = std::filesystem;
+
+SalesTable::SalesTable(TransactionModel& db)
+ // language=SQLite
+ : GetRowsStatement(db.GetSQLite(), R"""(
+SELECT * FROM Sales WHERE rowid >= ? AND rowid < ?
+)""")
+ // language=SQLite
+ // TODO
+ , FilterRowsStatement(db.GetSQLite(), R"""(
+)""") {
+}
+
+DeliveryTable::DeliveryTable(TransactionModel& db) {
+}
+
+PurchasesTable::PurchasesTable(TransactionModel& db) {
+}
+
+static std::string GetDatabaseFilePath(const Project& project) {
+ auto dbsDir = project.GetPath() / "databases";
+ fs::create_directories(dbsDir);
+
+ auto dbFile = dbsDir / "transactions.sqlite3";
+ return dbFile.string();
+}
+
+/// Wrapper for SQLite::Database that creates the default tables
+TransactionModel::DatabaseWrapper::DatabaseWrapper(TransactionModel& self)
+ : mSqlite(GetDatabaseFilePath(*self.mProject), SQLite::OPEN_READWRITE) {
+
+ // If this table doesn't exist, the database probably just got initialized
+ if (mSqlite.tableExists("Sales")) {
+ return;
+ }
+
+ // 'Sales' schema
+ // - Customer: the customer item ID
+ // - Deadline: unix epoch time of order deadline
+ // - DeliveryTime: the time this order was completed (through a set of deliveries)
+
+ // 'Purchases' schema
+ // - Factory: the factory id,
+ // - OrderTime: the time this order was made
+ // - DeliveryTime: the time this order was completed (through a set of deliveries)
+
+ // 'Deliveries' schema
+ // - SendTime: unix epoch time of sending to delivery
+ // - ArriveTime: unix epoch time of delivery arrived at warehouse; 0 if not arrived yet
+ // - AssociatedOrder: rowid of the order that this delivery is completing (which table: Outgoing=true -> Sales, Outgoing=false -> Purchases)
+ // - Outgoing: true if the delivery is from warehouse to customer; false if the delivery is from factory to warehouse
+
+ // language=SQLite
+ mSqlite.exec(R"""(
+CREATE TABLE IF NOT EXISTS Sales(
+ INT PRIMARY KEY,
+ Customer INT,
+ Deadline DATETIME,
+ DeliveryTime DATETIME
+);
+CREATE TABLE IF NOT EXISTS SalesItems(
+ SaleId INT,
+ ItemId INT,
+ Count INT
+);
+
+CREATE TABLE IF NOT EXISTS Purchases(
+ INT PRIMARY KEY,
+ Factory INT,
+ OrderTime DATETIME,
+ DeliveryTime DATETIME
+);
+CREATE TABLE IF NOT EXISTS PurchasesItems(
+ PurchaseId INT,
+ ItemId INT,
+ Count INT
+);
+
+CREATE TABLE IF NOT EXISTS Deliveries(
+ INT PRIMARY KEY,
+ SendTime DATETIME,
+ ArriveTime DATETIME,
+ AssociatedOrder INT,
+ Outgoing BOOLEAN
+);
+CREATE TABLE IF NOT EXISTS DeliveriesItems(
+ DeliveryId INT,
+ ItemId INT,
+ Count INT
+);
+)""");
+}
+
+TransactionModel::TransactionModel(Project& project)
+ : mProject{ &project }
+ , mDbWrapper(*this)
+ , mSales(*this)
+ , mPurchases(*this)
+ , mDeliveries(*this) {
+}
+
+const SQLite::Database& TransactionModel::GetSQLite() const {
+ return mDbWrapper.mSqlite;
+}
+
+SQLite::Database& TransactionModel::GetSQLite() {
+ return mDbWrapper.mSqlite;
+}
+
+const SalesTable& TransactionModel::GetSales() const {
+ return mSales;
+}
+
+SalesTable& TransactionModel::GetSales() {
+ return mSales;
+}
+
+const PurchasesTable& TransactionModel::GetPurchases() const {
+ return mPurchases;
+}
+
+PurchasesTable& TransactionModel::GetPurchases() {
+ return mPurchases;
+}
+
+const DeliveryTable& TransactionModel::GetDeliveries() const {
+ return mDeliveries;
+}
+
+DeliveryTable& TransactionModel::GetDeliveries() {
+ return mDeliveries;
+}
diff --git a/core/src/Model/TransactionDatabase.hpp b/core/src/Model/TransactionsModel.hpp
index 9c869c4..beed663 100644
--- a/core/src/Model/TransactionDatabase.hpp
+++ b/core/src/Model/TransactionsModel.hpp
@@ -12,31 +12,36 @@ public:
SQLite::Statement FilterRowsStatement;
public:
- SalesTable(TransactionDatabase& db);
-
- int GetEntryCont() const;
+ SalesTable(TransactionModel& db);
};
class PurchasesTable {
public:
- PurchasesTable(TransactionDatabase& db);
+ PurchasesTable(TransactionModel& db);
};
class DeliveryTable {
public:
- DeliveryTable(TransactionDatabase& db);
+ DeliveryTable(TransactionModel& db);
};
-class TransactionDatabase {
+// TODO fuck SQLite::Statement has move ctor but not move assignment operator
+class TransactionModel {
private:
+ class DatabaseWrapper {
+ public:
+ SQLite::Database mSqlite;
+ DatabaseWrapper(TransactionModel& self);
+ };
+
Project* mProject;
- SQLite::Database mDb;
+ DatabaseWrapper mDbWrapper;
SalesTable mSales;
PurchasesTable mPurchases;
DeliveryTable mDeliveries;
public:
- TransactionDatabase(Project& project);
+ TransactionModel(Project& project);
const SQLite::Database& GetSQLite() const;
SQLite::Database& GetSQLite();
@@ -46,5 +51,5 @@ public:
const PurchasesTable& GetPurchases() const;
PurchasesTable& GetPurchases();
const DeliveryTable& GetDeliveries() const;
- DeliveryTable& GetDeliveries()
+ DeliveryTable& GetDeliveries();
};
diff --git a/core/src/Model/fwd.hpp b/core/src/Model/fwd.hpp
index e153923..4bd508c 100644
--- a/core/src/Model/fwd.hpp
+++ b/core/src/Model/fwd.hpp
@@ -22,4 +22,4 @@ class Project;
class SalesTable;
class PurchasesTable;
class DeliveryTable;
-class TransactionDatabase;
+class TransactionModel;
diff --git a/core/src/UI/UI_DatabaseView.cpp b/core/src/UI/UI_DatabaseView.cpp
index 2b74918..8791c5b 100644
--- a/core/src/UI/UI_DatabaseView.cpp
+++ b/core/src/UI/UI_DatabaseView.cpp
@@ -1,5 +1,6 @@
#include "UI.hpp"
+#include "Model/Filter.hpp"
#include "Model/Project.hpp"
#include "UI/Localization.hpp"
#include "UI/States.hpp"
@@ -10,6 +11,7 @@
#include <SQLiteCpp/Statement.h>
#include <imgui.h>
#include <cstdint>
+#include <ctime>
#include <memory>
#include <vector>
@@ -25,6 +27,13 @@ public:
std::string DeliveryTime;
};
+class PurchaseEntry {
+public:
+ std::string Factory;
+ std::string OrderTime;
+ std::string DeliveryTime;
+};
+
class SalesViewTab {
private:
Project* mProject;
@@ -42,38 +51,63 @@ private:
/// A cached, contiguous (row id of each entry is monotonically increasing, but not necessarily starts at 0) list ready-to-be-presented entries. May be incomplete.
std::vector<SaleEntry> mEntries;
- // TODO this is very impractical (running filter in client code instead of letting SQLite doing it)
- // Maybe simply cache a list of indices produced by a sql query?
-
- /// A bitset of all active (should-be-displayed) entries based on current filter, updated whenever \c mActiveFilter is updated.
- /// Should have the exact same size as \c entries.
- std::vector<bool> mActiveEntries;
-
- /// A cached list of index to entries that should be displayed on the current page.
- std::vector<int> mCurrentPageEntries;
+ /// A vector of row ids of entries (in \c mEntries) that are visible under the current filter. To use these indices, the elements should be mapped to \c mEntries
+ /// index by offsetting by \c mFirstCachedRowId.
+ std::vector<int> mActiveEntries;
- /// The current page the user is on.
- int mCurrentPage;
/// Last possible page for the current set table and filter (inclusive).
int mLastPage;
+ /// The current page the user is on.
+ int mCurrentPage;
- /* UI states */
-
- int mSelectedEntry;
+ int mSelectedEntryRowId;
public:
+ Project* GetProject() const {
+ return mProject;
+ }
+
void OnProjectChanged(Project* newProject) {
mProject = newProject;
+ mFirstCachedRowId = 0;
+ mLastCachedRowId = 0;
+
mEntries.clear();
- mCurrentPage = 0;
- mLastPage = -1;
+ mActiveEntries.clear();
+
+ UpdateLastPage();
+ SetPage(0);
- mSelectedEntry = -1;
+ mSelectedEntryRowId = -1;
+ }
+
+ TableRowsFilter* GetFilter() const {
+ return mActiveFilter.get();
+ }
+
+ void OnFilterChanged() {
+ auto& stmt = mProject->GetTransactionsModel().GetSales().FilterRowsStatement;
+ DEFER {
+ stmt.reset();
+ };
+
+ // TODO lazy loading when too many results
+ mActiveEntries.clear();
+ int columnIdx = stmt.getColumnIndex("rowid");
+ while (stmt.executeStep()) {
+ mActiveEntries.push_back(stmt.getColumn(columnIdx).getInt());
+ }
+
+ UpdateLastPage();
+ SetPage(0);
+
+ mSelectedEntryRowId = -1;
}
void OnFilterChanged(std::unique_ptr<TableRowsFilter> filter) {
- // TODO
+ mActiveFilter = std::move(filter);
+ OnFilterChanged();
}
void Draw() {
@@ -94,7 +128,7 @@ public:
}
ImGui::SameLine();
- if (ImGui::Button(ls->Edit.Get(), mSelectedEntry == -1)) {
+ if (ImGui::Button(ls->Edit.Get(), mSelectedEntryRowId == -1)) {
ImGui::OpenPopup(ls->EditSaleEntryDialogTitle.Get());
}
if (ImGui::BeginPopupModal(ls->EditSaleEntryDialogTitle.Get(), &dummy, ImGuiWindowFlags_AlwaysAutoResize)) {
@@ -109,22 +143,17 @@ public:
ImGui::TableSetupColumn(ls->DatabaseDeliveryTimeColumn.Get());
ImGui::TableHeadersRow();
- for (int i : mCurrentPageEntries) {
- auto& entry = mEntries[i];
-
- ImGui::TableNextColumn();
- if (ImGui::Selectable(entry.Customer.c_str(), mSelectedEntry == i)) {
- mSelectedEntry = i;
+ auto [begin, end] = CalcRangeForPage(mCurrentPage);
+ if (mActiveFilter) {
+ end = std::min(end, (int64_t)mActiveEntries.size() - 1);
+ for (int i = begin; i < end; ++i) {
+ int rowId = mActiveEntries[i];
+ DrawEntry(GetEntry(rowId), rowId);
}
-
- ImGui::NextColumn();
- ImGui::Text("%s", entry.Deadline.c_str());
-
- ImGui::NextColumn();
- if (entry.DeliveryTime.empty()) {
- ImGui::Text("%s", ls->NotDeliveredMessage.Get());
- } else {
- ImGui::Text("%s", entry.DeliveryTime.c_str());
+ } else {
+ end = std::min(end, mLastCachedRowId);
+ for (int rowId = begin; rowId < end; ++rowId) {
+ DrawEntry(GetEntry(rowId), rowId);
}
}
@@ -132,23 +161,43 @@ public:
}
}
+ void DrawEntry(const SaleEntry& entry, int rowId) {
+ auto ls = LocaleStrings::Instance.get();
+
+ ImGui::TableNextColumn();
+ if (ImGui::Selectable(entry.Customer.c_str(), mSelectedEntryRowId == rowId, ImGuiSelectableFlags_SpanAllColumns)) {
+ mSelectedEntryRowId = rowId;
+ }
+
+ ImGui::TableNextColumn();
+ ImGui::Text("%s", entry.Deadline.c_str());
+
+ ImGui::TableNextColumn();
+ if (entry.DeliveryTime.empty()) {
+ ImGui::Text("%s", ls->NotDeliveredMessage.Get());
+ } else {
+ ImGui::Text("%s", entry.DeliveryTime.c_str());
+ }
+ }
+
+ const SaleEntry& GetEntry(int64_t rowId) const {
+ return mEntries[RowIdToIndex(rowId)];
+ }
+
+ SaleEntry& GetEntry(int64_t rowId) {
+ return mEntries[RowIdToIndex(rowId)];
+ }
+
private:
void SetPage(int page) {
- if (mCurrentPage != page) return;
-
mCurrentPage = page;
EnsureCacheCoversPage(page);
+ }
- auto [begin, end] = CalcRangeForPage(page);
- begin -= mFirstCachedRowId;
- end -= mFirstCachedRowId;
-
- mCurrentPageEntries.clear();
- for (auto i = begin; i < end; ++i) {
- if (mActiveEntries[i]) {
- mCurrentPageEntries.push_back(i);
- }
- }
+ void UpdateLastPage() {
+ mLastPage = mActiveEntries.empty()
+ ? 0
+ : CalcPageForRowId(mActiveEntries.back());
}
void EnsureCacheCoversPage(int page) {
@@ -166,11 +215,11 @@ private:
bool doRebuild = false;
if (firstRow < mFirstCachedRowId) {
- newFirst = CalcPageForRowId(firstRow) * kMaxEntriesPerPage;
+ newFirst = (CalcPageForRowId(firstRow) + 1) * kMaxEntriesPerPage;
doRebuild = true;
}
if (lastRow > mLastCachedRowId) {
- newLast = CalcPageForRowId(lastRow) * kMaxEntriesPerPage;
+ newLast = (CalcPageForRowId(lastRow) + 1) * kMaxEntriesPerPage;
doRebuild = true;
}
if (!doRebuild) return;
@@ -178,12 +227,12 @@ private:
auto front = LoadRange(newFirst, mFirstCachedRowId);
auto back = LoadRange(mLastCachedRowId + 1, newLast + 1);
- mEntries.insert(mEntries.begin(), front.begin(), front.end());
- mEntries.insert(mEntries.end(), back.begin(), back.end());
- // TODO update mActiveEntries
+ mFirstCachedRowId -= front.size();
+ mLastCachedRowId += back.size();
- mFirstCachedRowId = newFirst;
- mLastCachedRowId = newLast;
+ // TODO don't reallocate buffer/move elements around all the time. Maybe use a linked list of buckets?
+ mEntries.insert(mEntries.begin(), std::make_move_iterator(front.begin()), std::make_move_iterator(front.end()));
+ mEntries.insert(mEntries.end(), std::make_move_iterator(back.begin()), std::make_move_iterator(back.end()));
}
std::vector<SaleEntry> LoadRange(int64_t begin, int64_t end) {
@@ -196,7 +245,7 @@ private:
result.reserve(size);
- auto& stmt = mProject->GetDatabase().GetSales().GetRowsStatement;
+ auto& stmt = mProject->GetTransactionsModel().GetSales().GetRowsStatement;
DEFER {
stmt.reset();
};
@@ -204,34 +253,54 @@ private:
stmt.bind(1, begin);
stmt.bind(2, end);
- while (true) {
- bool hasResult = stmt.executeStep();
- if (hasResult) {
- result.push_back(stmt.getColumns<SaleEntry, 3>());
- } else {
- return result;
+ auto StringifyEpochTime = [](int64_t epoch) -> std::string {
+ if (epoch == 0) {
+ return "";
}
+
+ auto t = static_cast<time_t>(epoch);
+ std::string str(29, '\0');
+ std::strftime(str.data(), 21, "%Y-%m-%dT%H:%M:%S.", std::localtime(&t));
+ return str;
+ };
+
+ int customerCol = stmt.getColumnIndex("Customer");
+ int deadlineCol = stmt.getColumnIndex("Deadline");
+ int deliveryTimeCol = stmt.getColumnIndex("DeliveryTime");
+
+ while (stmt.executeStep()) {
+ auto customer = stmt.getColumn(customerCol).getInt();
+ auto deadline = stmt.getColumn(deadlineCol).getInt64();
+ auto deliveryTime = stmt.getColumn(deliveryTimeCol).getInt64();
+ result.push_back(SaleEntry{
+ .Customer = mProject->Customers.Find(customer)->GetName(),
+ .Deadline = StringifyEpochTime(deadline),
+ .DeliveryTime = StringifyEpochTime(deliveryTime),
+ });
}
+ return result;
+ }
+
+ int RowIdToIndex(int64_t rowId) const {
+ return rowId - mFirstCachedRowId;
+ }
+
+ int64_t IndexToRowId(int index) const {
+ return index + mFirstCachedRowId;
}
static int CalcPageForRowId(int64_t rowId) {
return rowId / kMaxEntriesPerPage;
}
- /// Calculate range [begin, end) of row ids that the path-th page would show.
+ /// Calculate range [begin, end) of index for the list of entries that are currently visible that the path-th page would show.
+ /// i.e. when there is a filter, look into \c mActiveEntryIndices; when there is no filter, use directly.
static std::pair<int64_t, int64_t> CalcRangeForPage(int page) {
int begin = page * kMaxEntriesPerPage;
return { begin, begin + kMaxEntriesPerPage };
}
};
-class PurchaseEntry {
-public:
- std::string Factory;
- std::string OrderTime;
- std::string DeliveryTime;
-};
-
class PurchasesViewTab {
private:
std::vector<PurchaseEntry> mEntries;
@@ -256,26 +325,25 @@ void UI::DatabaseViewTab() {
auto& uis = UIState::GetInstance();
static Project* currentProject = nullptr;
- static auto salesView = std::make_unique<SalesViewTab>();
- static auto purchasesView = std::make_unique<PurchasesViewTab>();
+ static SalesViewTab salesView;
+ static PurchasesViewTab purchasesView;
if (currentProject != uis.CurrentProject.get()) {
currentProject = uis.CurrentProject.get();
- salesView->OnProjectChanged(currentProject);
- purchasesView->OnProjectChanged(currentProject);
+ salesView.OnProjectChanged(currentProject);
+ purchasesView.OnProjectChanged(currentProject);
}
if (ImGui::BeginTabBar("##DatabaseViewTabs")) {
if (ImGui::BeginTabItem(ls->SalesViewTab.Get())) {
- salesView->Draw();
+ salesView.Draw();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem(ls->PurchasesViewTab.Get())) {
- purchasesView->Draw();
+ purchasesView.Draw();
ImGui::EndTabItem();
}
- // if (ImGui::BeginTabItem(ls->DeliveriesTableTab.Get())) {
- // ImGui::EndTabItem();
- // }
+
+ ImGui::EndTabBar();
}
}