Skip to content

Commit 554cb34

Browse files
committed
v4 build installer test
1 parent 364976b commit 554cb34

5 files changed

Lines changed: 97 additions & 65 deletions

File tree

.github/workflows/build-installer.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ env:
1010
BUILD_TYPE: Release
1111
QT_VERSION: "6.8.3"
1212
QT_MODULES: "qtcharts qtwebsockets qtmultimedia"
13-
QT_IFW_VERSION: "4.8"
13+
QT_IFW_VERSION: "4.7"
14+
QT_IFW_TOOL: "tools_ifw"
1415

1516
jobs:
1617
build-installer:
@@ -103,15 +104,15 @@ jobs:
103104
run: |
104105
pip install aqtinstall
105106
if [ "${{ runner.os }}" == "Windows" ]; then
106-
aqt install-tool windows desktop qt.tools.ifw.${{ env.QT_IFW_VERSION }} \
107+
aqt install-tool windows desktop ${{ env.QT_IFW_TOOL }} \
107108
--outputdir "${{ runner.temp }}/qtifw"
108109
echo "QT_IFW_DIR=${{ runner.temp }}/qtifw/Tools/QtInstallerFramework/${{ env.QT_IFW_VERSION }}" >> $GITHUB_ENV
109110
elif [ "${{ runner.os }}" == "macOS" ]; then
110-
aqt install-tool mac desktop qt.tools.ifw.${{ env.QT_IFW_VERSION }} \
111+
aqt install-tool mac desktop ${{ env.QT_IFW_TOOL }} \
111112
--outputdir "${{ runner.temp }}/qtifw"
112113
echo "QT_IFW_DIR=${{ runner.temp }}/qtifw/Tools/QtInstallerFramework/${{ env.QT_IFW_VERSION }}" >> $GITHUB_ENV
113114
else
114-
aqt install-tool linux desktop qt.tools.ifw.${{ env.QT_IFW_VERSION }} \
115+
aqt install-tool linux desktop ${{ env.QT_IFW_TOOL }} \
115116
--outputdir "${{ runner.temp }}/qtifw"
116117
echo "QT_IFW_DIR=${{ runner.temp }}/qtifw/Tools/QtInstallerFramework/${{ env.QT_IFW_VERSION }}" >> $GITHUB_ENV
117118
fi

