Implement SQLite-backed profile CRUD in Profiles window
This commit is contained in:
@@ -1,18 +1,20 @@
|
|||||||
#include "profiles_window.h"
|
#include "profiles_window.h"
|
||||||
|
|
||||||
|
#include "profile_repository.h"
|
||||||
#include "session_window.h"
|
#include "session_window.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include <QAbstractItemView>
|
#include <QAbstractItemView>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
|
#include <QInputDialog>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QListWidget>
|
#include <QListWidget>
|
||||||
#include <QListWidgetItem>
|
#include <QListWidgetItem>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QStringList>
|
#include <QVariant>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
@@ -22,15 +24,32 @@ ProfilesWindow::ProfilesWindow(QWidget* parent)
|
|||||||
m_profilesList(nullptr),
|
m_profilesList(nullptr),
|
||||||
m_newButton(nullptr),
|
m_newButton(nullptr),
|
||||||
m_editButton(nullptr),
|
m_editButton(nullptr),
|
||||||
m_deleteButton(nullptr)
|
m_deleteButton(nullptr),
|
||||||
|
m_repository(std::make_unique<ProfileRepository>())
|
||||||
{
|
{
|
||||||
setWindowTitle(QStringLiteral("OrbitHub Profiles"));
|
setWindowTitle(QStringLiteral("OrbitHub Profiles"));
|
||||||
resize(520, 620);
|
resize(520, 620);
|
||||||
|
|
||||||
setupUi();
|
setupUi();
|
||||||
populateSampleProfiles();
|
|
||||||
|
if (!m_repository->initError().isEmpty()) {
|
||||||
|
QMessageBox::critical(this,
|
||||||
|
QStringLiteral("Database Error"),
|
||||||
|
QStringLiteral("Failed to initialize SQLite database: %1")
|
||||||
|
.arg(m_repository->initError()));
|
||||||
|
m_newButton->setEnabled(false);
|
||||||
|
m_editButton->setEnabled(false);
|
||||||
|
m_deleteButton->setEnabled(false);
|
||||||
|
m_searchBox->setEnabled(false);
|
||||||
|
m_profilesList->setEnabled(false);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadProfiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfilesWindow::~ProfilesWindow() = default;
|
||||||
|
|
||||||
void ProfilesWindow::setupUi()
|
void ProfilesWindow::setupUi()
|
||||||
{
|
{
|
||||||
auto* central = new QWidget(this);
|
auto* central = new QWidget(this);
|
||||||
@@ -63,49 +82,131 @@ void ProfilesWindow::setupUi()
|
|||||||
connect(m_searchBox,
|
connect(m_searchBox,
|
||||||
&QLineEdit::textChanged,
|
&QLineEdit::textChanged,
|
||||||
this,
|
this,
|
||||||
[this](const QString& text) { filterProfiles(text); });
|
[this](const QString& text) { loadProfiles(text); });
|
||||||
|
|
||||||
connect(m_profilesList,
|
connect(m_profilesList,
|
||||||
&QListWidget::itemDoubleClicked,
|
&QListWidget::itemDoubleClicked,
|
||||||
this,
|
this,
|
||||||
[this](QListWidgetItem* item) { openSessionForItem(item); });
|
[this](QListWidgetItem* item) { openSessionForItem(item); });
|
||||||
|
|
||||||
// Milestone 0 keeps profile management as placeholders.
|
connect(m_newButton, &QPushButton::clicked, this, [this]() { createProfile(); });
|
||||||
auto showTodo = [this](const QString& action) {
|
connect(m_editButton, &QPushButton::clicked, this, [this]() { editSelectedProfile(); });
|
||||||
|
connect(m_deleteButton, &QPushButton::clicked, this, [this]() { deleteSelectedProfile(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfilesWindow::loadProfiles(const QString& query)
|
||||||
|
{
|
||||||
|
m_profilesList->clear();
|
||||||
|
|
||||||
|
const std::vector<Profile> profiles = m_repository->listProfiles(query);
|
||||||
|
for (const Profile& profile : profiles) {
|
||||||
|
auto* item = new QListWidgetItem(profile.name, m_profilesList);
|
||||||
|
item->setData(Qt::UserRole, QVariant::fromValue(profile.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<qint64> ProfilesWindow::selectedProfileId() const
|
||||||
|
{
|
||||||
|
QListWidgetItem* item = m_profilesList->currentItem();
|
||||||
|
if (item == nullptr) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QVariant value = item->data(Qt::UserRole);
|
||||||
|
if (!value.isValid()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.toLongLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfilesWindow::createProfile()
|
||||||
|
{
|
||||||
|
bool accepted = false;
|
||||||
|
const QString name = QInputDialog::getText(this,
|
||||||
|
QStringLiteral("New Profile"),
|
||||||
|
QStringLiteral("Profile name:"),
|
||||||
|
QLineEdit::Normal,
|
||||||
|
QString(),
|
||||||
|
&accepted);
|
||||||
|
|
||||||
|
if (!accepted || name.trimmed().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_repository->createProfile(name).has_value()) {
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
QStringLiteral("Create Profile"),
|
||||||
|
QStringLiteral("Failed to create profile. Names must be unique."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadProfiles(m_searchBox->text());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfilesWindow::editSelectedProfile()
|
||||||
|
{
|
||||||
|
const std::optional<qint64> profileId = selectedProfileId();
|
||||||
|
QListWidgetItem* currentItem = m_profilesList->currentItem();
|
||||||
|
if (!profileId.has_value() || currentItem == nullptr) {
|
||||||
QMessageBox::information(this,
|
QMessageBox::information(this,
|
||||||
QStringLiteral("Milestone 0"),
|
QStringLiteral("Edit Profile"),
|
||||||
QStringLiteral("%1 is planned for Milestone 1.").arg(action));
|
QStringLiteral("Select a profile first."));
|
||||||
};
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
connect(m_newButton, &QPushButton::clicked, this, [showTodo]() { showTodo(QStringLiteral("New Profile")); });
|
bool accepted = false;
|
||||||
connect(m_editButton, &QPushButton::clicked, this, [showTodo]() { showTodo(QStringLiteral("Edit Profile")); });
|
const QString name = QInputDialog::getText(this,
|
||||||
connect(m_deleteButton,
|
QStringLiteral("Edit Profile"),
|
||||||
&QPushButton::clicked,
|
QStringLiteral("Profile name:"),
|
||||||
|
QLineEdit::Normal,
|
||||||
|
currentItem->text(),
|
||||||
|
&accepted);
|
||||||
|
|
||||||
|
if (!accepted || name.trimmed().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_repository->updateProfile(profileId.value(), name)) {
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
QStringLiteral("Edit Profile"),
|
||||||
|
QStringLiteral("Failed to update profile. Names must be unique."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadProfiles(m_searchBox->text());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfilesWindow::deleteSelectedProfile()
|
||||||
|
{
|
||||||
|
const std::optional<qint64> profileId = selectedProfileId();
|
||||||
|
QListWidgetItem* currentItem = m_profilesList->currentItem();
|
||||||
|
if (!profileId.has_value() || currentItem == nullptr) {
|
||||||
|
QMessageBox::information(this,
|
||||||
|
QStringLiteral("Delete Profile"),
|
||||||
|
QStringLiteral("Select a profile first."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QMessageBox::StandardButton confirm = QMessageBox::question(
|
||||||
this,
|
this,
|
||||||
[showTodo]() { showTodo(QStringLiteral("Delete Profile")); });
|
QStringLiteral("Delete Profile"),
|
||||||
|
QStringLiteral("Delete profile '%1'?").arg(currentItem->text()),
|
||||||
|
QMessageBox::Yes | QMessageBox::No,
|
||||||
|
QMessageBox::No);
|
||||||
|
|
||||||
|
if (confirm != QMessageBox::Yes) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProfilesWindow::populateSampleProfiles()
|
if (!m_repository->deleteProfile(profileId.value())) {
|
||||||
{
|
QMessageBox::warning(this,
|
||||||
const QStringList sampleProfiles = {
|
QStringLiteral("Delete Profile"),
|
||||||
QStringLiteral("Production Bastion"),
|
QStringLiteral("Failed to delete profile."));
|
||||||
QStringLiteral("Staging API Host"),
|
return;
|
||||||
QStringLiteral("Local Lab VM"),
|
|
||||||
QStringLiteral("CI Build Agent")};
|
|
||||||
|
|
||||||
m_profilesList->addItems(sampleProfiles);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProfilesWindow::filterProfiles(const QString& query)
|
loadProfiles(m_searchBox->text());
|
||||||
{
|
|
||||||
const QString trimmed = query.trimmed();
|
|
||||||
|
|
||||||
for (int i = 0; i < m_profilesList->count(); ++i) {
|
|
||||||
QListWidgetItem* item = m_profilesList->item(i);
|
|
||||||
const bool matches = trimmed.isEmpty()
|
|
||||||
|| item->text().contains(trimmed, Qt::CaseInsensitive);
|
|
||||||
item->setHidden(!matches);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProfilesWindow::openSessionForItem(QListWidgetItem* item)
|
void ProfilesWindow::openSessionForItem(QListWidgetItem* item)
|
||||||
|
|||||||
@@ -3,7 +3,10 @@
|
|||||||
|
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -12,6 +15,7 @@ class QListWidgetItem;
|
|||||||
class QLineEdit;
|
class QLineEdit;
|
||||||
class QPushButton;
|
class QPushButton;
|
||||||
class SessionWindow;
|
class SessionWindow;
|
||||||
|
class ProfileRepository;
|
||||||
|
|
||||||
class ProfilesWindow : public QMainWindow
|
class ProfilesWindow : public QMainWindow
|
||||||
{
|
{
|
||||||
@@ -19,6 +23,7 @@ class ProfilesWindow : public QMainWindow
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ProfilesWindow(QWidget* parent = nullptr);
|
explicit ProfilesWindow(QWidget* parent = nullptr);
|
||||||
|
~ProfilesWindow() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QLineEdit* m_searchBox;
|
QLineEdit* m_searchBox;
|
||||||
@@ -27,10 +32,14 @@ private:
|
|||||||
QPushButton* m_editButton;
|
QPushButton* m_editButton;
|
||||||
QPushButton* m_deleteButton;
|
QPushButton* m_deleteButton;
|
||||||
std::vector<QPointer<SessionWindow>> m_sessionWindows;
|
std::vector<QPointer<SessionWindow>> m_sessionWindows;
|
||||||
|
std::unique_ptr<ProfileRepository> m_repository;
|
||||||
|
|
||||||
void setupUi();
|
void setupUi();
|
||||||
void populateSampleProfiles();
|
void loadProfiles(const QString& query = QString());
|
||||||
void filterProfiles(const QString& query);
|
std::optional<qint64> selectedProfileId() const;
|
||||||
|
void createProfile();
|
||||||
|
void editSelectedProfile();
|
||||||
|
void deleteSelectedProfile();
|
||||||
void openSessionForItem(QListWidgetItem* item);
|
void openSessionForItem(QListWidgetItem* item);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user