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
2122static const char * MF = " font-family:'Consolas',monospace;" ;
2223static 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}
2526static 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
2930QWidget* 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
160161void 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
535541void 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
592600void 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
630641void 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 });
0 commit comments