fincept-qt/src/app/MainWindow.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -727,7 +727,7 @@ void MainWindow::setup_dock_screens() {
727727

728728
// Eagerly constructed: lightweight screens with no data fetching or timers.
729729
dock_router_->register_screen("report_builder", new screens::ReportBuilderScreen);
730-
dock_router_->register_screen("profile", new screens::ProfileScreen);
730+
dock_router_->register_factory("profile", []() { return new screens::ProfileScreen; });
731731
dock_router_->register_screen("settings", new screens::SettingsScreen);
732732
dock_router_->register_screen("about", new screens::AboutScreen);
733733
dock_router_->register_screen("support", new screens::SupportScreen);

fincept-qt/src/core/config/ProfileManager.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "core/config/ProfileManager.h"
22
#include "core/config/AppPaths.h"
3+
#include "core/logging/Logger.h"
34

45
#include <QDir>
56
#include <QFile>
@@ -90,13 +91,22 @@ QStringList ProfileManager::list_profiles() const {
9091
}
9192

9293
void ProfileManager::create_profile(const QString& name) {
94+
// Sanitise: same rules as set_active()
95+
QString clean = name.trimmed().toLower();
96+
for (QChar& c : clean) {
97+
if (!c.isLetterOrNumber() && c != '-' && c != '_')
98+
c = '_';
99+
}
100+
if (clean.isEmpty())
101+
return;
102+
93103
QStringList profiles = list_profiles();
94-
if (!profiles.contains(name)) {
95-
profiles.append(name);
104+
if (!profiles.contains(clean)) {
105+
profiles.append(clean);
96106
save_manifest(profiles);
97107
}
98108
// Create directory tree eagerly so it's ready for use
99-
const QString root = AppPaths::root() + "/profiles/" + name;
109+
const QString root = AppPaths::root() + "/profiles/" + clean;
100110
QDir().mkpath(root + "/data");
101111
QDir().mkpath(root + "/logs");
102112
QDir().mkpath(root + "/cache");
@@ -129,6 +139,8 @@ void ProfileManager::save_manifest(const QStringList& profiles) const {
129139
if (f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
130140
f.write(QJsonDocument(obj).toJson(QJsonDocument::Indented));
131141
f.close();
142+
} else {
143+
LOG_ERROR("ProfileManager", "Failed to write profiles manifest: " + manifest_path());
132144
}
133145
}
134146

fincept-qt/src/screens/profile/ProfileScreen.cpp

Lines changed: 74 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <QApplication>
99
#include <QClipboard>
10+
#include <QDesktopServices>
1011
#include <QFrame>
1112
#include <QGridLayout>
1213
#include <QHBoxLayout>
@@ -20,10 +21,10 @@ namespace fincept::screens {
2021

2122
static const char* MF = "font-family:'Consolas',monospace;";
2223
static QString PANEL_SS() {
23-
return QString("background:%1;border:1px solid %2;").arg(ui::colors::BG_BASE, ui::colors::BORDER_DIM);
24+
return QString("background:%1;border:1px solid %2;").arg(QString(ui::colors::BG_BASE), QString(ui::colors::BORDER_DIM));
2425
}
2526
static QString HDR_SS() {
26-
return QString("background:%1;border-bottom:1px solid %2;").arg(ui::colors::BG_RAISED, ui::colors::BORDER_DIM);
27+
return QString("background:%1;border-bottom:1px solid %2;").arg(QString(ui::colors::BG_RAISED), QString(ui::colors::BORDER_DIM));
2728
}
2829

2930
QWidget* ProfileScreen::make_panel(const QString& title) {
@@ -39,7 +40,7 @@ QWidget* ProfileScreen::make_panel(const QString& title) {
3940
hl->setContentsMargins(12, 0, 12, 0);
4041
auto* t = new QLabel(title);
4142
t->setStyleSheet(
42-
QString("color:%1;font-size:12px;font-weight:700;background:transparent;letter-spacing:0.5px;%2").arg(ui::colors::AMBER, MF));
43+
QString("color:%1;font-size:12px;font-weight:700;background:transparent;letter-spacing:0.5px;%2").arg(QString(ui::colors::AMBER), MF));
4344
hl->addWidget(t);
4445
hl->addStretch();
4546
vl->addWidget(hdr);
@@ -149,22 +150,19 @@ void ProfileScreen::build_tab_nav(QVBoxLayout* root) {
149150
auto* btn = new QPushButton(tabs[i]);
150151
btn->setFixedHeight(32);
151152
btn->setCursor(Qt::PointingHandCursor);
152-
btn->setProperty("section_idx", i);
153153
connect(btn, &QPushButton::clicked, this, [this, i]() { on_section_changed(i); });
154154
hl->addWidget(btn);
155+
nav_buttons_.append(btn);
155156
}
156157
hl->addStretch();
157158
root->addWidget(nav);
158159
}
159160

160161
void ProfileScreen::on_section_changed(int index) {
161162
sections_->setCurrentIndex(index);
162-
for (auto* btn : findChildren<QPushButton*>()) {
163-
QVariant v = btn->property("section_idx");
164-
if (!v.isValid())
165-
continue;
166-
btn->setStyleSheet(
167-
v.toInt() == index
163+
for (int i = 0; i < nav_buttons_.size(); ++i) {
164+
nav_buttons_[i]->setStyleSheet(
165+
i == index
168166
? QString("QPushButton{background:%1;color:%2;border:none;padding:0 14px;"
169167
"font-size:12px;font-weight:700;letter-spacing:0.5px;font-family:'Consolas',monospace;}")
170168
.arg(ui::colors::AMBER, ui::colors::BG_BASE)
@@ -475,16 +473,24 @@ QWidget* ProfileScreen::build_support() {
475473
auto* lrl = new QHBoxLayout(lr);
476474
lrl->setContentsMargins(12, 10, 12, 10);
477475
lrl->setSpacing(8);
478-
for (auto& s : {"DOCS", "GITHUB", "DISCORD", "FAQ"}) {
479-
auto* b = new QPushButton(s);
476+
auto make_link_btn = [&](const QString& label, const QString& url) {
477+
auto* b = new QPushButton(label);
480478
b->setFixedHeight(26);
479+
b->setCursor(Qt::PointingHandCursor);
481480
b->setStyleSheet(
482481
QString("QPushButton{background:%1;color:%2;border:1px solid %3;padding:0 12px;"
483482
"font-size:11px;font-family:'Consolas',monospace;}QPushButton:hover{color:%4;background:%5;}")
484483
.arg(ui::colors::BG_RAISED, ui::colors::CYAN, ui::colors::BORDER_DIM,
485484
ui::colors::TEXT_PRIMARY, ui::colors::BG_HOVER));
485+
connect(b, &QPushButton::clicked, this, [url]() {
486+
QDesktopServices::openUrl(QUrl(url));
487+
});
486488
lrl->addWidget(b);
487-
}
489+
};
490+
make_link_btn("DOCS", "https://github.com/Fincept-Corporation/FinceptTerminal/tree/main/docs");
491+
make_link_btn("GITHUB", "https://github.com/Fincept-Corporation/FinceptTerminal");
492+
make_link_btn("DISCORD", "https://discord.gg/ae87a8ygbN");
493+
make_link_btn("FAQ", "https://github.com/Fincept-Corporation/FinceptTerminal/wiki");
488494
lrl->addStretch();
489495
lvl->addWidget(lr);
490496
vl->addWidget(lp);
@@ -529,7 +535,7 @@ void ProfileScreen::refresh_all() {
529535
.arg(MF));
530536
bill_plan_->setText(s.account_type().toUpper());
531537
bill_credits_->setText(QString::number(s.user_info.credit_balance, 'f', 2));
532-
bill_support_->setText("COMMUNITY");
538+
// bill_support_ is populated by fetch_billing_data() from the API; leave it as-is here
533539
}
534540

535541
void ProfileScreen::fetch_usage_data() {
@@ -539,50 +545,52 @@ void ProfileScreen::fetch_usage_data() {
539545
usg_plan_->setText(s.account_type().toUpper());
540546
usg_rate_->setText("");
541547

542-
auth::UserApi::instance().get_user_usage(30, [this](auth::ApiResponse r) {
548+
QPointer<ProfileScreen> self = this;
549+
auth::UserApi::instance().get_user_usage(30, [self](auth::ApiResponse r) {
550+
if (!self) return;
543551
if (!r.success) {
544552
LOG_WARN("Profile", "Usage fetch failed: " + r.error);
545553
return;
546554
}
547555
auto data = r.data.contains("data") ? r.data["data"].toObject() : r.data;
548556
if (data.contains("account")) {
549557
auto a = data["account"].toObject();
550-
usg_credits_->setText(QString::number(a["credit_balance"].toDouble(), 'f', 0));
551-
usg_plan_->setText(a["account_type"].toString().toUpper());
552-
usg_rate_->setText(QString::number(a["rate_limit_per_hour"].toInt()));
558+
self->usg_credits_->setText(QString::number(a["credit_balance"].toDouble(), 'f', 0));
559+
self->usg_plan_->setText(a["account_type"].toString().toUpper());
560+
self->usg_rate_->setText(QString::number(a["rate_limit_per_hour"].toInt()));
553561
}
554562
if (data.contains("summary")) {
555563
auto s = data["summary"].toObject();
556-
usg_total_req_->setText(QString::number(s["total_requests"].toInt()));
557-
usg_cred_used_->setText(QString::number(s["total_credits_used"].toDouble(), 'f', 0));
558-
usg_avg_cred_->setText(QString::number(s["avg_credits_per_request"].toDouble(), 'f', 2));
559-
usg_avg_resp_->setText(QString::number(s["avg_response_time_ms"].toDouble(), 'f', 0));
564+
self->usg_total_req_->setText(QString::number(s["total_requests"].toInt()));
565+
self->usg_cred_used_->setText(QString::number(s["total_credits_used"].toDouble(), 'f', 0));
566+
self->usg_avg_cred_->setText(QString::number(s["avg_credits_per_request"].toDouble(), 'f', 2));
567+
self->usg_avg_resp_->setText(QString::number(s["avg_response_time_ms"].toDouble(), 'f', 0));
560568
}
561569
if (data.contains("daily_usage")) {
562570
auto d = data["daily_usage"].toArray();
563-
usg_daily_table_->setRowCount(0);
571+
self->usg_daily_table_->setRowCount(0);
564572
for (int i = d.size() - 1; i >= 0 && i >= d.size() - 10; i--) {
565573
auto e = d[i].toObject();
566-
int row = usg_daily_table_->rowCount();
567-
usg_daily_table_->insertRow(row);
568-
usg_daily_table_->setItem(row, 0, new QTableWidgetItem(e["date"].toString()));
569-
usg_daily_table_->setItem(row, 1, new QTableWidgetItem(QString::number(e["request_count"].toInt())));
570-
usg_daily_table_->setItem(row, 2,
574+
int row = self->usg_daily_table_->rowCount();
575+
self->usg_daily_table_->insertRow(row);
576+
self->usg_daily_table_->setItem(row, 0, new QTableWidgetItem(e["date"].toString()));
577+
self->usg_daily_table_->setItem(row, 1, new QTableWidgetItem(QString::number(e["request_count"].toInt())));
578+
self->usg_daily_table_->setItem(row, 2,
571579
new QTableWidgetItem(QString::number(e["credits_used"].toDouble(), 'f', 0)));
572580
}
573581
}
574582
if (data.contains("endpoint_breakdown")) {
575583
auto eps = data["endpoint_breakdown"].toArray();
576-
usg_endpoint_table_->setRowCount(0);
584+
self->usg_endpoint_table_->setRowCount(0);
577585
for (const auto& v : eps) {
578586
auto e = v.toObject();
579-
int row = usg_endpoint_table_->rowCount();
580-
usg_endpoint_table_->insertRow(row);
581-
usg_endpoint_table_->setItem(row, 0, new QTableWidgetItem(e["endpoint"].toString()));
582-
usg_endpoint_table_->setItem(row, 1, new QTableWidgetItem(QString::number(e["request_count"].toInt())));
583-
usg_endpoint_table_->setItem(
587+
int row = self->usg_endpoint_table_->rowCount();
588+
self->usg_endpoint_table_->insertRow(row);
589+
self->usg_endpoint_table_->setItem(row, 0, new QTableWidgetItem(e["endpoint"].toString()));
590+
self->usg_endpoint_table_->setItem(row, 1, new QTableWidgetItem(QString::number(e["request_count"].toInt())));
591+
self->usg_endpoint_table_->setItem(
584592
row, 2, new QTableWidgetItem(QString::number(e["credits_used"].toDouble(), 'f', 0)));
585-
usg_endpoint_table_->setItem(
593+
self->usg_endpoint_table_->setItem(
586594
row, 3, new QTableWidgetItem(QString::number(e["avg_response_time_ms"].toDouble(), 'f', 0)));
587595
}
588596
}
@@ -591,17 +599,20 @@ void ProfileScreen::fetch_usage_data() {
591599

592600
void ProfileScreen::fetch_billing_data() {
593601
LOG_INFO("Profile", "Fetching billing data...");
594-
auth::UserApi::instance().get_user_subscription([this](auth::ApiResponse r) {
602+
QPointer<ProfileScreen> self = this;
603+
auth::UserApi::instance().get_user_subscription([self](auth::ApiResponse r) {
604+
if (!self) return;
595605
if (!r.success) {
596606
LOG_WARN("Profile", "Subscription fetch failed: " + r.error);
597607
return;
598608
}
599609
auto d = r.data.contains("data") ? r.data["data"].toObject() : r.data;
600-
bill_plan_->setText(d["account_type"].toString().toUpper());
601-
bill_credits_->setText(QString::number(d["credit_balance"].toDouble(), 'f', 2));
602-
bill_support_->setText(d["support_type"].toString().toUpper());
610+
self->bill_plan_->setText(d["account_type"].toString().toUpper());
611+
self->bill_credits_->setText(QString::number(d["credit_balance"].toDouble(), 'f', 2));
612+
self->bill_support_->setText(d["support_type"].toString().toUpper());
603613
});
604-
auth::UserApi::instance().get_payment_history(1, 20, [this](auth::ApiResponse r) {
614+
auth::UserApi::instance().get_payment_history(1, 20, [self](auth::ApiResponse r) {
615+
if (!self) return;
605616
if (!r.success) {
606617
LOG_WARN("Profile", "Payment history failed: " + r.error);
607618
return;
@@ -612,24 +623,26 @@ void ProfileScreen::fetch_billing_data() {
612623
p = d["transactions"].toArray();
613624
if (p.isEmpty() && d.contains("data"))
614625
p = d["data"].toArray();
615-
bill_history_->setRowCount(0);
626+
self->bill_history_->setRowCount(0);
616627
for (const auto& v : p) {
617628
auto e = v.toObject();
618-
int row = bill_history_->rowCount();
619-
bill_history_->insertRow(row);
620-
bill_history_->setItem(row, 0, new QTableWidgetItem(e["created_at"].toString().left(10)));
621-
bill_history_->setItem(row, 1, new QTableWidgetItem(e["plan_name"].toString()));
622-
bill_history_->setItem(row, 2,
629+
int row = self->bill_history_->rowCount();
630+
self->bill_history_->insertRow(row);
631+
self->bill_history_->setItem(row, 0, new QTableWidgetItem(e["created_at"].toString().left(10)));
632+
self->bill_history_->setItem(row, 1, new QTableWidgetItem(e["plan_name"].toString()));
633+
self->bill_history_->setItem(row, 2,
623634
new QTableWidgetItem(QString("$%1").arg(e["amount_usd"].toDouble(), 0, 'f', 2)));
624-
bill_history_->setItem(row, 3, new QTableWidgetItem(QString::number(e["credits_purchased"].toInt())));
625-
bill_history_->setItem(row, 4, new QTableWidgetItem(e["status"].toString().toUpper()));
635+
self->bill_history_->setItem(row, 3, new QTableWidgetItem(QString::number(e["credits_purchased"].toInt())));
636+
self->bill_history_->setItem(row, 4, new QTableWidgetItem(e["status"].toString().toUpper()));
626637
}
627638
});
628639
}
629640

630641
void ProfileScreen::fetch_login_history() {
631642
LOG_INFO("Profile", "Fetching login history...");
632-
auth::UserApi::instance().get_login_history(20, 0, [this](auth::ApiResponse r) {
643+
QPointer<ProfileScreen> self = this;
644+
auth::UserApi::instance().get_login_history(20, 0, [self](auth::ApiResponse r) {
645+
if (!self) return;
633646
if (!r.success) {
634647
LOG_WARN("Profile", "Login history failed: " + r.error);
635648
return;
@@ -638,14 +651,14 @@ void ProfileScreen::fetch_login_history() {
638651
auto h = d["login_history"].toArray();
639652
if (h.isEmpty())
640653
h = d["history"].toArray();
641-
sec_login_hist_->setRowCount(0);
654+
self->sec_login_hist_->setRowCount(0);
642655
for (const auto& v : h) {
643656
auto e = v.toObject();
644-
int row = sec_login_hist_->rowCount();
645-
sec_login_hist_->insertRow(row);
646-
sec_login_hist_->setItem(row, 0, new QTableWidgetItem(e["timestamp"].toString().left(19)));
647-
sec_login_hist_->setItem(row, 1, new QTableWidgetItem(e["ip_address"].toString()));
648-
sec_login_hist_->setItem(row, 2, new QTableWidgetItem(e["status"].toString().toUpper()));
657+
int row = self->sec_login_hist_->rowCount();
658+
self->sec_login_hist_->insertRow(row);
659+
self->sec_login_hist_->setItem(row, 0, new QTableWidgetItem(e["timestamp"].toString().left(19)));
660+
self->sec_login_hist_->setItem(row, 1, new QTableWidgetItem(e["ip_address"].toString()));
661+
self->sec_login_hist_->setItem(row, 2, new QTableWidgetItem(e["status"].toString().toUpper()));
649662
}
650663
});
651664
}
@@ -690,6 +703,8 @@ void ProfileScreen::show_edit_profile_dialog() {
690703
QString("QPushButton{background:%1;color:%2;border:none;padding:0 16px;"
691704
"font-size:11px;font-weight:700;font-family:'Consolas',monospace;}QPushButton:hover{background:#b45309;}")
692705
.arg(ui::colors::AMBER, ui::colors::BG_BASE));
706+
QPointer<ProfileScreen> self = this;
707+
QPointer<QDialog> dlg_ptr = dlg;
693708
connect(sv, &QPushButton::clicked, this, [=]() {
694709
QJsonObject data;
695710
if (!un->text().trimmed().isEmpty())
@@ -698,10 +713,12 @@ void ProfileScreen::show_edit_profile_dialog() {
698713
data["phone"] = ph->text().trimmed();
699714
if (!co->text().trimmed().isEmpty())
700715
data["country"] = co->text().trimmed();
701-
auth::UserApi::instance().update_user_profile(data, [this, dlg](auth::ApiResponse r) {
716+
auth::UserApi::instance().update_user_profile(data, [self, dlg_ptr](auth::ApiResponse r) {
717+
if (!self) return;
702718
if (r.success) {
703719
auth::AuthManager::instance().refresh_user_data();
704-
dlg->accept();
720+
if (dlg_ptr)
721+
dlg_ptr->accept();
705722
}
706723
});
707724
});

fincept-qt/src/screens/profile/ProfileScreen.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <QDialog>
33
#include <QLabel>
44
#include <QLineEdit>
5+
#include <QList>
56
#include <QPushButton>
67
#include <QStackedWidget>
78
#include <QTableWidget>
@@ -19,6 +20,7 @@ class ProfileScreen : public QWidget {
1920

2021
private:
2122
QStackedWidget* sections_ = nullptr;
23+
QList<QPushButton*> nav_buttons_;
2224

2325
// Header labels
2426
QLabel* username_header_ = nullptr;

0 commit comments

Comments
 (0)