diff --git a/zeiterfassungclient/translations/zeiterfassungclient_de.ts b/zeiterfassungclient/translations/zeiterfassungclient_de.ts index 955ad5d..030a2e6 100644 --- a/zeiterfassungclient/translations/zeiterfassungclient_de.ts +++ b/zeiterfassungclient/translations/zeiterfassungclient_de.ts @@ -20,123 +20,123 @@ main - + Loading translations... - - + + Invalid language selection! - + You did not select a valid language! - + Loading theme... - - - - + + + + Could not load theme! - + Theme file does not exist! - + Loading strip layouts... - - - - - - - - + + + + + + + + Could not load strips! - + Loading login page... - - + + Could not access Zeiterfassung! - + Base url - + Please enter the base url to the Zeiterfassung: - + Invalid url! - + This url is not valid! - + Authenticating... - - + + Could not authenticate with Zeiterfassung! - + Getting user information... - - + + Could not get user information! - - + + Could not load plugin %0! - - + + Plugin not valid %0! - + Loading settings... diff --git a/zeiterfassungclient/translations/zeiterfassungclient_en.ts b/zeiterfassungclient/translations/zeiterfassungclient_en.ts index b6caf6f..3ec9457 100644 --- a/zeiterfassungclient/translations/zeiterfassungclient_en.ts +++ b/zeiterfassungclient/translations/zeiterfassungclient_en.ts @@ -20,123 +20,123 @@ main - + Loading translations... - - + + Invalid language selection! - + You did not select a valid language! - + Loading theme... - - - - + + + + Could not load theme! - + Theme file does not exist! - + Loading strip layouts... - - - - - - - - + + + + + + + + Could not load strips! - + Loading login page... - - + + Could not access Zeiterfassung! - + Base url - + Please enter the base url to the Zeiterfassung: - + Invalid url! - + This url is not valid! - + Authenticating... - - + + Could not authenticate with Zeiterfassung! - + Getting user information... - - + + Could not get user information! - - + + Could not load plugin %0! - - + + Plugin not valid %0! - + Loading settings... diff --git a/zeiterfassungguilib/CMakeLists.txt b/zeiterfassungguilib/CMakeLists.txt index 384946b..986e128 100644 --- a/zeiterfassungguilib/CMakeLists.txt +++ b/zeiterfassungguilib/CMakeLists.txt @@ -6,6 +6,7 @@ find_package(Qt5LinguistTools CONFIG REQUIRED) set(HEADERS mainwindow.h + projectsmodel.h settingswidget.h stripfactory.h stripswidget.h @@ -19,6 +20,7 @@ set(HEADERS set(SOURCES mainwindow.cpp + projectsmodel.cpp settingswidget.cpp stripfactory.cpp stripswidget.cpp diff --git a/zeiterfassungguilib/mainwindow.cpp b/zeiterfassungguilib/mainwindow.cpp index 3488101..6f37d99 100644 --- a/zeiterfassungguilib/mainwindow.cpp +++ b/zeiterfassungguilib/mainwindow.cpp @@ -37,7 +37,8 @@ MainWindow::MainWindow(ZeiterfassungSettings &settings, ZeiterfassungApi &erfass m_stripFactory(stripFactory), m_plugins(plugins), m_currentStripWidget(Q_NULLPTR), - m_timerId(-1) + m_timerId(-1), + m_projectsModel(m_userInfo.userId, QDate::currentDate(), m_erfassung) { ui->setupUi(this); @@ -96,6 +97,8 @@ MainWindow::MainWindow(ZeiterfassungSettings &settings, ZeiterfassungApi &erfass } dateChangedSlot(ui->dateEditDate->date()); + + ui->comboBoxDebug->setModel(&m_projectsModel); } MainWindow::~MainWindow() diff --git a/zeiterfassungguilib/mainwindow.h b/zeiterfassungguilib/mainwindow.h index da195cc..0f2b209 100644 --- a/zeiterfassungguilib/mainwindow.h +++ b/zeiterfassungguilib/mainwindow.h @@ -10,6 +10,7 @@ #include "replies/getuserinforeply.h" #include "replies/getcomboboxreply.h" #include "replies/getpresencestatusreply.h" +#include "projectsmodel.h" class QMenu; class QToolBar; @@ -85,4 +86,6 @@ private: StripsWidget *m_currentStripWidget; int m_timerId; + + ProjectsModel m_projectsModel; }; diff --git a/zeiterfassungguilib/mainwindow.ui b/zeiterfassungguilib/mainwindow.ui index 91a2c16..aebe993 100644 --- a/zeiterfassungguilib/mainwindow.ui +++ b/zeiterfassungguilib/mainwindow.ui @@ -176,6 +176,22 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + diff --git a/zeiterfassungguilib/projectsmodel.cpp b/zeiterfassungguilib/projectsmodel.cpp new file mode 100644 index 0000000..7e4db59 --- /dev/null +++ b/zeiterfassungguilib/projectsmodel.cpp @@ -0,0 +1,217 @@ +#include "projectsmodel.h" + +#include + +#include "zeiterfassungapi.h" +#include "replies/getcomboboxreply.h" + +ProjectsModel::ProjectsModel(int userId, const QDate &date, ZeiterfassungApi &api, QObject *parent) : + QAbstractListModel(parent), + m_userId(userId), + m_date(date), + m_api(api) +{ + m_getProjectsReply = m_api.doGetProjects(m_userId, m_date); + connect(m_getProjectsReply.get(), &ZeiterfassungReply::finished, this, &ProjectsModel::getProjectsFinished); +} + +int ProjectsModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + if (m_getProjectsReply != nullptr) + return 1; + + int rows { 0 }; + for (const auto &project : m_projects) + { + rows++; // header + if (project.getWorkpackagesReply != nullptr) + rows++; // loading or failed + else + rows += project.workpackages.size(); + } + return rows; +} + +QVariant ProjectsModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.column() != 0 || index.row() < 0) + return {}; + + if (m_getProjectsReply != nullptr) + { + if (index.row() != 0) + return {}; + + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + if (m_getProjectsReply->isFinished()) + { + Q_ASSERT(!m_getProjectsReply->success()); + return m_getProjectsReply->message(); + } + else + return tr("Loading projects..."); + default: + return {}; + } + } + + int row { 0 }; + for (const auto &project : m_projects) + { + if (row++ == index.row()) + { + switch (role) + { + case Qt::DisplayRole: + return project.value + " (" + project.label + ')'; + case Qt::EditRole: + return project.value; + case Qt::FontRole: + { + QFont font; + font.setBold(true); + return font; + } + default: + return {}; + } + } + + if (project.getWorkpackagesReply != nullptr) + { + if (row++ == index.row()) + { + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + if (project.getWorkpackagesReply->isFinished()) + { + Q_ASSERT(!project.getWorkpackagesReply->success()); + return project.getWorkpackagesReply->message(); + } + else + return tr("Loading workpackages..."); + default: + return {}; + } + } + } + else + { + for (const auto &workpackage : project.workpackages) + { + if (row++ == index.row()) + { + switch (role) + { + case Qt::DisplayRole: + return std::get<0>(workpackage) + " (" + std::get<1>(workpackage) + ')'; + case Qt::EditRole: + return std::get<0>(workpackage); + default: + return {}; + } + } + } + } + } + + return {}; +} + +Qt::ItemFlags ProjectsModel::flags(const QModelIndex &index) const +{ + auto flags = QAbstractListModel::flags(index); + if (!isSelectable(index)) + flags &=~Qt::ItemIsSelectable; + return flags; +} + +void ProjectsModel::getProjectsFinished() +{ + if (!m_getProjectsReply->success()) + { + emit dataChanged(createIndex(0, 0), createIndex(0, 0), { Qt::DisplayRole, Qt::EditRole }); + return; + } + + emit beginResetModel(); + for (const auto &item : m_getProjectsReply->items()) + { + const auto index = m_projects.size(); + auto &project = m_projects.emplace_back(item.label, item.value, m_api.doGetWorkpackages(m_userId, m_date, item.value)); + connect(project.getWorkpackagesReply.get(), &ZeiterfassungReply::finished, this, [this,index](){ getWorkspacesFinished(index); }); + } + m_getProjectsReply = nullptr; + emit endResetModel(); +} + +void ProjectsModel::getWorkspacesFinished(int index) +{ + getWorkspacesFinished(m_projects[index]); +} + +void ProjectsModel::getWorkspacesFinished(ProjectsModel::Project &project) +{ + if (!project.getWorkpackagesReply->success()) + { + //TODO: improve performance + emit beginResetModel(); + emit endResetModel(); + return; + } + + for (const auto &item : project.getWorkpackagesReply->items()) + project.workpackages.push_back(std::make_pair(item.value, item.label)); + + project.getWorkpackagesReply = nullptr; + + //TODO: improve performance + emit beginResetModel(); + emit endResetModel(); +} + +bool ProjectsModel::isSelectable(const QModelIndex &index) const +{ + if (!index.isValid() || index.column() != 0 || index.row() < 0) + return {}; + + if (m_getProjectsReply != nullptr) + { + if (index.row() != 0) + return {}; + + return false; + } + + int row { 0 }; + for (const auto &project : m_projects) + { + if (row++ == index.row()) + return false; + + if (project.getWorkpackagesReply != nullptr) + { + if (row++ == index.row()) + return false; + } + else + { + for (const auto &workpackage : project.workpackages) + { + Q_UNUSED(workpackage) + if (row++ == index.row()) + return true; + } + } + } + + return {}; +} diff --git a/zeiterfassungguilib/projectsmodel.h b/zeiterfassungguilib/projectsmodel.h new file mode 100644 index 0000000..88fa1d9 --- /dev/null +++ b/zeiterfassungguilib/projectsmodel.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include + +#include +#include + +class ZeiterfassungApi; +class GetComboboxReply; + +class ProjectsModel : public QAbstractListModel +{ + Q_OBJECT + + struct Project { + Project(const QString &label, const QString &value, std::unique_ptr getWorkpackagesReply) : + label(label), value(value), getWorkpackagesReply(std::move(getWorkpackagesReply)) + {} + + QString label; + QString value; + + std::unique_ptr getWorkpackagesReply; + + std::vector > workpackages; + }; + +public: + explicit ProjectsModel(int userId, const QDate &date, ZeiterfassungApi &api, QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + +private slots: + void getProjectsFinished(); + void getWorkspacesFinished(int index); + void getWorkspacesFinished(Project &project); + +private: + bool isSelectable(const QModelIndex &index) const; + + const int m_userId; + const QDate m_date; + ZeiterfassungApi &m_api; + + std::unique_ptr m_getProjectsReply; + std::vector m_projects; +}; diff --git a/zeiterfassungguilib/translations/zeiterfassungguilib_de.ts b/zeiterfassungguilib/translations/zeiterfassungguilib_de.ts index 86d284b..fdaff3c 100644 --- a/zeiterfassungguilib/translations/zeiterfassungguilib_de.ts +++ b/zeiterfassungguilib/translations/zeiterfassungguilib_de.ts @@ -78,8 +78,8 @@ - - + + Start Kommen @@ -89,116 +89,129 @@ Gehen - + &File &Datei - + &About &Über - + &View &Ansicht - + &Tools &Werkzeuge - + &Quit &Beenden - + About &Me Über &mich - + About &zeiterfassung Über &zeiterfassung - + About &Qt Über &Qt - + &Today &Heute - + &Refresh everything Alles &neu laden - + &Settings &Einstellungen - + Help Hilfe - + Zeiterfassung - %0 (%1) Zeiterfassung - %0 (%1) - + Workpackage Arbeitspaket - + Text Text - - + + Could not load bookings! Konnte Buchungen nicht laden! - - + + Could not create booking! Konnte Buchung nicht erstellen! - - + + Could not edit time assignment! Konnte Kontierung nicht bearbeiten! - - + + %0 (%1) %0 (%1) - + Could not create time assignment! Konnte Kontierung nicht erstellen! - - + + Switch Wechseln + + ProjectsModel + + + Loading projects... + + + + + Loading workpackages... + + + SettingsDialog diff --git a/zeiterfassungguilib/translations/zeiterfassungguilib_en.ts b/zeiterfassungguilib/translations/zeiterfassungguilib_en.ts index 5692874..e6f4778 100644 --- a/zeiterfassungguilib/translations/zeiterfassungguilib_en.ts +++ b/zeiterfassungguilib/translations/zeiterfassungguilib_en.ts @@ -78,8 +78,8 @@ - - + + Start @@ -89,116 +89,129 @@ - + &File - + &About - + &View - + &Tools - + &Quit - + About &Me - + About &zeiterfassung - + About &Qt - + &Today - + &Refresh everything - + &Settings - + Help - + Zeiterfassung - %0 (%1) - + Workpackage - + Text - - + + Could not load bookings! - - + + Could not create booking! - + Could not create time assignment! - - + + Could not edit time assignment! - - + + Switch - - + + %0 (%1) + + ProjectsModel + + + Loading projects... + + + + + Loading workpackages... + + + SettingsDialog diff --git a/zeiterfassungnetworklib/replies/zeiterfassungreply.cpp b/zeiterfassungnetworklib/replies/zeiterfassungreply.cpp index 2270dc1..438792c 100644 --- a/zeiterfassungnetworklib/replies/zeiterfassungreply.cpp +++ b/zeiterfassungnetworklib/replies/zeiterfassungreply.cpp @@ -8,9 +8,15 @@ ZeiterfassungReply::ZeiterfassungReply(ZeiterfassungApi *zeiterfassung) : QObject(zeiterfassung), m_zeiterfassung(zeiterfassung), + m_finished(false), m_success(false) { + connect(this, &ZeiterfassungReply::finished, this, [this](){ m_finished = true; }); +} +bool ZeiterfassungReply::isFinished() const +{ + return m_finished; } bool ZeiterfassungReply::success() const diff --git a/zeiterfassungnetworklib/replies/zeiterfassungreply.h b/zeiterfassungnetworklib/replies/zeiterfassungreply.h index 8b9b5b3..8050c17 100644 --- a/zeiterfassungnetworklib/replies/zeiterfassungreply.h +++ b/zeiterfassungnetworklib/replies/zeiterfassungreply.h @@ -15,6 +15,8 @@ class ZEITERFASSUNGNETWORKLIB_EXPORT ZeiterfassungReply : public QObject public: explicit ZeiterfassungReply(ZeiterfassungApi *zeiterfassung); + bool isFinished() const; + bool success() const; const QString &message() const; @@ -33,6 +35,7 @@ protected: private: ZeiterfassungApi *m_zeiterfassung; + bool m_finished; bool m_success; QString m_message; };