v0.1.2: Preferences page, About page, full icon set, redesigned edit form
- Rework the add/edit form onto a generic, catalog-driven field model (JobFieldCatalog + JobEditModel), replacing the old hand-written ApplicationEditDialog with ApplicationEditPage - Add a Preferences page (reachable from the hamburger menu) with a KColorSchemeManager-backed color scheme switcher - Add an About page reading from KAboutData - Ship a full hicolor icon set (16-128px + scalable) via ecm_install_icons instead of a single flat SVG - Restyle the edit form after KDE System Settings' Audio page: Kirigami.ListSectionHeader per category, flat separated rows, no card borders - Explicitly declare Kirigami/KirigamiAddons/ColorScheme as CMake dependencies instead of relying on the QML import scanner alone - Add KDEClangFormat/KDEGitCommitHooks, a CMakePresets.json, and a REUSE + clang-format lint CI workflow - Reformat SPDX headers to include copyright, remove debug screenshot scaffolding
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2026 ToServeTheKing <[email protected]>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "jobeditmodel.h"
|
||||
|
||||
#include <QDate>
|
||||
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
using JobFieldCatalog::ComboRow;
|
||||
using JobFieldCatalog::DateRow;
|
||||
using JobFieldCatalog::Field;
|
||||
using JobFieldCatalog::SpinBoxRow;
|
||||
using JobFieldCatalog::TextAreaRow;
|
||||
using JobFieldCatalog::TextRow;
|
||||
|
||||
namespace
|
||||
{
|
||||
/// The three salary fields share the "0 shown in the UI means unset, -1 stored" convention.
|
||||
bool isSalaryField(const QString &id)
|
||||
{
|
||||
return id == QLatin1String("salaryMin") || id == QLatin1String("salaryMax") || id == QLatin1String("salaryExpectation");
|
||||
}
|
||||
}
|
||||
|
||||
JobEditModel::JobEditModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
, m_fields(JobFieldCatalog::fields())
|
||||
{
|
||||
resetValues();
|
||||
}
|
||||
|
||||
int JobEditModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return m_fields.size();
|
||||
}
|
||||
|
||||
QVariant JobEditModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid() || index.row() < 0 || index.row() >= m_fields.size()) {
|
||||
return {};
|
||||
}
|
||||
const Field &field = m_fields.at(index.row());
|
||||
switch (role) {
|
||||
case FieldIdRole:
|
||||
return field.id;
|
||||
case CategoryIdRole:
|
||||
return field.categoryId;
|
||||
case LabelRole:
|
||||
return field.label;
|
||||
case RowTypeRole:
|
||||
return static_cast<int>(field.rowType);
|
||||
case ValueRole:
|
||||
return m_values.value(field.id);
|
||||
case ComboOptionsRole:
|
||||
return field.comboOptions;
|
||||
case PlaceholderRole:
|
||||
return field.placeholder;
|
||||
case SpinMinRole:
|
||||
return field.spinMin;
|
||||
case SpinMaxRole:
|
||||
return field.spinMax;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> JobEditModel::roleNames() const
|
||||
{
|
||||
return {
|
||||
{FieldIdRole, "fieldId"},
|
||||
{CategoryIdRole, "categoryId"},
|
||||
{LabelRole, "label"},
|
||||
{RowTypeRole, "rowType"},
|
||||
{ValueRole, "value"},
|
||||
{ComboOptionsRole, "comboOptions"},
|
||||
{PlaceholderRole, "placeholder"},
|
||||
{SpinMinRole, "spinMin"},
|
||||
{SpinMaxRole, "spinMax"},
|
||||
};
|
||||
}
|
||||
|
||||
JobsModel *JobEditModel::jobsModel() const
|
||||
{
|
||||
return m_jobsModel;
|
||||
}
|
||||
|
||||
void JobEditModel::setJobsModel(JobsModel *model)
|
||||
{
|
||||
if (m_jobsModel == model) {
|
||||
return;
|
||||
}
|
||||
m_jobsModel = model;
|
||||
Q_EMIT jobsModelChanged();
|
||||
}
|
||||
|
||||
int JobEditModel::editingJobId() const
|
||||
{
|
||||
return m_editingJobId;
|
||||
}
|
||||
|
||||
void JobEditModel::setEditingJobId(int id)
|
||||
{
|
||||
if (m_editingJobId == id) {
|
||||
return;
|
||||
}
|
||||
m_editingJobId = id;
|
||||
resetValues();
|
||||
Q_EMIT editingJobIdChanged();
|
||||
}
|
||||
|
||||
void JobEditModel::resetValues()
|
||||
{
|
||||
m_values.clear();
|
||||
|
||||
if (m_editingJobId < 0 || !m_jobsModel) {
|
||||
m_values[u"currency"_s] = u"USD"_s;
|
||||
m_values[u"stage"_s] = u"Applied"_s;
|
||||
m_values[u"dateApplied"_s] = QDate::currentDate();
|
||||
m_values[u"remoteType"_s] = u"Unspecified"_s;
|
||||
m_values[u"salaryMin"_s] = 0;
|
||||
m_values[u"salaryMax"_s] = 0;
|
||||
m_values[u"salaryExpectation"_s] = 0;
|
||||
} else {
|
||||
const QVariantMap data = m_jobsModel->jobData(m_editingJobId);
|
||||
for (const Field &field : m_fields) {
|
||||
QVariant value = data.value(field.id);
|
||||
if (field.id == u"remoteType"_s && value.toString().isEmpty()) {
|
||||
value = u"Unspecified"_s;
|
||||
}
|
||||
if (isSalaryField(field.id) && value.toInt() < 0) {
|
||||
value = 0;
|
||||
}
|
||||
m_values[field.id] = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (rowCount() > 0) {
|
||||
Q_EMIT dataChanged(index(0), index(rowCount() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
QVariantList JobEditModel::categories() const
|
||||
{
|
||||
QVariantList result;
|
||||
for (const JobFieldCatalog::Category &category : JobFieldCatalog::categories()) {
|
||||
result.append(QVariantMap{{u"id"_s, category.id}, {u"title"_s, category.title}});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString JobEditModel::lastError() const
|
||||
{
|
||||
return m_lastError;
|
||||
}
|
||||
|
||||
void JobEditModel::setValue(int row, const QVariant &value)
|
||||
{
|
||||
if (row < 0 || row >= m_fields.size()) {
|
||||
return;
|
||||
}
|
||||
m_values[m_fields.at(row).id] = value;
|
||||
Q_EMIT dataChanged(index(row), index(row), {ValueRole});
|
||||
}
|
||||
|
||||
bool JobEditModel::save()
|
||||
{
|
||||
if (!m_jobsModel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariantMap fields;
|
||||
for (auto it = m_values.constBegin(); it != m_values.constEnd(); ++it) {
|
||||
fields.insert(it.key(), it.value());
|
||||
}
|
||||
if (fields.value(u"remoteType"_s).toString() == u"Unspecified"_s) {
|
||||
fields[u"remoteType"_s] = QString();
|
||||
}
|
||||
for (const QString &id : {u"salaryMin"_s, u"salaryMax"_s, u"salaryExpectation"_s}) {
|
||||
if (fields.value(id).toInt() <= 0) {
|
||||
fields[id] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
const bool ok = m_editingJobId < 0 ? m_jobsModel->addJob(fields) : m_jobsModel->updateJob(m_editingJobId, fields);
|
||||
if (!ok) {
|
||||
m_lastError = m_jobsModel->lastError();
|
||||
Q_EMIT lastErrorChanged();
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool JobEditModel::deleteJob()
|
||||
{
|
||||
if (!m_jobsModel || m_editingJobId < 0) {
|
||||
return false;
|
||||
}
|
||||
return m_jobsModel->removeJob(m_editingJobId);
|
||||
}
|
||||
Reference in New Issue
Block a user