Implement SQLite-backed profile CRUD in Profiles window

This commit is contained in:
Keith Smith
2026-03-01 09:06:34 -07:00
parent fba7336f69
commit 09c54313af
2 changed files with 147 additions and 37 deletions

View File

@@ -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,51 +82,133 @@ 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(); });
QMessageBox::information(this, connect(m_deleteButton, &QPushButton::clicked, this, [this]() { deleteSelectedProfile(); });
QStringLiteral("Milestone 0"),
QStringLiteral("%1 is planned for Milestone 1.").arg(action));
};
connect(m_newButton, &QPushButton::clicked, this, [showTodo]() { showTodo(QStringLiteral("New Profile")); });
connect(m_editButton, &QPushButton::clicked, this, [showTodo]() { showTodo(QStringLiteral("Edit Profile")); });
connect(m_deleteButton,
&QPushButton::clicked,
this,
[showTodo]() { showTodo(QStringLiteral("Delete Profile")); });
} }
void ProfilesWindow::populateSampleProfiles() void ProfilesWindow::loadProfiles(const QString& query)
{ {
const QStringList sampleProfiles = { m_profilesList->clear();
QStringLiteral("Production Bastion"),
QStringLiteral("Staging API Host"),
QStringLiteral("Local Lab VM"),
QStringLiteral("CI Build Agent")};
m_profilesList->addItems(sampleProfiles); const std::vector<Profile> profiles = m_repository->listProfiles(query);
} for (const Profile& profile : profiles) {
auto* item = new QListWidgetItem(profile.name, m_profilesList);
void ProfilesWindow::filterProfiles(const QString& query) item->setData(Qt::UserRole, QVariant::fromValue(profile.id));
{
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);
} }
} }
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,
QStringLiteral("Edit Profile"),
QStringLiteral("Select a profile first."));
return;
}
bool accepted = false;
const QString name = QInputDialog::getText(this,
QStringLiteral("Edit Profile"),
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,
QStringLiteral("Delete Profile"),
QStringLiteral("Delete profile '%1'?").arg(currentItem->text()),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::No);
if (confirm != QMessageBox::Yes) {
return;
}
if (!m_repository->deleteProfile(profileId.value())) {
QMessageBox::warning(this,
QStringLiteral("Delete Profile"),
QStringLiteral("Failed to delete profile."));
return;
}
loadProfiles(m_searchBox->text());
}
void ProfilesWindow::openSessionForItem(QListWidgetItem* item) void ProfilesWindow::openSessionForItem(QListWidgetItem* item)
{ {
if (item == nullptr) { if (item == nullptr) {

View File

@@ -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);
}; };