- API token loaded from .env at build time via qmake - All fields required with validation - Email validation with regex - Cleaner UI with placeholder text - Added .env to gitignore 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
151 lines
4.6 KiB
C++
151 lines
4.6 KiB
C++
#include "reportissuedialog.h"
|
|
#include "ui_reportissuedialog.h"
|
|
|
|
#include <QJsonArray>
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
#include <QMessageBox>
|
|
#include <QNetworkRequest>
|
|
#include <QRegularExpression>
|
|
|
|
namespace {
|
|
const QString GITEA_BASE_URL = "https://code.redline.llc";
|
|
const QString REPO_OWNER = "njohnson";
|
|
const QString REPO_NAME = "XPlor";
|
|
}
|
|
|
|
ReportIssueDialog::ReportIssueDialog(QWidget *parent)
|
|
: QDialog(parent)
|
|
, ui(new Ui::ReportIssueDialog)
|
|
, mNetworkManager(new QNetworkAccessManager(this))
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
#ifdef GITEA_ACCESS_TOKEN
|
|
mAccessToken = GITEA_ACCESS_TOKEN;
|
|
#endif
|
|
|
|
connect(ui->buttonSend, &QPushButton::clicked, this, &ReportIssueDialog::onSendClicked);
|
|
connect(ui->buttonCancel, &QPushButton::clicked, this, &ReportIssueDialog::onCancelClicked);
|
|
connect(mNetworkManager, &QNetworkAccessManager::finished, this, &ReportIssueDialog::onNetworkReplyFinished);
|
|
}
|
|
|
|
ReportIssueDialog::~ReportIssueDialog()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
bool ReportIssueDialog::isValidEmail(const QString &email)
|
|
{
|
|
static QRegularExpression emailRegex(
|
|
R"(^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$)"
|
|
);
|
|
return emailRegex.match(email).hasMatch();
|
|
}
|
|
|
|
bool ReportIssueDialog::validateFields()
|
|
{
|
|
QString summary = ui->lineEditSummary->text().trimmed();
|
|
QString details = ui->textEditDetails->toPlainText().trimmed();
|
|
QString email = ui->lineEditEmail->text().trimmed();
|
|
|
|
if (summary.isEmpty()) {
|
|
QMessageBox::warning(this, tr("Required Field"), tr("Please enter a summary for the issue."));
|
|
ui->lineEditSummary->setFocus();
|
|
return false;
|
|
}
|
|
|
|
if (details.isEmpty()) {
|
|
QMessageBox::warning(this, tr("Required Field"), tr("Please describe the issue in detail."));
|
|
ui->textEditDetails->setFocus();
|
|
return false;
|
|
}
|
|
|
|
if (email.isEmpty()) {
|
|
QMessageBox::warning(this, tr("Required Field"), tr("Please enter your email address."));
|
|
ui->lineEditEmail->setFocus();
|
|
return false;
|
|
}
|
|
|
|
if (!isValidEmail(email)) {
|
|
QMessageBox::warning(this, tr("Invalid Email"), tr("Please enter a valid email address."));
|
|
ui->lineEditEmail->setFocus();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ReportIssueDialog::onSendClicked()
|
|
{
|
|
if (!validateFields()) {
|
|
return;
|
|
}
|
|
|
|
QString title = ui->lineEditSummary->text().trimmed();
|
|
QString details = ui->textEditDetails->toPlainText().trimmed();
|
|
QString email = ui->lineEditEmail->text().trimmed();
|
|
|
|
QString body = details + QString("\n\n---\nContact: %1").arg(email);
|
|
|
|
ui->buttonSend->setEnabled(false);
|
|
ui->buttonSend->setText(tr("Sending..."));
|
|
|
|
sendIssueReport(title, body, email);
|
|
}
|
|
|
|
void ReportIssueDialog::onCancelClicked()
|
|
{
|
|
reject();
|
|
}
|
|
|
|
void ReportIssueDialog::sendIssueReport(const QString &title, const QString &body, const QString &/*email*/)
|
|
{
|
|
QUrl url(QString("%1/api/v1/repos/%2/%3/issues")
|
|
.arg(GITEA_BASE_URL, REPO_OWNER, REPO_NAME));
|
|
|
|
QNetworkRequest request(url);
|
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
|
|
|
if (!mAccessToken.isEmpty()) {
|
|
request.setRawHeader("Authorization", "token " + mAccessToken.toUtf8());
|
|
}
|
|
|
|
QJsonObject json;
|
|
json["title"] = title;
|
|
json["body"] = body;
|
|
json["labels"] = QJsonArray{12};
|
|
|
|
mNetworkManager->post(request, QJsonDocument(json).toJson());
|
|
}
|
|
|
|
void ReportIssueDialog::onNetworkReplyFinished(QNetworkReply *reply)
|
|
{
|
|
ui->buttonSend->setEnabled(true);
|
|
ui->buttonSend->setText(tr("Send Report"));
|
|
|
|
QByteArray responseData = reply->readAll();
|
|
QString responseStr = QString::fromUtf8(responseData);
|
|
|
|
if (reply->error() != QNetworkReply::NoError) {
|
|
QString errorStr = reply->errorString();
|
|
if (errorStr.isEmpty()) {
|
|
errorStr = "Unknown network error";
|
|
}
|
|
QMessageBox::critical(this, tr("Error"),
|
|
tr("Failed to send issue report:\n%1").arg(errorStr));
|
|
} else {
|
|
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
|
if (status == 201) {
|
|
QMessageBox::information(this, tr("Success"),
|
|
tr("Issue reported successfully. Thank you for your feedback!"));
|
|
accept();
|
|
} else {
|
|
QMessageBox::warning(this, tr("Failed"),
|
|
tr("Unexpected response from server (status %1).").arg(status));
|
|
}
|
|
}
|
|
|
|
reply->deleteLater();
|
|
}
|