|
35440
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
+SlackFileEditViewGoEDHomeDMsActivityFilesLater..•More+→Jiminny ...# engineering# frontend# general# infra-changes# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...Direct messagesG. Vasil Vasilev% Galya DimitrovaNikolay Ivanov®. Aneliya AngelovaAneliya Angelova, ...Stoyan TanevVesSteliyan GeorgievAdelina Petrova, Ili...R. Adelina Petrova::: AppsToastJira CloudHistoryWindowHelpSearch Jiminny Inc& platform-inner-...& 10MessagesChannel OverviewMore v+нещо от данніToday ~ обре да се видиStefka Stoyanova 12:06 PMНа Прод еявно нещо S3 пьтя му e null, може би затоване винаги се получават репортитеNikolay Nikolov 12:07 PMS3 path го взимаме от респонса от prophetStefka Stoyanova 12:08 PMпрофета е виновен :)Nikolay Nikolov 12:08 PMне, мисля че е от теста сега на Stagingтова малко не му вярвам че е на продтрябва да видим статус generated c nulls3path дали няма по базатаStefka Stoyanova 12:09 PMот 13 април го има на Прод@1Steliyan Georgiev 12:17 PMще погледна как беше в Профет, че съмзабравилSteliyan Georgiev 12:23 PM"S3 path го взимаме от респонса от prophet"@Nikolay Nikolov Ники, от кой респонс? Наколбека?Message & platform-inner-team+AaSupport Daily • in 2h 8 m100% <478Thu 16 Apr 12:52:29-zsh• 85-zsh86-zsh₴7* Unable to acce...O 88found 70 eligible24 frames, 8.4MB→ 0.9MB (9.2x), 24 JPEGs deleted44 frames, 9.6MB → 4.7MB (2.0x),44 JPEGs deletedping capture for monitor 2 (hash=-7442755362469703643, trigger=click)ping capture for monitor 1 (hash=-7442755362469703643, trigger=click)db.statement="\n\nSELECT\nsnapshot_path, \ndevice_name, \n_name, \ntimestamp ASC\nLIMIT\nrows_affected=0 rows_returned=47 elapsedfound 47 eligible frames22 frames, 7.8MB → 1.6MB (4.9x),22 JPEGs deleted23 frames,6.3MB+ 1.1MB(5.5x), 23 JPEGs deletedfound 36eligible frames17 frames, 6.OMB → 0.6MB (9.8x), 17 JPEGSdeleted17 frames, 4.5MB→ 0.7MB (6.9x), 17 JPEGs deletedping capture for monitor 2 (hash=-3186288122146735337, trigger=visual_change)ping capture for monitor 1 (hash=-2194076432514965278, trigger=click)sping capture for monitor 2 (hash=-8421734735842618831, trigger=visual_change)ping capture for monitor 2 (hash=6115512118410074134, trigger=click)ping capture for monitor 2 (hash=6795179045261365991, trigger=click)pingcapture for monitor 2 (hash=6795179045261365991, trigger=click)."db.statement="\n\nSELECT\nid, Insnapshot_path, \ndevice_name, intimestamp•_name, \n timestamp ASC\nLIMIT\n5000\n" rows_affected-0 rows_returned-62 elapsedfound 62 eligible frames32 frames, 11.2MB → 1.7MB (6.6x), 32 JPEGs deleted28 frames, 7.6MB → 1.4MB (5.6x),28 JPEGSdeletedping capture for monitor 2 (hash=-8421734735842618831, trigger=visual_change)ping capture for monitor 1 (hash=7055607398056673531, trigger=click)ping capture for monitor 2 (hash=1577438924825351478, trigger=click)ping capture for monitor 2 (hash=1577438924825351478, trigger=click)ping capture for monitor 1 (hash=6795179045261365991, trigger=click)ping capture for monitor 2 (hash=6795179045261365991, trigger=click)pingcapture for monitor 2 (hash=6795179045261365991, trigger=click)ping capture for monitor 1 (hash=4434561267597224148, trigger=click)found 76 eligible frames26 frames, 9.1MB → 1.2MB (7.9x), 26 JPEGs deleted48 frames,9.5MB → 3.9MB (2.4x),48 JPEGs deleteddb.statement="\n\nSELECT\nid, \nsnapshot_path, \ndevice_name, \ntimestamp_name, intimestamp ASC\nLIMIT\n5000\n'rows_affected=0 rows_returned=68 elapsedfound 68eligible frames28 frames,10.OMB → 2.0MB (5.0x), 28 JPEGs deleted38 frames,6.5MB → 1.8MB (3.6x),38 JPEGs deletedsping capture for monitor 2 (hash=-844337372351140400, trigger=visual_change)...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35440
|
|
35441
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
PhpStormFileEditFV faVsco.js vViewNavigateCodeLaravelRefactorToolsWindowHelp#11894 on JY-18909-automated-reports-ask-iminny kProject v>W testsO connect.lessV connect.vuedashboardDeallnsightserrorPagesD export-portalextension-installledinvitationvonconterenceWa VoUT• LiveCoachD LockedO login• MeetingConsentO mobileOJ onboardD ondemand• playbackD playlistssettingssharedD SoftphoneCoach> D Sygicons> D TeamInsightscomposablesdirectives> C helpers› D locales› plugins> router> Mstore> MutilsIs main.jsTs types.d.tsIs types.js> _ vite=.browserslistrc© ReportController.phpC TokenBuilder.php©TeamSetupController.phpxphp api.php© SendReportJob.phpC AutomatedReportsCommand.phpAskJiminnykeporscontroller.ono© AutomatedReportsCommandTest.php© AutomatedReportsSendCommand.php• AutomatedReportsService.phpC CreateActivityLoggedEvent.php© Team.php© AutomatedReportsRepository.php© TrackProviderInstalledEvent.phpActivityLoagea.onpAutomatedRenortscallbackService.ono© RequestGenerateAskJiminnyReportJob.php© RequestGenerateReportJob.php© AutomatedReportResult.php(C AutomatedReport.phpclass TeamSetupController extends ControllerA4 ×2 ^public function integrationAppConnect(): JsonResponse/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);/** @var ?SocialAccount $socialAccount */$socialAccount = $user->getSocialAccount($crmProviderKey);if ($socialAccount === null) {$this->logger->error('[IntegrationApp] Unexpected error.Social account is missing.','team_id' => $team->getId(),'iapR-provider' => $realProviderKey,provider' => $crmProviderKey,]):return response()->isondilsuccess' → tauser"message' => 'Something went wrong. Social account is cannot be found.',->setStatusCode( code: JsonResponse: :HTTP_FAILED_DEPENDENCY);209$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);$socialAccount->save);$this->logger→>info('[IntegrationApp] Social account is connected.', [teal1o s sreali-voeclo.'iepR-provider' = $realProviderKey,'provider'=> $crmProviderKey,Services+, o, c|v M DatabaseLEU<console S 124 m$v A jiminny@localhost4 SFA HS_localY A PRODA console 10 sY d SlAGING4 console 2 s 670 ms- Docker… Outouinn1 # 16953 rowsv7763361767296136153 776419156T=о9TDD1ID refresh_token_expires T! provider Y<null> googlesn Z001=onone<null> integration-app= custom.logA console [PROD]= laravel.logA SF [jiminny@localhost]L console [STAGING]<SCrIOTmethods:async integrationAppOnClick() {7):V connect.vue xA HS_local [jiminny@localhost]const connection = await integrationAppintegration(this.localProvider.name).openNewConnection({showPoweredBy: false,aLLowulrloleconnectons. tause.F):console.1og('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));if (connection && connection.disconnected === false) {try {const saveRequest = await axios.post("/api/v1/integration-app-connect"if (saveRequest.data && saveRequest.data.success === true) {/** If all is good refresh the page here */window. Location = "/dasnboard";returnrthrow new Error(saveRequest.data.message);} catch (error) {console. Log(error);showSnackbarError(normalizeError(error));16%1681691731/4</script>‹style module Lang="less" src="./connect.less"></style>QGEAOO state YconnectedTuu-rerresmconnectedI auth_scope Topenid https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/…ononerredordonun user.reaoraoiain0 retry_after T<null><null><nULE> 0lablf Support Daily • in 2h 8 mAL AutomatedReportsCommandTestv100% C4Thu 16 Apr 12:52:30Al console [EU]CascadeIntegration App Conneseelesponse1le ooleocolocuziodlooeoci4a lallle : LonoK1AVб0s19044U109 Key, crealedAt":"2024-10-03T10:06:09.911Z" "updatedAt":"2025-07-0s.00.51-15.101L ISbeactivalea ralse, locouni nutos. staulc.Inteeratlon.ape connecrors zonocrm/logo.png""connectorld":"64a158e7d2605720d232e07b","connectorVersion. 440.oAutncalloackonl.nuos..aot.inteeratlon.apg/oautn-calloack, nasmissinerarameterstalse,nasbocumentaton" .talse, nasoeerations"true"operationscount".ob%,"nasData" true"datacollectionscount :40, nasevents"Talse, "eventscount":, nasclobalweonooks"Talse,nasVam" true, "authlvoe": "oautnzwThis time the response does not contain a connection key — meaning thiscall returns the integration metadata only, no existing connection. CompareWith what you saw earlier that nao "connection": "connected": truer.The best place to add logging is right where the Promise resolves in thefrontend - so you can see exactly what connection object you get backaner openvewconnect.ion.Road connect vua "l 128-167connect.vueNow go to the /connect page, click the connect button, complete or closeTne moca, ano check tne orowser console lorintegrationAdo ovenvewconnection resotved.unis will snow vou.• The exact shape of the returned object (whether it has disconnected, connected, state, etc.)• Whether it resolves as null / undefined if the user closes the modalWhat the value is when ©Auth completes successfullvThe key question is whether connection.disconnected === false is actuallythe richt check — or it the sok returns sometning like connection.connected== true (matching what you saw in the network response earlier).Ask anything (&4L)+ @ CodeClaude Sonnet 4.6esyv• created_at Y2026-04-16 09:43:012024-10-22 15:13:36updated_at Y2026-04-16 09:43:012024-10-22 15:13:36Iprovider_user_token_encrypted YeyJpdiI6ImZpWLds0U5GYitVVGlLcE11YWLm0Xc9PSI:ShULl7070-04-10 0749.907070-04-10 07.49:90eyspallolkLxzvzMnFJ10wckebccnzbPnxduwvEyPslAdd rowSUM: 0 3:8 W Windsurf Teams164:26uir-of 2 spaces...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35441
|
|
35444
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
+SlackFileEditViewGoEDHomeDMsActivityFilesLater..•More+→Jiminny ...# engineering# frontend# general# infra-changes# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...Direct messagesG. Vasil Vasilev% Galya DimitrovaNikolay Ivanov®. Aneliya AngelovaAneliya Angelova, ...Stoyan TanevVesSteliyan GeorgievAdelina Petrova, Ili...R. Adelina Petrova::: AppsToastJira CloudHistoryWindowHelpSearch Jiminny Inc& platform-inner-...& 10MessagesChannel OverviewMore v+нещо от данніToday ~ обре да се видиStefka Stoyanova 12:06 PMНа Прод еявно нещо S3 пьтя му e null, може би затоване винаги се получават репортитеNikolay Nikolov 12:07 PMS3 path го взимаме от респонса от prophetStefka Stoyanova 12:08 PMпрофета е виновен :)Nikolay Nikolov 12:08 PMне, мисля че е от теста сега на Stagingтова малко не му вярвам че е на продтрябва да видим статус generated c nulls3path дали няма по базатаStefka Stoyanova 12:09 PMот 13 април го има на Прод@1Steliyan Georgiev 12:17 PMще погледна как беше в Профет, че съмзабравилSteliyan Georgiev 12:23 PM"S3 path го взимаме от респонса от prophet"@Nikolay Nikolov Ники, от кой респонс? Наколбека?Message & platform-inner-team+AaSupport Daily • in 2h 8 m100% <478Thu 16 Apr 12:52:36-zsh• 85-zsh86-zsh₴7* Unable to acce...O 88found 70 eligible24 frames, 8.4MB→ 0.9MB (9.2x), 24 JPEGs deleted44 frames, 9.6MB → 4.7MB (2.0x),44 JPEGs deletedping capture for monitor 2 (hash=-7442755362469703643, trigger=click)ping capture for monitor 1 (hash=-7442755362469703643, trigger=click)db.statement="\n\nSELECT\nsnapshot_path, \ndevice_name, \n_name, \ntimestamp ASC\nLIMIT\nrows_affected=0 rows_returned=47 elapsedfound 47 eligible frames22 frames, 7.8MB → 1.6MB (4.9x),22 JPEGs deleted23 frames,6.3MB+ 1.1MB(5.5x), 23 JPEGs deletedfound 36eligible frames17 frames, 6.OMB → 0.6MB (9.8x), 17 JPEGSdeleted17 frames, 4.5MB→ 0.7MB (6.9x), 17 JPEGs deletedping capture for monitor 2 (hash=-3186288122146735337, trigger=visual_change)ping capture for monitor 1 (hash=-2194076432514965278, trigger=click)sping capture for monitor 2 (hash=-8421734735842618831, trigger=visual_change)ping capture for monitor 2 (hash=6115512118410074134, trigger=click)ping capture for monitor 2 (hash=6795179045261365991, trigger=click)pingcapture for monitor 2 (hash=6795179045261365991, trigger=click)."db.statement="\n\nSELECT\nid, Insnapshot_path, \ndevice_name, intimestamp•_name, \n timestamp ASC\nLIMIT\n5000\n" rows_affected-0 rows_returned-62 elapsedfound 62 eligible frames32 frames, 11.2MB → 1.7MB (6.6x), 32 JPEGs deleted28 frames, 7.6MB → 1.4MB (5.6x),28 JPEGSdeletedping capture for monitor 2 (hash=-8421734735842618831, trigger=visual_change)ping capture for monitor 1 (hash=7055607398056673531, trigger=click)ping capture for monitor 2 (hash=1577438924825351478, trigger=click)ping capture for monitor 2 (hash=1577438924825351478, trigger=click)ping capture for monitor 1 (hash=6795179045261365991, trigger=click)ping capture for monitor 2 (hash=6795179045261365991, trigger=click)pingcapture for monitor 2 (hash=6795179045261365991, trigger=click)ping capture for monitor 1 (hash=4434561267597224148, trigger=click)found 76 eligible frames26 frames, 9.1MB → 1.2MB (7.9x), 26 JPEGs deleted48 frames,9.5MB → 3.9MB (2.4x),48 JPEGs deleteddb.statement="\n\nSELECT\nid, \nsnapshot_path, \ndevice_name, \ntimestamp_name, intimestamp ASC\nLIMIT\n5000\n'rows_affected=0 rows_returned=68 elapsedfound 68eligible frames28 frames,10.OMB → 2.0MB (5.0x), 28 JPEGs deleted38 frames,6.5MB → 1.8MB (3.6x),38 JPEGs deletedsping capture for monitor 2 (hash=-844337372351140400, trigger=visual_change)...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35444
|
|
35445
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
PhpStormFileEditFV faVsco.js vViewNavigateCodeLaravelRefactorToolsWindowHelp#11894 on JY-18909-automated-reports-ask-iminny kProject v>W testsO connect.lessV connect.vuedashboardDeallnsightserrorPagesD export-portalextension-installledinvitationonconterenceWa VoUT• LiveCoachD LockedO login• MeetingConsentO mobileOJ onboardD ondemand• playbackD playlistssettingssharedD SoftphoneCoach> D Sygicons> D TeamInsightscomposablesdirectives> C helpers› D locales› plugins> router> Mstore> MutilsIs main.jsTs types.d.tsIs types.jsO vite=.browserslistrcServicesv M DatabaseLEU<console S 124 m$v A jiminny@localhostd SFA HS_localY A PRODA console 10 sY d SlAGING4 console 2 s 670 ms- Docker© ReportController.phpC TokenBuilder.php©TeamSetupController.phpxphp api.php© SendReportJob.phpC AutomatedReportsCommand.phpAskJiminnykeporscontroller.ono© AutomatedReportsCommandTest.php© AutomatedReportsSendCommand.php• AutomatedReportsService.phpC CreateActivityLoggedEvent.php© Team.php© AutomatedReportsRepository.php© TrackProviderInstalledEvent.phpActivityLoagea.onpAutomatedRenortscallbackService.ono© RequestGenerateAskJiminnyReportJob.php© RequestGenerateReportJob.php© AutomatedReportResult.php(C AutomatedReport.phpclass TeamSetupController extends ControllerA4 ×2 ^public function integrationAppConnect(): JsonResponse/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);/** @var ?SocialAccount $socialAccount */$socialAccount = $user->getSocialAccount($crmProviderKey);if ($socialAccount === null) {$this->logger->error('[IntegrationApp] Unexpected error.Social account is missing.','team_id' => $team->getId(),'iapR-provider' => $realProviderKey,provider' => $crmProviderKey,]):return response()->json([success → Tause.'message' => 'Something went wrong. Social account is cannot be found.',->setStatusCode( code:JsonResponse::HTTP_FAILED_DEPENDENCY);209$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);$socialAccount->save);$this->logger→>info('[IntegrationApp] Social account is connected.', [teal1o s sreali-voeclo.'iepR-provider' = $realProviderKey,'provider'=> $crmProviderKey,… Outouinn1 # 16953rowsves v1.7763361767296136153 .776419150DD1ID refresh_token_expires T! provider Y<hôu> googlesn Z001=ononeintegration-app= custom.logA console [PROD]= laravel.logA SF [jiminny@localhost]L console [STAGING]<SCrIOTmethods:async integrationAppOnClick() {7):V connect.vue xA HS_local [jiminny@localhost]const connection = await integrationAppintegration(this.localProvider.name).openNewConnection({showPoweredBy: false,aLLowulrloleconnectons. tause.F):console.1og('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));if (connection && connection.disconnected === false) {try {const saveRequest = await axios.post("/api/v1/integration-app-connect"if (saveRequest.data && saveRequest.data.success === true) {/** If all is good refresh the page here */window.location = "/dashboard";returnrthrow new Error(saveRequest.data.message);} catch (error) {console. Log(error);showSnackbarError(normalizeError(error));> 0lablf Support Daily • in 2h 8 mA100% C4Thu 16 Apr 12:52:37L AutomatedReportsCommandTestvAl console [EU]CascadeIntegration App Conneseelesponse1le ooleocolocuziodlooeoci4a lallle : LonoK1AVб0s19044U109 Key, crealedat":"2024-10-03T10:06:09.911Z","updatedAt":"2025-07-0s.00.51-15.101L ISbeactivalea ralse, locouni nutos. staulc.Inteeratlon.ape connecrors zonocrm/logo.png""connectorld":"64a158e7d2605720d232e07b","connectorVersion. 440.oAutncalloackonl.nuos..aot.inteeratlon.apg/oautn-calloack, nasmissinerarameterstalse,nasbocumentaton" .talse, nasoeerations"true"operationscount".ob%,"nasData" true"datacollectionscount :40, nasevents"Talse, "eventscount":, nasclobalweonooks"Talse,nasVam" true, "authlvoe": "oautnzwThis time the response does not contain a connection key — meaning thiscall returns the integration metadata only, no existing connection. CompareWith what you saw earlier that nao "connection": "connected": truer.The best place to add logging is right where the Promise resolves in thefrontend - so you can see exactly what connection object you get backaner ovenvewconnect.ion.Road connect vua "l 128-167connect.vueNow go to the /connect page, click the connect button, complete or closeTne moca, ano check tne orowser console lorintegrationAdo ovenvewconnection resotved.unis will snow vou.• The exact shape of the returned object (whether it has disconnected, connected, state, etc.)• Whether it resolves as null / undefined if the user closes the modalWhat the value is when ©Auth completes successfullvThe key question is whether connection.disconnected === false is actuallythe richt check — or it the sok returns sometning like connection.connected== true (matching what you saw in the network response earlier).16%1681691731/4</script>‹style module Lang="less" src="./connect.less"></style>Ask anything (&4L)+ @ CodeClaude Sonnet 4.6QGEAOstate YconnectedTULL-reTreshconnectedI auth_scope Topenid https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/…ononerredordonun user.reaoraoiain0 retry_after T<null><null>• created_at Yupdated_at Y2026-04-16 09:43:012026-04-16 09:43:012024-10-22 15:13:362024-10-22 15:13:362026-04-16 09:45:502026-04-16 07:45:50IO provider_user_token_encrypted TeyJpdiI6ImZpWLds0U5GYitVVGlLcE11YWLm0Xc9PSI:ShULleyJpdiT6IkZxZVZMNFJiUWtReGttNzBPNXdUWVE9PSI!• details: Unexpected token '??=. (4 minutes ago)Winasun leams164:26uir-of 2 spaces...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35445
|
|
35450
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
Previous Highlighted Error
Next Highlighted Error
<template>
<WelcomeLayout
title="Account disconnected"
textPosition="center"
:icon="faUnlink"
:class="$style.layout"
>
<div :class="$style.container" v-if="providersLoaded">
<p>
<strong>
It looks like your {{ localProvider.displayName }} account has become
disconnected
</strong>
</p>
<p :class="$style.small">Please re-connect to continue</p>
<p v-if="isInIframe">
We'll open the {{ localProvider.displayName }} authentication in a new
tab. Please return here and refresh the page once complete
</p>
<GoogleLikeButton
v-if="localProvider.viaIntegrationApp && crmTokenLoaded"
as="a"
:key="localProvider.name"
:brand-logo="localProvider.name"
:class="$style.connectButton"
@click="integrationAppOnClick"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
<GoogleLikeButton
v-if="!localProvider.viaIntegrationApp"
as="a"
:key="localProvider.name"
:href="`/auth/redirect/${localProvider.name}`"
:target="target"
:brand-logo="localProvider.name"
:class="$style.connectButton"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
</div>
<BuildInfo />
<KioskBanner />
</WelcomeLayout>
</template>
<script>
import window from "window";
import axios from "axios";
import { faUnlink } from "@fortawesome/pro-regular-svg-icons";
import isInIframe from "@/utils/isInIframe";
import BuildInfo from "@/components/layout/BuildInfo/BuildInfo.vue";
import KioskBanner from "@/components/shared/KioskBanner/KioskBanner.vue";
import WelcomeLayout from "@/components/layout/WelcomeLayout/WelcomeLayout.vue";
import GoogleLikeButton from "@/components/shared/Buttons/GoogleLikeButton.vue";
import { showSnackbarError, normalizeError } from "@/utils/index";
import { IntegrationAppClient } from "@integration-app/sdk";
export default {
name: "ConnectPage",
components: {
BuildInfo,
KioskBanner,
WelcomeLayout,
GoogleLikeButton,
},
data() {
return {
...window.connectData,
crmToken: null,
faUnlink,
isInIframe,
providers: [],
providersLoaded: false,
crmTokenLoaded: false,
};
},
computed: {
localProvider() {
return this.providers.find((e) => e.name === this.provider);
},
target() {
return this.isInIframe ? "_blank" : null;
},
},
created() {
this.getProviders();
},
mounted() {
this.showErrors();
},
watch: {
providersLoaded() {
if (this.providersLoaded) {
this.prepareIntegrationAppConnection();
}
},
},
methods: {
showErrors() {
if (!this.error) return;
showSnackbarError(this.error, undefined, undefined, false);
},
unwrapEntityResponse({ data }) {
return data.map(({ icon, name, displayName, viaIntegrationApp }) => {
return { icon, name, displayName, viaIntegrationApp };
});
},
async getProviders() {
try {
const response = await axios.get("/api/v1/connect-providers");
this.providers = this.unwrapEntityResponse(response);
this.providersLoaded = true;
} catch {
showSnackbarError(
"An error occurred, while loading form data (connect providers).",
);
}
},
async prepareIntegrationAppConnection() {
if (this.localProvider.viaIntegrationApp) {
try {
const response = await axios.get("/api/v1/integration-app-token");
this.crmToken = response.data.token;
this.crmTokenLoaded = true;
} catch (error) {
console.log(error);
showSnackbarError(
`An error occurred while preparing the page.
Try refreshing, if the error persists get in touch with the Jiminny team.`,
);
}
}
},
async integrationAppOnClick() {
const integrationApp = new IntegrationAppClient({
token: this.crmToken,
});
const connection = await integrationApp
.integration(this.localProvider.name)
.openNewConnection({
showPoweredBy: false,
allowMultipleConnections: false,
});
console.log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));
if (connection && connection.disconnected === false) {
try {
const saveRequest = await axios.post(
"/api/v1/integration-app-connect",
);
if (saveRequest.data && saveRequest.data.success === true) {
/** If all is good refresh the page here */
window.location = "/dashboard";
return;
}
throw new Error(saveRequest.data.message);
} catch (error) {
console.log(error);
showSnackbarError(normalizeError(error));
}
}
},
},
};
</script>
<style module lang="less" src="./connect.less"></style>
Project
Project
New File or Directory…
Expand Selected...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35450
|
|
35452
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
Previous Highlighted Error
Next Highlighted Error
<template>
<WelcomeLayout
title="Account disconnected"
textPosition="center"
:icon="faUnlink"
:class="$style.layout"
>
<div :class="$style.container" v-if="providersLoaded">
<p>
<strong>
It looks like your {{ localProvider.displayName }} account has become
disconnected
</strong>
</p>
<p :class="$style.small">Please re-connect to continue</p>
<p v-if="isInIframe">
We'll open the {{ localProvider.displayName }} authentication in a new
tab. Please return here and refresh the page once complete
</p>
<GoogleLikeButton
v-if="localProvider.viaIntegrationApp && crmTokenLoaded"
as="a"
:key="localProvider.name"
:brand-logo="localProvider.name"
:class="$style.connectButton"
@click="integrationAppOnClick"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
<GoogleLikeButton
v-if="!localProvider.viaIntegrationApp"
as="a"
:key="localProvider.name"
:href="`/auth/redirect/${localProvider.name}`"
:target="target"
:brand-logo="localProvider.name"
:class="$style.connectButton"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
</div>
<BuildInfo />
<KioskBanner />
</WelcomeLayout>
</template>
<script>
import window from "window";
import axios from "axios";
import { faUnlink } from "@fortawesome/pro-regular-svg-icons";
import isInIframe from "@/utils/isInIframe";
import BuildInfo from "@/components/layout/BuildInfo/BuildInfo.vue";
import KioskBanner from "@/components/shared/KioskBanner/KioskBanner.vue";
import WelcomeLayout from "@/components/layout/WelcomeLayout/WelcomeLayout.vue";
import GoogleLikeButton from "@/components/shared/Buttons/GoogleLikeButton.vue";
import { showSnackbarError, normalizeError } from "@/utils/index";
import { IntegrationAppClient } from "@integration-app/sdk";
export default {
name: "ConnectPage",
components: {
BuildInfo,
KioskBanner,
WelcomeLayout,
GoogleLikeButton,
},
data() {
return {
...window.connectData,
crmToken: null,
faUnlink,
isInIframe,
providers: [],
providersLoaded: false,
crmTokenLoaded: false,
};
},
computed: {
localProvider() {
return this.providers.find((e) => e.name === this.provider);
},
target() {
return this.isInIframe ? "_blank" : null;
},
},
created() {
this.getProviders();
},
mounted() {
this.showErrors();
},
watch: {
providersLoaded() {
if (this.providersLoaded) {
this.prepareIntegrationAppConnection();
}
},
},
methods: {
showErrors() {
if (!this.error) return;
showSnackbarError(this.error, undefined, undefined, false);
},
unwrapEntityResponse({ data }) {
return data.map(({ icon, name, displayName, viaIntegrationApp }) => {
return { icon, name, displayName, viaIntegrationApp };
});
},
async getProviders() {
try {
const response = await axios.get("/api/v1/connect-providers");
this.providers = this.unwrapEntityResponse(response);
this.providersLoaded = true;
} catch {
showSnackbarError(
"An error occurred, while loading form data (connect providers).",
);
}
},
async prepareIntegrationAppConnection() {
if (this.localProvider.viaIntegrationApp) {
try {
const response = await axios.get("/api/v1/integration-app-token");
this.crmToken = response.data.token;
this.crmTokenLoaded = true;
} catch (error) {
console.log(error);
showSnackbarError(
`An error occurred while preparing the page.
Try refreshing, if the error persists get in touch with the Jiminny team.`,
);
}
}
},
async integrationAppOnClick() {
const integrationApp = new IntegrationAppClient({
token: this.crmToken,
});
const connection = await integrationApp
.integration(this.localProvider.name)
.openNewConnection({
showPoweredBy: false,
allowMultipleConnections: false,
});
console.log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));
if (connection && connection.disconnected === false) {
try {
const saveRequest = await axios.post(
"/api/v1/integration-app-connect",
);
if (saveRequest.data && saveRequest.data.success === true) {
/** If all is good refresh the page here */
window.location = "/dashboard";
return;
}
throw new Error(saveRequest.data.message);
} catch (error) {
console.log(error);
showSnackbarError(normalizeError(error));
}
}
},
},
};
</script>
<style module lang="less" src="./connect.less"></style>
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35452
|
|
35456
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
Previous Highlighted Error
Next Highlighted Error
<template>
<WelcomeLayout
title="Account disconnected"
textPosition="center"
:icon="faUnlink"
:class="$style.layout"
>
<div :class="$style.container" v-if="providersLoaded">
<p>
<strong>
It looks like your {{ localProvider.displayName }} account has become
disconnected
</strong>
</p>
<p :class="$style.small">Please re-connect to continue</p>
<p v-if="isInIframe">
We'll open the {{ localProvider.displayName }} authentication in a new
tab. Please return here and refresh the page once complete
</p>
<GoogleLikeButton
v-if="localProvider.viaIntegrationApp && crmTokenLoaded"
as="a"
:key="localProvider.name"
:brand-logo="localProvider.name"
:class="$style.connectButton"
@click="integrationAppOnClick"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
<GoogleLikeButton
v-if="!localProvider.viaIntegrationApp"
as="a"
:key="localProvider.name"
:href="`/auth/redirect/${localProvider.name}`"
:target="target"
:brand-logo="localProvider.name"
:class="$style.connectButton"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
</div>
<BuildInfo />
<KioskBanner />
</WelcomeLayout>
</template>
<script>
import window from "window";
import axios from "axios";
import { faUnlink } from "@fortawesome/pro-regular-svg-icons";
import isInIframe from "@/utils/isInIframe";
import BuildInfo from "@/components/layout/BuildInfo/BuildInfo.vue";
import KioskBanner from "@/components/shared/KioskBanner/KioskBanner.vue";
import WelcomeLayout from "@/components/layout/WelcomeLayout/WelcomeLayout.vue";
import GoogleLikeButton from "@/components/shared/Buttons/GoogleLikeButton.vue";
import { showSnackbarError, normalizeError } from "@/utils/index";
import { IntegrationAppClient } from "@integration-app/sdk";
export default {
name: "ConnectPage",
components: {
BuildInfo,
KioskBanner,
WelcomeLayout,
GoogleLikeButton,
},
data() {
return {
...window.connectData,
crmToken: null,
faUnlink,
isInIframe,
providers: [],
providersLoaded: false,
crmTokenLoaded: false,
};
},
computed: {
localProvider() {
return this.providers.find((e) => e.name === this.provider);
},
target() {
return this.isInIframe ? "_blank" : null;
},
},
created() {
this.getProviders();
},
mounted() {
this.showErrors();
},
watch: {
providersLoaded() {
if (this.providersLoaded) {
this.prepareIntegrationAppConnection();
}
},
},
methods: {
showErrors() {
if (!this.error) return;
showSnackbarError(this.error, undefined, undefined, false);
},
unwrapEntityResponse({ data }) {
return data.map(({ icon, name, displayName, viaIntegrationApp }) => {
return { icon, name, displayName, viaIntegrationApp };
});
},
async getProviders() {
try {
const response = await axios.get("/api/v1/connect-providers");
this.providers = this.unwrapEntityResponse(response);
this.providersLoaded = true;
} catch {
showSnackbarError(
"An error occurred, while loading form data (connect providers).",
);
}
},
async prepareIntegrationAppConnection() {
if (this.localProvider.viaIntegrationApp) {
try {
const response = await axios.get("/api/v1/integration-app-token");
this.crmToken = response.data.token;
this.crmTokenLoaded = true;
} catch (error) {
console.log(error);
showSnackbarError(
`An error occurred while preparing the page.
Try refreshing, if the error persists get in touch with the Jiminny team.`,
);
}
}
},
async integrationAppOnClick() {
const integrationApp = new IntegrationAppClient({
token: this.crmToken,
});
const connection = await integrationApp
.integration(this.localProvider.name)
.openNewConnection({
showPoweredBy: false,
allowMultipleConnections: false,
});
console.log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));
if (connection && connection.disconnected === false) {
try {
const saveRequest = await axios.post(
"/api/v1/integration-app-connect",
);
if (saveRequest.data && saveRequest.data.success === true) {
/** If all is good refresh the page here */
window.location = "/dashboard";
return;
}
throw new Error(saveRequest.data.message);
} catch (error) {
console.log(error);
showSnackbarError(normalizeError(error));
}
}
},
},
};
</script>
<style module lang="less" src="./connect.less"></style>
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35456
|
|
35460
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
PhpStormFileEditFV faVsco.js vViewNavigateCodeLaravelRefactorToolsWindowHelp#11894 on JY-18909-automated-reports-ask-iminny kProject v>W testsO connect.lessV connect.vuedashboardDeallnsightserrorPagesD export-portalextension-installledinvitationonconterenceWa VoUT• LiveCoachD LockedO login• MeetingConsentO mobileOJ onboardD ondemand• playbackD playlistssettingssharedD SoftphoneCoach> D Sygicons> D TeamInsightscomposablesdirectives> C helpers› D locales> O plugins> router> Mstore> MutilsIs main.jsTs types.d.tsIs types.js> _ vite=.browserslistrcServicesv M DatabaseLEU<console S 124 m$v A jiminny@localhost4,SFA HS_localY A PRODA console 10 sY d SlAGING4 console 2 s 670 ms- Docker© ReportController.phpC TokenBuilder.php©TeamSetupController.phpxphp api.php© SendReportJob.phpC AutomatedReportsCommand.phpAskJiminnykeporscontroller.ono© AutomatedReportsCommandTest.php© AutomatedReportsSendCommand.php• AutomatedReportsService.phpC CreateActivityLoggedEvent.php© Team.php© AutomatedReportsRepository.php© TrackProviderInstalledEvent.phpActivityLoagea.onpAutomatedRenortscallbackService.ono© RequestGenerateAskJiminnyReportJob.phpRequestGenerateReportJob.php© AutomatedReportResult.php(C AutomatedReport.phpclass TeamSetupController extends ControllerA4 ×2 ^public function integrationAppConnect(): JsonResponse/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);/** @var ?SocialAccount $socialAccount */$socialAccount = $user->getSocialAccount($crmProviderKey);if ($socialAccount === null) {$this->logger->error('[IntegrationApp] Unexpected error.Social account is missing.','team_id' => $team->getId(),'iapR-provider' => $realProviderKey,provider' => $crmProviderKey,]):return response()->isondilsuccess → Tause."message' => 'Something went wrong. Social account is cannot be found.',->setStatusCode( code: JsonResponse: :HTTP_FAILED_DEPENDENCY);209$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);$socialAccount->save);211$this->logger→>info('[IntegrationApp] Social account is connected.', [teal1o s sreali-voeclo.'iepR-provider' = $realProviderKey,'provider'=> $crmProviderKey,… Outouinn1 # 16952 rowsves v776336176729613615ID refresh_token_expires Tx: Auto vDD1Oprovider T<null> googlesn Z001=onone= custom.logA console [PROD]= laravel.logA SF [jiminny@localhost]L console [STAGING]<SCrIOTmethods:async integrationAppOnClick() {7):V connect.vue xA HS_local [jiminny@localhost]14%150const connection = await integrationAppintegration(this.localProvider.name).openNewConnection({showPoweredBy: false,aLLowulrloleconnectons. tause.F):console.1og('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));if (connection && connection.disconnected === false) {try {const saveRequest = await axios.post("/api/v1/integration-app-connect"if (saveRequest.data && saveRequest.data.success === true) {/** If all is good refresh the page here */window. Location = "/dasnboard";returnrthrow new Error(saveRequest.data.message);} catch (error) {console. Log(error);showSnackbarError(normalizeError(error));16%16%1691731/4</script>‹style module Lang="less" src="./connect.less"></style>QGEAOstate Yconnectedfull-refreshI auth_scope Topenid https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/…ononerredordonun user.reaoraoiain0 retry_after T<null><null>Support Daily • in 2h 7 mAL AutomatedReportsCommandTestv100% C4Thu 16 Apr 12:53:51Al console [EU]CascadeIntegration App Conneseelesponse1le ooleocolocuziodlooeoci4a lallle : LonoM1 .Y8d9f9d420f89*,"key":, crealedat":"2024-10-03T10:06:09.911Z" "updatedAt":"2025-07-0s.00.51-15.101L ISbeactivalea ralse, locouni nutos. staulc.Inteeratlon.ape connecrors zonocrm/logo.png""connectorld":"64a158e7d2605720d232e07b","connectorVersion. 440.oAutncalloackonl.nuos..aot.inteeratlon.apg/oautn-calloack, nasmissinerarameterstalse,nasbocumentaton" .talse, nasoeerations"true"operationscount".ob%,"nasData" true"datacollectionscount :40, nasevents"Talse, "eventscount":, nasclobalweonooks"Talse,nasVam" true, "authlvoe": "oautnzwThis time the response does not contain a connection key — meaning thiscall returns the integration metadata only, no existing connection. CompareWith what you saw earlier that nao "connection": "connected": truer.The best place to add logging is right where the Promise resolves in thefrontend - so you can see exactly what connection object you get backaner openvewconnect.ion.Road connect vua "l 128-167connect.vueNow go to the /connect page, click the connect button, complete or closeTne moca, ano check tne orowser console lorintegrationAdo ovenvewconnection resotved.unis will snow vou.• The exact shape of the returned object (whether it has disconnected, connected, state, etc.)• Whether it resolves as null / undefined if the user closes the modalWhat the value is when ©Auth completes successfullvThe key question is whether connection.disconnected === false is actuallythe richt check — or it the sok returns sometning like connection.connected== true (matching what you saw in the network response earlier).Ask anything (&4L)+ @ CodeClaude Sonnet 4.66: -IO created_at T : Iupdated_at T2026-04-16 09:43:012026-04-16 09:43:012024-10-22 15:13:36 2024-10-22 15:13:36IO provider_user_token_encrypted TeyJpdiI6ImZpWLds0U5GYitVVGlLcE11YWLm0Xc9PSI:ShULl2 rows retrieved starting from 1 in 609 ms (execution: 27 ms, fetching: 582 ms)Winasun leams164:26uir-of 2 spaces...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35460
|
|
35529
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
Previous Highlighted Error
Next Highlighted Error
<template>
<WelcomeLayout
title="Account disconnected"
textPosition="center"
:icon="faUnlink"
:class="$style.layout"
>
<div :class="$style.container" v-if="providersLoaded">
<p>
<strong>
It looks like your {{ localProvider.displayName }} account has become
disconnected
</strong>
</p>
<p :class="$style.small">Please re-connect to continue</p>
<p v-if="isInIframe">
We'll open the {{ localProvider.displayName }} authentication in a new
tab. Please return here and refresh the page once complete
</p>
<GoogleLikeButton
v-if="localProvider.viaIntegrationApp && crmTokenLoaded"
as="a"
:key="localProvider.name"
:brand-logo="localProvider.name"
:class="$style.connectButton"
@click="integrationAppOnClick"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
<GoogleLikeButton
v-if="!localProvider.viaIntegrationApp"
as="a"
:key="localProvider.name"
:href="`/auth/redirect/${localProvider.name}`"
:target="target"
:brand-logo="localProvider.name"
:class="$style.connectButton"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
</div>
<BuildInfo />
<KioskBanner />
</WelcomeLayout>
</template>
<script>
import window from "window";
import axios from "axios";
import { faUnlink } from "@fortawesome/pro-regular-svg-icons";
import isInIframe from "@/utils/isInIframe";
import BuildInfo from "@/components/layout/BuildInfo/BuildInfo.vue";
import KioskBanner from "@/components/shared/KioskBanner/KioskBanner.vue";
import WelcomeLayout from "@/components/layout/WelcomeLayout/WelcomeLayout.vue";
import GoogleLikeButton from "@/components/shared/Buttons/GoogleLikeButton.vue";
import { showSnackbarError, normalizeError } from "@/utils/index";
import { IntegrationAppClient } from "@integration-app/sdk";
export default {
name: "ConnectPage",
components: {
BuildInfo,
KioskBanner,
WelcomeLayout,
GoogleLikeButton,
},
data() {
return {
...window.connectData,
crmToken: null,
faUnlink,
isInIframe,
providers: [],
providersLoaded: false,
crmTokenLoaded: false,
};
},
computed: {
localProvider() {
return this.providers.find((e) => e.name === this.provider);
},
target() {
return this.isInIframe ? "_blank" : null;
},
},
created() {
this.getProviders();
},
mounted() {
this.showErrors();
},
watch: {
providersLoaded() {
if (this.providersLoaded) {
this.prepareIntegrationAppConnection();
}
},
},
methods: {
showErrors() {
if (!this.error) return;
showSnackbarError(this.error, undefined, undefined, false);
},
unwrapEntityResponse({ data }) {
return data.map(({ icon, name, displayName, viaIntegrationApp }) => {
return { icon, name, displayName, viaIntegrationApp };
});
},
async getProviders() {
try {
const response = await axios.get("/api/v1/connect-providers");
this.providers = this.unwrapEntityResponse(response);
this.providersLoaded = true;
} catch {
showSnackbarError(
"An error occurred, while loading form data (connect providers).",
);
}
},
async prepareIntegrationAppConnection() {
if (this.localProvider.viaIntegrationApp) {
try {
const response = await axios.get("/api/v1/integration-app-token");
this.crmToken = response.data.token;
this.crmTokenLoaded = true;
} catch (error) {
console.log(error);
showSnackbarError(
`An error occurred while preparing the page.
Try refreshing, if the error persists get in touch with the Jiminny team.`,
);
}
}
},
async integrationAppOnClick() {
const integrationApp = new IntegrationAppClient({
token: this.crmToken,
});
const connection = await integrationApp
.integration(this.localProvider.name)
.openNewConnection({
showPoweredBy: false,
allowMultipleConnections: false,
});
console.log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));
if (connection && connection.disconnected === false) {
try {
const saveRequest = await axios.post(
"/api/v1/integration-app-connect",
);
if (saveRequest.data && saveRequest.data.success === true) {
/** If all is good refresh the page here */
window.location = "/dashboard";
return;
}
throw new Error(saveRequest.data.message);
} catch (error) {
console.log(error);
showSnackbarError(normalizeError(error));
}
}
},
},
};
</script>
<style module lang="less" src="./connect.less"></style>
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35529
|
|
35530
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
Previous Highlighted Error
Next Highlighted Error
<template>
<WelcomeLayout
title="Account disconnected"
textPosition="center"
:icon="faUnlink"
:class="$style.layout"
>
<div :class="$style.container" v-if="providersLoaded">
<p>
<strong>
It looks like your {{ localProvider.displayName }} account has become
disconnected
</strong>
</p>
<p :class="$style.small">Please re-connect to continue</p>
<p v-if="isInIframe">
We'll open the {{ localProvider.displayName }} authentication in a new
tab. Please return here and refresh the page once complete
</p>
<GoogleLikeButton
v-if="localProvider.viaIntegrationApp && crmTokenLoaded"
as="a"
:key="localProvider.name"
:brand-logo="localProvider.name"
:class="$style.connectButton"
@click="integrationAppOnClick"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
<GoogleLikeButton
v-if="!localProvider.viaIntegrationApp"
as="a"
:key="localProvider.name"
:href="`/auth/redirect/${localProvider.name}`"
:target="target"
:brand-logo="localProvider.name"
:class="$style.connectButton"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
</div>
<BuildInfo />
<KioskBanner />
</WelcomeLayout>
</template>
<script>
import window from "window";
import axios from "axios";
import { faUnlink } from "@fortawesome/pro-regular-svg-icons";
import isInIframe from "@/utils/isInIframe";
import BuildInfo from "@/components/layout/BuildInfo/BuildInfo.vue";
import KioskBanner from "@/components/shared/KioskBanner/KioskBanner.vue";
import WelcomeLayout from "@/components/layout/WelcomeLayout/WelcomeLayout.vue";
import GoogleLikeButton from "@/components/shared/Buttons/GoogleLikeButton.vue";
import { showSnackbarError, normalizeError } from "@/utils/index";
import { IntegrationAppClient } from "@integration-app/sdk";
export default {
name: "ConnectPage",
components: {
BuildInfo,
KioskBanner,
WelcomeLayout,
GoogleLikeButton,
},
data() {
return {
...window.connectData,
crmToken: null,
faUnlink,
isInIframe,
providers: [],
providersLoaded: false,
crmTokenLoaded: false,
};
},
computed: {
localProvider() {
return this.providers.find((e) => e.name === this.provider);
},
target() {
return this.isInIframe ? "_blank" : null;
},
},
created() {
this.getProviders();
},
mounted() {
this.showErrors();
},
watch: {
providersLoaded() {
if (this.providersLoaded) {
this.prepareIntegrationAppConnection();
}
},
},
methods: {
showErrors() {
if (!this.error) return;
showSnackbarError(this.error, undefined, undefined, false);
},
unwrapEntityResponse({ data }) {
return data.map(({ icon, name, displayName, viaIntegrationApp }) => {
return { icon, name, displayName, viaIntegrationApp };
});
},
async getProviders() {
try {
const response = await axios.get("/api/v1/connect-providers");
this.providers = this.unwrapEntityResponse(response);
this.providersLoaded = true;
} catch {
showSnackbarError(
"An error occurred, while loading form data (connect providers).",
);
}
},
async prepareIntegrationAppConnection() {
if (this.localProvider.viaIntegrationApp) {
try {
const response = await axios.get("/api/v1/integration-app-token");
this.crmToken = response.data.token;
this.crmTokenLoaded = true;
} catch (error) {
console.log(error);
showSnackbarError(
`An error occurred while preparing the page.
Try refreshing, if the error persists get in touch with the Jiminny team.`,
);
}
}
},
async integrationAppOnClick() {
const integrationApp = new IntegrationAppClient({
token: this.crmToken,
});
const connection = await integrationApp
.integration(this.localProvider.name)
.openNewConnection({
showPoweredBy: false,
allowMultipleConnections: false,
});
console.log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));
if (connection && connection.disconnected === false) {
try {
const saveRequest = await axios.post(
"/api/v1/integration-app-connect",
);
if (saveRequest.data && saveRequest.data.success === true) {
/** If all is good refresh the page here */
window.location = "/dashboard";
return;
}
throw new Error(saveRequest.data.message);
} catch (error) {
console.log(error);
showSnackbarError(normalizeError(error));
}
}
},
},
};
</script>
<style module lang="less" src="./connect.less"></style>
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35530
|
|
35590
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
Previous Highlighted Error
Next Highlighted Error
<template>
<WelcomeLayout
title="Account disconnected"
textPosition="center"
:icon="faUnlink"
:class="$style.layout"
>
<div :class="$style.container" v-if="providersLoaded">
<p>
<strong>
It looks like your {{ localProvider.displayName }} account has become
disconnected
</strong>
</p>
<p :class="$style.small">Please re-connect to continue</p>
<p v-if="isInIframe">
We'll open the {{ localProvider.displayName }} authentication in a new
tab. Please return here and refresh the page once complete
</p>
<GoogleLikeButton
v-if="localProvider.viaIntegrationApp && crmTokenLoaded"
as="a"
:key="localProvider.name"
:brand-logo="localProvider.name"
:class="$style.connectButton"
@click="integrationAppOnClick"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
<GoogleLikeButton
v-if="!localProvider.viaIntegrationApp"
as="a"
:key="localProvider.name"
:href="`/auth/redirect/${localProvider.name}`"
:target="target"
:brand-logo="localProvider.name"
:class="$style.connectButton"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
</div>
<BuildInfo />
<KioskBanner />
</WelcomeLayout>
</template>
<script>
import window from "window";
import axios from "axios";
import { faUnlink } from "@fortawesome/pro-regular-svg-icons";
import isInIframe from "@/utils/isInIframe";
import BuildInfo from "@/components/layout/BuildInfo/BuildInfo.vue";
import KioskBanner from "@/components/shared/KioskBanner/KioskBanner.vue";
import WelcomeLayout from "@/components/layout/WelcomeLayout/WelcomeLayout.vue";
import GoogleLikeButton from "@/components/shared/Buttons/GoogleLikeButton.vue";
import { showSnackbarError, normalizeError } from "@/utils/index";
import { IntegrationAppClient } from "@integration-app/sdk";
export default {
name: "ConnectPage",
components: {
BuildInfo,
KioskBanner,
WelcomeLayout,
GoogleLikeButton,
},
data() {
return {
...window.connectData,
crmToken: null,
faUnlink,
isInIframe,
providers: [],
providersLoaded: false,
crmTokenLoaded: false,
};
},
computed: {
localProvider() {
return this.providers.find((e) => e.name === this.provider);
},
target() {
return this.isInIframe ? "_blank" : null;
},
},
created() {
this.getProviders();
},
mounted() {
this.showErrors();
},
watch: {
providersLoaded() {
if (this.providersLoaded) {
this.prepareIntegrationAppConnection();
}
},
},
methods: {
showErrors() {
if (!this.error) return;
showSnackbarError(this.error, undefined, undefined, false);
},
unwrapEntityResponse({ data }) {
return data.map(({ icon, name, displayName, viaIntegrationApp }) => {
return { icon, name, displayName, viaIntegrationApp };
});
},
async getProviders() {
try {
const response = await axios.get("/api/v1/connect-providers");
this.providers = this.unwrapEntityResponse(response);
this.providersLoaded = true;
} catch {
showSnackbarError(
"An error occurred, while loading form data (connect providers).",
);
}
},
async prepareIntegrationAppConnection() {
if (this.localProvider.viaIntegrationApp) {
try {
const response = await axios.get("/api/v1/integration-app-token");
this.crmToken = response.data.token;
this.crmTokenLoaded = true;
} catch (error) {
console.log(error);
showSnackbarError(
`An error occurred while preparing the page.
Try refreshing, if the error persists get in touch with the Jiminny team.`,
);
}
}
},
async integrationAppOnClick() {
const integrationApp = new IntegrationAppClient({
token: this.crmToken,
});
const connection = await integrationApp
.integration(this.localProvider.name)
.openNewConnection({
showPoweredBy: false,
allowMultipleConnections: false,
});
console.log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));
if (connection && connection.disconnected === false) {
try {
const saveRequest = await axios.post(
"/api/v1/integration-app-connect",
);
if (saveRequest.data && saveRequest.data.success === true) {
/** If all is good refresh the page here */
window.location = "/dashboard";
return;
}
throw new Error(saveRequest.data.message);
} catch (error) {
console.log(error);
showSnackbarError(normalizeError(error));
}
}
},
},
};
</script>
<style module lang="less" src="./connect.less"></style>
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35590
|
|
35591
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
Previous Highlighted Error
Next Highlighted Error
<template>
<WelcomeLayout
title="Account disconnected"
textPosition="center"
:icon="faUnlink"
:class="$style.layout"
>
<div :class="$style.container" v-if="providersLoaded">
<p>
<strong>
It looks like your {{ localProvider.displayName }} account has become
disconnected
</strong>
</p>
<p :class="$style.small">Please re-connect to continue</p>
<p v-if="isInIframe">
We'll open the {{ localProvider.displayName }} authentication in a new
tab. Please return here and refresh the page once complete
</p>
<GoogleLikeButton
v-if="localProvider.viaIntegrationApp && crmTokenLoaded"
as="a"
:key="localProvider.name"
:brand-logo="localProvider.name"
:class="$style.connectButton"
@click="integrationAppOnClick"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
<GoogleLikeButton
v-if="!localProvider.viaIntegrationApp"
as="a"
:key="localProvider.name"
:href="`/auth/redirect/${localProvider.name}`"
:target="target"
:brand-logo="localProvider.name"
:class="$style.connectButton"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
</div>
<BuildInfo />
<KioskBanner />
</WelcomeLayout>
</template>
<script>
import window from "window";
import axios from "axios";
import { faUnlink } from "@fortawesome/pro-regular-svg-icons";
import isInIframe from "@/utils/isInIframe";
import BuildInfo from "@/components/layout/BuildInfo/BuildInfo.vue";
import KioskBanner from "@/components/shared/KioskBanner/KioskBanner.vue";
import WelcomeLayout from "@/components/layout/WelcomeLayout/WelcomeLayout.vue";
import GoogleLikeButton from "@/components/shared/Buttons/GoogleLikeButton.vue";
import { showSnackbarError, normalizeError } from "@/utils/index";
import { IntegrationAppClient } from "@integration-app/sdk";
export default {
name: "ConnectPage",
components: {
BuildInfo,
KioskBanner,
WelcomeLayout,
GoogleLikeButton,
},
data() {
return {
...window.connectData,
crmToken: null,
faUnlink,
isInIframe,
providers: [],
providersLoaded: false,
crmTokenLoaded: false,
};
},
computed: {
localProvider() {
return this.providers.find((e) => e.name === this.provider);
},
target() {
return this.isInIframe ? "_blank" : null;
},
},
created() {
this.getProviders();
},
mounted() {
this.showErrors();
},
watch: {
providersLoaded() {
if (this.providersLoaded) {
this.prepareIntegrationAppConnection();
}
},
},
methods: {
showErrors() {
if (!this.error) return;
showSnackbarError(this.error, undefined, undefined, false);
},
unwrapEntityResponse({ data }) {
return data.map(({ icon, name, displayName, viaIntegrationApp }) => {
return { icon, name, displayName, viaIntegrationApp };
});
},
async getProviders() {
try {
const response = await axios.get("/api/v1/connect-providers");
this.providers = this.unwrapEntityResponse(response);
this.providersLoaded = true;
} catch {
showSnackbarError(
"An error occurred, while loading form data (connect providers).",
);
}
},
async prepareIntegrationAppConnection() {
if (this.localProvider.viaIntegrationApp) {
try {
const response = await axios.get("/api/v1/integration-app-token");
this.crmToken = response.data.token;
this.crmTokenLoaded = true;
} catch (error) {
console.log(error);
showSnackbarError(
`An error occurred while preparing the page.
Try refreshing, if the error persists get in touch with the Jiminny team.`,
);
}
}
},
async integrationAppOnClick() {
const integrationApp = new IntegrationAppClient({
token: this.crmToken,
});
const connection = await integrationApp
.integration(this.localProvider.name)
.openNewConnection({
showPoweredBy: false,
allowMultipleConnections: false,
});
console.log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));
if (connection && connection.disconnected === false) {
try {
const saveRequest = await axios.post(
"/api/v1/integration-app-connect",
);
if (saveRequest.data && saveRequest.data.success === true) {
/** If all is good refresh the page here */
window.location = "/dashboard";
return;
}
throw new Error(saveRequest.data.message);
} catch (error) {
console.log(error);
showSnackbarError(normalizeError(error));
}
}
},
},
};
</script>
<style module lang="less" src="./connect.less"></style>
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35591
|
|
35594
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
Previous Highlighted Error
Next Highlighted Error
<template>
<WelcomeLayout
title="Account disconnected"
textPosition="center"
:icon="faUnlink"
:class="$style.layout"
>
<div :class="$style.container" v-if="providersLoaded">
<p>
<strong>
It looks like your {{ localProvider.displayName }} account has become
disconnected
</strong>
</p>
<p :class="$style.small">Please re-connect to continue</p>
<p v-if="isInIframe">
We'll open the {{ localProvider.displayName }} authentication in a new
tab. Please return here and refresh the page once complete
</p>
<GoogleLikeButton
v-if="localProvider.viaIntegrationApp && crmTokenLoaded"
as="a"
:key="localProvider.name"
:brand-logo="localProvider.name"
:class="$style.connectButton"
@click="integrationAppOnClick"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
<GoogleLikeButton
v-if="!localProvider.viaIntegrationApp"
as="a"
:key="localProvider.name"
:href="`/auth/redirect/${localProvider.name}`"
:target="target"
:brand-logo="localProvider.name"
:class="$style.connectButton"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
</div>
<BuildInfo />
<KioskBanner />
</WelcomeLayout>
</template>
<script>
import window from "window";
import axios from "axios";
import { faUnlink } from "@fortawesome/pro-regular-svg-icons";
import isInIframe from "@/utils/isInIframe";
import BuildInfo from "@/components/layout/BuildInfo/BuildInfo.vue";
import KioskBanner from "@/components/shared/KioskBanner/KioskBanner.vue";
import WelcomeLayout from "@/components/layout/WelcomeLayout/WelcomeLayout.vue";
import GoogleLikeButton from "@/components/shared/Buttons/GoogleLikeButton.vue";
import { showSnackbarError, normalizeError } from "@/utils/index";
import { IntegrationAppClient } from "@integration-app/sdk";
export default {
name: "ConnectPage",
components: {
BuildInfo,
KioskBanner,
WelcomeLayout,
GoogleLikeButton,
},
data() {
return {
...window.connectData,
crmToken: null,
faUnlink,
isInIframe,
providers: [],
providersLoaded: false,
crmTokenLoaded: false,
};
},
computed: {
localProvider() {
return this.providers.find((e) => e.name === this.provider);
},
target() {
return this.isInIframe ? "_blank" : null;
},
},
created() {
this.getProviders();
},
mounted() {
this.showErrors();
},
watch: {
providersLoaded() {
if (this.providersLoaded) {
this.prepareIntegrationAppConnection();
}
},
},
methods: {
showErrors() {
if (!this.error) return;
showSnackbarError(this.error, undefined, undefined, false);
},
unwrapEntityResponse({ data }) {
return data.map(({ icon, name, displayName, viaIntegrationApp }) => {
return { icon, name, displayName, viaIntegrationApp };
});
},
async getProviders() {
try {
const response = await axios.get("/api/v1/connect-providers");
this.providers = this.unwrapEntityResponse(response);
this.providersLoaded = true;
} catch {
showSnackbarError(
"An error occurred, while loading form data (connect providers).",
);
}
},
async prepareIntegrationAppConnection() {
if (this.localProvider.viaIntegrationApp) {
try {
const response = await axios.get("/api/v1/integration-app-token");
this.crmToken = response.data.token;
this.crmTokenLoaded = true;
} catch (error) {
console.log(error);
showSnackbarError(
`An error occurred while preparing the page.
Try refreshing, if the error persists get in touch with the Jiminny team.`,
);
}
}
},
async integrationAppOnClick() {
const integrationApp = new IntegrationAppClient({
token: this.crmToken,
});
const connection = await integrationApp
.integration(this.localProvider.name)
.openNewConnection({
showPoweredBy: false,
allowMultipleConnections: false,
});
console.log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));
if (connection && connection.disconnected === false) {
try {
const saveRequest = await axios.post(
"/api/v1/integration-app-connect",
);
if (saveRequest.data && saveRequest.data.success === true) {
/** If all is good refresh the page here */
window.location = "/dashboard";
return;
}
throw new Error(saveRequest.data.message);
} catch (error) {
console.log(error);
showSnackbarError(normalizeError(error));
}
}
},
},
};
</script>
<style module lang="less" src="./connect.less"></style>
Project
Project
New File or Directory…
Expand Selected...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35594
|
|
35597
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
Previous Highlighted Error
Next Highlighted Error
<template>
<WelcomeLayout
title="Account disconnected"
textPosition="center"
:icon="faUnlink"
:class="$style.layout"
>
<div :class="$style.container" v-if="providersLoaded">
<p>
<strong>
It looks like your {{ localProvider.displayName }} account has become
disconnected
</strong>
</p>
<p :class="$style.small">Please re-connect to continue</p>
<p v-if="isInIframe">
We'll open the {{ localProvider.displayName }} authentication in a new
tab. Please return here and refresh the page once complete
</p>
<GoogleLikeButton
v-if="localProvider.viaIntegrationApp && crmTokenLoaded"
as="a"
:key="localProvider.name"
:brand-logo="localProvider.name"
:class="$style.connectButton"
@click="integrationAppOnClick"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
<GoogleLikeButton
v-if="!localProvider.viaIntegrationApp"
as="a"
:key="localProvider.name"
:href="`/auth/redirect/${localProvider.name}`"
:target="target"
:brand-logo="localProvider.name"
:class="$style.connectButton"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
</div>
<BuildInfo />
<KioskBanner />
</WelcomeLayout>
</template>
<script>
import window from "window";
import axios from "axios";
import { faUnlink } from "@fortawesome/pro-regular-svg-icons";
import isInIframe from "@/utils/isInIframe";
import BuildInfo from "@/components/layout/BuildInfo/BuildInfo.vue";
import KioskBanner from "@/components/shared/KioskBanner/KioskBanner.vue";
import WelcomeLayout from "@/components/layout/WelcomeLayout/WelcomeLayout.vue";
import GoogleLikeButton from "@/components/shared/Buttons/GoogleLikeButton.vue";
import { showSnackbarError, normalizeError } from "@/utils/index";
import { IntegrationAppClient } from "@integration-app/sdk";
export default {
name: "ConnectPage",
components: {
BuildInfo,
KioskBanner,
WelcomeLayout,
GoogleLikeButton,
},
data() {
return {
...window.connectData,
crmToken: null,
faUnlink,
isInIframe,
providers: [],
providersLoaded: false,
crmTokenLoaded: false,
};
},
computed: {
localProvider() {
return this.providers.find((e) => e.name === this.provider);
},
target() {
return this.isInIframe ? "_blank" : null;
},
},
created() {
this.getProviders();
},
mounted() {
this.showErrors();
},
watch: {
providersLoaded() {
if (this.providersLoaded) {
this.prepareIntegrationAppConnection();
}
},
},
methods: {
showErrors() {
if (!this.error) return;
showSnackbarError(this.error, undefined, undefined, false);
},
unwrapEntityResponse({ data }) {
return data.map(({ icon, name, displayName, viaIntegrationApp }) => {
return { icon, name, displayName, viaIntegrationApp };
});
},
async getProviders() {
try {
const response = await axios.get("/api/v1/connect-providers");
this.providers = this.unwrapEntityResponse(response);
this.providersLoaded = true;
} catch {
showSnackbarError(
"An error occurred, while loading form data (connect providers).",
);
}
},
async prepareIntegrationAppConnection() {
if (this.localProvider.viaIntegrationApp) {
try {
const response = await axios.get("/api/v1/integration-app-token");
this.crmToken = response.data.token;
this.crmTokenLoaded = true;
} catch (error) {
console.log(error);
showSnackbarError(
`An error occurred while preparing the page.
Try refreshing, if the error persists get in touch with the Jiminny team.`,
);
}
}
},
async integrationAppOnClick() {
const integrationApp = new IntegrationAppClient({
token: this.crmToken,
});
const connection = await integrationApp
.integration(this.localProvider.name)
.openNewConnection({
showPoweredBy: false,
allowMultipleConnections: false,
});
console.log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));
if (connection && connection.disconnected === false) {
try {
const saveRequest = await axios.post(
"/api/v1/integration-app-connect",
);
if (saveRequest.data && saveRequest.data.success === true) {
/** If all is good refresh the page here */
window.location = "/dashboard";
return;
}
throw new Error(saveRequest.data.message);
} catch (error) {
console.log(error);
showSnackbarError(normalizeError(error));
}
}
},
},
};
</script>
<style module lang="less" src="./connect.less"></style>
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35597
|
|
35598
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
Previous Highlighted Error
Next Highlighted Error
<template>
<WelcomeLayout
title="Account disconnected"
textPosition="center"
:icon="faUnlink"
:class="$style.layout"
>
<div :class="$style.container" v-if="providersLoaded">
<p>
<strong>
It looks like your {{ localProvider.displayName }} account has become
disconnected
</strong>
</p>
<p :class="$style.small">Please re-connect to continue</p>
<p v-if="isInIframe">
We'll open the {{ localProvider.displayName }} authentication in a new
tab. Please return here and refresh the page once complete
</p>
<GoogleLikeButton
v-if="localProvider.viaIntegrationApp && crmTokenLoaded"
as="a"
:key="localProvider.name"
:brand-logo="localProvider.name"
:class="$style.connectButton"
@click="integrationAppOnClick"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
<GoogleLikeButton
v-if="!localProvider.viaIntegrationApp"
as="a"
:key="localProvider.name"
:href="`/auth/redirect/${localProvider.name}`"
:target="target"
:brand-logo="localProvider.name"
:class="$style.connectButton"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
</div>
<BuildInfo />
<KioskBanner />
</WelcomeLayout>
</template>
<script>
import window from "window";
import axios from "axios";
import { faUnlink } from "@fortawesome/pro-regular-svg-icons";
import isInIframe from "@/utils/isInIframe";
import BuildInfo from "@/components/layout/BuildInfo/BuildInfo.vue";
import KioskBanner from "@/components/shared/KioskBanner/KioskBanner.vue";
import WelcomeLayout from "@/components/layout/WelcomeLayout/WelcomeLayout.vue";
import GoogleLikeButton from "@/components/shared/Buttons/GoogleLikeButton.vue";
import { showSnackbarError, normalizeError } from "@/utils/index";
import { IntegrationAppClient } from "@integration-app/sdk";
export default {
name: "ConnectPage",
components: {
BuildInfo,
KioskBanner,
WelcomeLayout,
GoogleLikeButton,
},
data() {
return {
...window.connectData,
crmToken: null,
faUnlink,
isInIframe,
providers: [],
providersLoaded: false,
crmTokenLoaded: false,
};
},
computed: {
localProvider() {
return this.providers.find((e) => e.name === this.provider);
},
target() {
return this.isInIframe ? "_blank" : null;
},
},
created() {
this.getProviders();
},
mounted() {
this.showErrors();
},
watch: {
providersLoaded() {
if (this.providersLoaded) {
this.prepareIntegrationAppConnection();
}
},
},
methods: {
showErrors() {
if (!this.error) return;
showSnackbarError(this.error, undefined, undefined, false);
},
unwrapEntityResponse({ data }) {
return data.map(({ icon, name, displayName, viaIntegrationApp }) => {
return { icon, name, displayName, viaIntegrationApp };
});
},
async getProviders() {
try {
const response = await axios.get("/api/v1/connect-providers");
this.providers = this.unwrapEntityResponse(response);
this.providersLoaded = true;
} catch {
showSnackbarError(
"An error occurred, while loading form data (connect providers).",
);
}
},
async prepareIntegrationAppConnection() {
if (this.localProvider.viaIntegrationApp) {
try {
const response = await axios.get("/api/v1/integration-app-token");
this.crmToken = response.data.token;
this.crmTokenLoaded = true;
} catch (error) {
console.log(error);
showSnackbarError(
`An error occurred while preparing the page.
Try refreshing, if the error persists get in touch with the Jiminny team.`,
);
}
}
},
async integrationAppOnClick() {
const integrationApp = new IntegrationAppClient({
token: this.crmToken,
});
const connection = await integrationApp
.integration(this.localProvider.name)
.openNewConnection({
showPoweredBy: false,
allowMultipleConnections: false,
});
console.log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));
if (connection && connection.disconnected === false) {
try {
const saveRequest = await axios.post(
"/api/v1/integration-app-connect",
);
if (saveRequest.data && saveRequest.data.success === true) {
/** If all is good refresh the page here */
window.location = "/dashboard";
return;
}
throw new Error(saveRequest.data.message);
} catch (error) {
console.log(error);
showSnackbarError(normalizeError(error));
}
}
},
},
};
</script>
<style module lang="less" src="./connect.less"></style>
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35598
|
|
35599
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
Previous Highlighted Error
Next Highlighted Error
<template>
<WelcomeLayout
title="Account disconnected"
textPosition="center"
:icon="faUnlink"
:class="$style.layout"
>
<div :class="$style.container" v-if="providersLoaded">
<p>
<strong>
It looks like your {{ localProvider.displayName }} account has become
disconnected
</strong>
</p>
<p :class="$style.small">Please re-connect to continue</p>
<p v-if="isInIframe">
We'll open the {{ localProvider.displayName }} authentication in a new
tab. Please return here and refresh the page once complete
</p>
<GoogleLikeButton
v-if="localProvider.viaIntegrationApp && crmTokenLoaded"
as="a"
:key="localProvider.name"
:brand-logo="localProvider.name"
:class="$style.connectButton"
@click="integrationAppOnClick"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
<GoogleLikeButton
v-if="!localProvider.viaIntegrationApp"
as="a"
:key="localProvider.name"
:href="`/auth/redirect/${localProvider.name}`"
:target="target"
:brand-logo="localProvider.name"
:class="$style.connectButton"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
</div>
<BuildInfo />
<KioskBanner />
</WelcomeLayout>
</template>
<script>
import window from "window";
import axios from "axios";
import { faUnlink } from "@fortawesome/pro-regular-svg-icons";
import isInIframe from "@/utils/isInIframe";
import BuildInfo from "@/components/layout/BuildInfo/BuildInfo.vue";
import KioskBanner from "@/components/shared/KioskBanner/KioskBanner.vue";
import WelcomeLayout from "@/components/layout/WelcomeLayout/WelcomeLayout.vue";
import GoogleLikeButton from "@/components/shared/Buttons/GoogleLikeButton.vue";
import { showSnackbarError, normalizeError } from "@/utils/index";
import { IntegrationAppClient } from "@integration-app/sdk";
export default {
name: "ConnectPage",
components: {
BuildInfo,
KioskBanner,
WelcomeLayout,
GoogleLikeButton,
},
data() {
return {
...window.connectData,
crmToken: null,
faUnlink,
isInIframe,
providers: [],
providersLoaded: false,
crmTokenLoaded: false,
};
},
computed: {
localProvider() {
return this.providers.find((e) => e.name === this.provider);
},
target() {
return this.isInIframe ? "_blank" : null;
},
},
created() {
this.getProviders();
},
mounted() {
this.showErrors();
},
watch: {
providersLoaded() {
if (this.providersLoaded) {
this.prepareIntegrationAppConnection();
}
},
},
methods: {
showErrors() {
if (!this.error) return;
showSnackbarError(this.error, undefined, undefined, false);
},
unwrapEntityResponse({ data }) {
return data.map(({ icon, name, displayName, viaIntegrationApp }) => {
return { icon, name, displayName, viaIntegrationApp };
});
},
async getProviders() {
try {
const response = await axios.get("/api/v1/connect-providers");
this.providers = this.unwrapEntityResponse(response);
this.providersLoaded = true;
} catch {
showSnackbarError(
"An error occurred, while loading form data (connect providers).",
);
}
},
async prepareIntegrationAppConnection() {
if (this.localProvider.viaIntegrationApp) {
try {
const response = await axios.get("/api/v1/integration-app-token");
this.crmToken = response.data.token;
this.crmTokenLoaded = true;
} catch (error) {
console.log(error);
showSnackbarError(
`An error occurred while preparing the page.
Try refreshing, if the error persists get in touch with the Jiminny team.`,
);
}
}
},
async integrationAppOnClick() {
const integrationApp = new IntegrationAppClient({
token: this.crmToken,
});
const connection = await integrationApp
.integration(this.localProvider.name)
.openNewConnection({
showPoweredBy: false,
allowMultipleConnections: false,
});
if (connection && connection.connected === true) {
try {
const saveRequest = await axios.post(
"/api/v1/integration-app-connect",
);
if (saveRequest.data && saveRequest.data.success === true) {
/** If all is good refresh the page here */
window.location = "/dashboard";
return;
}
throw new Error(saveRequest.data.message);
} catch (error) {
console.log(error);
showSnackbarError(normalizeError(error));
}
}
},
},
};
</script>
<style module lang="less" src="./connect.less"></style>
Project
Project
New File or Directory…
Expand Selected
Collapse All...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35599
|
|
35603
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Code changed:
Hide...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35603
|
|
35606
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35606
|
|
35637
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
Previous Highlighted Error
Next Highlighted Error
<template>
<WelcomeLayout
title="Account disconnected"
textPosition="center"
:icon="faUnlink"
:class="$style.layout"
>
<div :class="$style.container" v-if="providersLoaded">
<p>
<strong>
It looks like your {{ localProvider.displayName }} account has become
disconnected
</strong>
</p>
<p :class="$style.small">Please re-connect to continue</p>
<p v-if="isInIframe">
We'll open the {{ localProvider.displayName }} authentication in a new
tab. Please return here and refresh the page once complete
</p>
<GoogleLikeButton
v-if="localProvider.viaIntegrationApp && crmTokenLoaded"
as="a"
:key="localProvider.name"
:brand-logo="localProvider.name"
:class="$style.connectButton"
@click="integrationAppOnClick"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
<GoogleLikeButton
v-if="!localProvider.viaIntegrationApp"
as="a"
:key="localProvider.name"
:href="`/auth/redirect/${localProvider.name}`"
:target="target"
:brand-logo="localProvider.name"
:class="$style.connectButton"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
</div>
<BuildInfo />
<KioskBanner />
</WelcomeLayout>
</template>
<script>
import window from "window";
import axios from "axios";
import { faUnlink } from "@fortawesome/pro-regular-svg-icons";
import isInIframe from "@/utils/isInIframe";
import BuildInfo from "@/components/layout/BuildInfo/BuildInfo.vue";
import KioskBanner from "@/components/shared/KioskBanner/KioskBanner.vue";
import WelcomeLayout from "@/components/layout/WelcomeLayout/WelcomeLayout.vue";
import GoogleLikeButton from "@/components/shared/Buttons/GoogleLikeButton.vue";
import { showSnackbarError, normalizeError } from "@/utils/index";
import { IntegrationAppClient } from "@integration-app/sdk";
export default {
name: "ConnectPage",
components: {
BuildInfo,
KioskBanner,
WelcomeLayout,
GoogleLikeButton,
},
data() {
return {
...window.connectData,
crmToken: null,
faUnlink,
isInIframe,
providers: [],
providersLoaded: false,
crmTokenLoaded: false,
};
},
computed: {
localProvider() {
return this.providers.find((e) => e.name === this.provider);
},
target() {
return this.isInIframe ? "_blank" : null;
},
},
created() {
this.getProviders();
},
mounted() {
this.showErrors();
},
watch: {
providersLoaded() {
if (this.providersLoaded) {
this.prepareIntegrationAppConnection();
}
},
},
methods: {
showErrors() {
if (!this.error) return;
showSnackbarError(this.error, undefined, undefined, false);
},
unwrapEntityResponse({ data }) {
return data.map(({ icon, name, displayName, viaIntegrationApp }) => {
return { icon, name, displayName, viaIntegrationApp };
});
},
async getProviders() {
try {
const response = await axios.get("/api/v1/connect-providers");
this.providers = this.unwrapEntityResponse(response);
this.providersLoaded = true;
} catch {
showSnackbarError(
"An error occurred, while loading form data (connect providers).",
);
}
},
async prepareIntegrationAppConnection() {
if (this.localProvider.viaIntegrationApp) {
try {
const response = await axios.get("/api/v1/integration-app-token");
this.crmToken = response.data.token;
this.crmTokenLoaded = true;
} catch (error) {
console.log(error);
showSnackbarError(
`An error occurred while preparing the page.
Try refreshing, if the error persists get in touch with the Jiminny team.`,
);
}
}
},
async integrationAppOnClick() {
const integrationApp = new IntegrationAppClient({
token: this.crmToken,
});
const connection = await integrationApp
.integration(this.localProvider.name)
.openNewConnection({
showPoweredBy: false,
allowMultipleConnections: false,
});
if (connection && connection.connected === true) {
try {
const saveRequest = await axios.post(
"/api/v1/integration-app-connect",
);
if (saveRequest.data && saveRequest.data.success === true) {
/** If all is good refresh the page here */
window.location = "/dashboard";
return;
}
throw new Error(saveRequest.data.message);
} catch (error) {
console.log(error);
showSnackbarError(normalizeError(error));
}
}
},
},
};
</script>
<style module lang="less" src="./connect.less"></style>
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35637
|
|
35644
|
PhpStormFileEditViewNavigateCodeLaravelRefactorToo PhpStormFileEditViewNavigateCodeLaravelRefactorToolsWindowProject v› testsO connect.lessV connect.vuedashboardDeallnsightserrorPagesD export-portalextension-installledinvitationonconterenceWa VoUT• LiveCoachD LockedO loginMeetingConsentO mobileConboard› _mocks_→ ItestsV MobileAppDownk0 Onboard.lessV Onboard vueTs useProvidersSyn‹D ondemandD playback• playlistsSettingssharedSoftphoneCoachSyglcons› _ leaminsights> M composables> M directives> M7 helpers> → locales> D pluginsrouterO storeServices+,o, c|v M DatabaseLEU<console S 124 m$v A jiminny@localhostL SFA HS_localY A PRODA console 10 sY d SlAGING4 console 2 s 670 ms- DockerHelp© ReportController.phpC TokenBuilder.php© TeamSetupController.php xphp api.php© SendReportJob.phpC AutomatedReportsCommand.phpAskJiminnykeporscontroller.ono© AutomatedReportsCommandTest.php© AutomatedReportsSendCommand.php• AutomatedReportsService.phpC CreateActivityLoggedEvent.php© Team.php© AutomatedReportsRepository.php© TrackProviderInstalledEvent.phpActivityLoagea.onp)AutomatedRenortscallbackservice.onv© RequestGenerateAskJiminnyReportJob.php© RequestGenerateReportJob.php© AutomatedReportResult.php(C AutomatedReport.phpclass TeamSetupController extends ControllerA4 X2^public function integrationAppConnect(): JsonResponse/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);/** @var ?SocialAccount $socialAccount */$socialAccount = $user->getSocialAccount($crmProviderKey);if ($socialAccount === null) {$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.','team_id' => $team->getId(),'iapR-provider' => $realProviderKey,provider' => $crmProviderKey,]):return response()->isondi203204205206207success → Tause."message' => 'Something went wrong. Social account is cannot be found.',->setStatusCode( code: JsonResponse: :HTTP_FAILED_DEPENDENCY):209$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED) ;$socialAccount->save0);$this->logger→>info('[IntegrationApp] Social account is connected.', [teal1or s srealievoerlo.'iepR-provider' = $realProviderKey,'provider'=> $crmProviderKey,… Outouilnnn # 1695>rowsvT=о0TID refresh_token_expires Tes v7763361762.729613615IX: AutoDD1Oprovider<null> google<n Z001=ononeQGEAOO state Yconnectedfull-refreshSupport Daily - in 1h 59mA100% C4Thu 16 Apr 13:01:14= custom.log4 console [EUl= laravel.logA SF [jiminny@localhost]A console [STAGING]V connect.vue XV Onboard.vueA HS_local [jiminny@localhost]15015416%168169170173console PRODI<SCrI0methods:async integrationApp0nClick() {consc conneccion = avalt inueeracionapoopenrewconnecronlnsnowrowereaby. Talse,allowMultipleConnections: false,F):CascadeIntegration App ConneWhether it resolves as null/ undefined if the user closes the modalWhat the value is when OAuth completes successfullyIne key •uestionis wherner connection, dnsconnected === talse sacrualvthe right check — or if the SDK returns something like connection.connected== true (matching what you saw in the network response earlier).lllaleelttlelatelsleinetconsole.1og('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connecti Acceptif (connection && connection.disconnected === false) {Regectif (connection && connection.connected F== true) €20607d1846231, tenantid. ADafd25ae490e8dO5d8ca2'"'isTest" false,"rationld"."66fe6c913202f3a165e3c14d","externalAppld":"66716516T09:46:11.197Z" "L16T09:57:59.175Z"."retryAttempts":0,"isDeactivated":false]try {const saveRequest = await axios.post("/api/v1/integration-app-connect"The object uses connected: true - there is no disconnected field at all. Sothe current check connection.disconnected == false evaluates to undefined == false → false - meaning the POST to /api/v1/integration-app-coif (saveRequest.data && saveRequest.data.success === true) €nnect S nevercallled anera success connection./** If all is good refresh the page here */This is the bug. Fix it in both connect.vue and Onboard.vue:Window. Locacion = "/dasnboard":return;• connect.vue+1-3throw new Error(saveRequest.data.message);} catch (error) {console. Log(error);showSnackbarError(normalizeError(error));Now fix the same bug in Onboard. vue:Read Onboard vue #L503-517D Onboard.vue+1 -1Both files fixed. Summary of the bug:The SDK returns connected: true — there is no disconnected fieldconnection.disconnected === false was undefined === false →alwavs talse → restnevertrecSame in Onboard.Vue. connection.dicconnected == true was undefine1 file with changesfront-end/src/components/connect/1 connect wue</script>Reject allAccept allAsk anything (&4L)‹style module Lang="less" src="-/connect.less"></style>+ @ codeClaude Sonnet 4.6I auth_scope Topenid https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/…pnone.read.aomin user.read.aom.n0 retry_after T<null><null>• created_at Yupdated_at Y2026-04-16 09:43:012026-04-16 09:43:012024-10-22 15:13:362024-10-22 15:13:36IO provider_user_token_encrypted TeyJpdiI6ImZpWLds0U5GYitVVGlLcE11YWLm0Xc9PSI:SIULLWinasun leamsuir-o( 2 spaces...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35644
|
|
35727
|
Computing annotation for connect.vue
Project: faVs Computing annotation for connect.vue
Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35727
|
|
35729
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
FirefoxFileEditViewHistoryBookmarksProfilesToolsWindowHelp> 0ld6]-zsh• 28526.60kB26.87kB27.91kB30.75kB34.35kB39.49kB39.69kB41.87kB43.21kB47.84kB48.24kB55.13kB61.28kB62.98kB63.06kB64.62kB79.57kB94.84kB115.66kB117.59kB120.68 kB128.67kB129.28kB164.28 kB176.44kB180.40kB197.96kB210.96kB218.14kB264.94kB298.53kB307.13kB343.99kB367.43kB689.63kB825.14kB1,402.47kBSupport Daily - in 1h 56 mAAPP (-zsh)₴4DOCKER• 881DEV (docker)82APP (-zsh)X3ec2-user@ip-10-30-….../public/vue-assets/assets/GridView-vMogKjqT.js./public/vue-assets/assets/ondemand-DgxEX09i.js../public/vue-assets/assets/CrmLink-rTdmxqkp.js./public/vue-assets/assets/liquor-tree-DbetBeVs.js./public/vue-assets/assets/DealRiskList-COSgQDWm.js../public/vue-assets/assets/AskAnything-BiYJNLXH.js:/public/vue-assets/assets/lib-BPR1zwwF.js./public/vue-assets/assets/AppFormField-0eTkGyuH.js:./public/vue-assets/assets/deal-view-B4d9Fnc0.js../public/vue-assets/assets/exports-DIyAIXcT.js../public/vue-assets/assets/playlists-CJz1PCg2.js../public/vue-assets/assets/callScoringTemplates-DQc-joSr.js../public/vue-assets/assets/_copy0bject-DzIIjTZN.js./public/vue-assets/assets/pusher-CYYPj3Hn.js./public/vue-assets/assets/onboard-BGmZPXYh.js../public/vue-assets/assets/StatusBadge-D_dxGN0U.js./public/vue-assets/assets/kiosk-hoNoVi3Z.js./public/vue-assets/assets/deal-insights-CCCD53q2.js../public/vue-assets/assets/ListView-Daurhtak.js../public/vue-assets/assets/_plugin-vue_export-helper-sSs0rPyg.js./public/vue-assets/assets/WelcomeLayout-CI_AuldJ.js:./public/vue-assets/assets/dashboard-B-uDq9Qs.js../public/vue-assets/assets/emoji-input-D_ee3_TC.js../public/vue-assets/assets/sentry-unCNBfeg.js../public/vue-assets/assets/OrgSettingsLayout-Br7DRJ0o.js../public/vue-assets/assets/vuex.esm-bundler-CxmCn-TU.js../public/vue-assets/assets/playback-CB909wM4.js./public/vue-assets/assets/AppButton-OYq5I1u7.js../public/vue-assets/assets/index.module-DoWLv01P.js../public/vue-assets/assets/intl-tel-input-C4VqCHzY.js../public/vue-assets/assets/team-insights-CPsGbra7.js../public/vue-assets/assets/popper-DC--DigQ.js../public/vue-assets/assets/PhoneField-DsfvGNK0.js./public/vue-assets/assets/live-DbuadCCc.js./public/vue-assets/assets/video-js-skin.less_vue_type_style_index_0_src_true_lang-D2hx_saf.js../public/vue-assets/assets/index-CV_ZMn85.js:./public/vue-assets/assets/logged-in-layout-qoUV-hWG.js-zshgzip:10.06kBgzip:9.39kBgz1p:10.18kBgzip:9.58kB9z1p:10.60kBgz1p:14.98kBgzip:12.70kB9z1p:12.68kBgzip:14.35kBgzip:16.46kBgzip:15.07kBgzip:13.28kBgz1p:20.08kBgzip:18.89kB9z1p:21.84kBgz1p:22.94kBgzip:22.63kB9z1p:28.18kBgzip:33.76kB9z1p:38.70 kB921p:34.16kBgzip:40.05kBgz1p:36.72kBgzip:52.24 kB9z1p:56.16kBgz1p:67.85kBgzip:61.61kB9z1p:68.66kBgz1p:64.16kB9z1p:60.30kBgzip:77.20 kBgzip:103.87kBgz1p:84.90kBgzip:97.04kBgzip: 202.81kBgz1p:72.44kBgzip: 438.06kB[plugin builtin:vite-reporter](!) Some chunks are larger than 500 kBafter minification. Consider:- Using dynamic import() to code-split the application- Use build.rolldownOptions.output.codeSplittingto improve chunking: https://rolldown.rs/reference/Output0ptions.codeSplitting- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.• built in 29.05slukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app/front-end (JY-18909-automated-reports-ask-jiminny) $I86-zshmaр:92.74kBmap:73.94kBmap:93.18kBтар :78.74kBтар:115.18kBmap:173.20kBтар :138.34kBтар:150.73 kBmap:150.62kBmaр:294.48kBтар:153.25kBmaр:65.85kBmap:239.59kBтар :219.27kBmар:201.33kBmap:244.72kBтар :300.68kBтар :292.79kBmap:308.10kBmaр:500.60kBтар:258.56kBmaр:410.48kBmap:266.15kBтар :831.82 kBтар:623.70kBmap:836.88kBтар :680.92kBmар :3,947.49 kBmap:1,108.20 kBmap:475.61kBтар:959.66kBmap: 1,245.28kBmap:849.05kBтар :792.41kBmар: 3,016.64 kBmap:436.28kBmaр: 6,282.82kB100% <47O 878Thu 16 Apr 13:04:40181* Unable to acce...O x8APP...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35729
|
|
35731
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35731
|
|
35734
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
FirefoxFileEditViewHistoryBookmarksProfilesToolsWindowHelp> 0ld6]-zsh• 28526.60kB26.87kB27.91kB30.75kB34.35kB39.49kB39.69kB41.87kB43.21kB47.84kB48.24kB55.13kB61.28kB62.98kB63.06kB64.62kB79.57kB94.84kB115.66kB117.59kB120.68 kB128.67kB129.28kB164.28 kB176.44kB180.40kB197.96kB210.96kB218.14kB264.94kB298.53kB307.13kB343.99kB367.43kB689.63kB825.14kB1,402.47kBSupport Daily - in 1h 56 mAAPP (-zsh)₴4DOCKER25981DEV (docker)82APP (-zsh)X3ec2-user@ip-10-30-….../public/vue-assets/assets/GridView-vMogKjqT.js./public/vue-assets/assets/ondemand-DgxEX09i.js../public/vue-assets/assets/CrmLink-rTdmxqkp.js./public/vue-assets/assets/liquor-tree-DbetBeVs.js./public/vue-assets/assets/DealRiskList-COSgQDWm.js../public/vue-assets/assets/AskAnything-BiYJNLXH.js:/public/vue-assets/assets/lib-BPR1zwwF.js./public/vue-assets/assets/AppFormField-0eTkGyuH.js:./public/vue-assets/assets/deal-view-B4d9Fnc0.js../public/vue-assets/assets/exports-DIyAIXcT.js../public/vue-assets/assets/playlists-CJz1PCg2.js../public/vue-assets/assets/callScoringTemplates-DQc-joSr.js../public/vue-assets/assets/_copy0bject-DzIIjTZN.js./public/vue-assets/assets/pusher-CYYPj3Hn.js./public/vue-assets/assets/onboard-BGmZPXYh.js../public/vue-assets/assets/StatusBadge-D_dxGN0U.js./public/vue-assets/assets/kiosk-hoNoVi3Z.js./public/vue-assets/assets/deal-insights-CCCD53q2.js../public/vue-assets/assets/ListView-Daurhtak.js../public/vue-assets/assets/_plugin-vue_export-helper-sSs0rPyg.js./public/vue-assets/assets/WelcomeLayout-CI_AuldJ.js:./public/vue-assets/assets/dashboard-B-uDq9Qs.js../public/vue-assets/assets/emoji-input-D_ee3_TC.js../public/vue-assets/assets/sentry-unCNBfeg.js../public/vue-assets/assets/OrgSettingsLayout-Br7DRJ0o.js../public/vue-assets/assets/vuex.esm-bundler-CxmCn-TU.js../public/vue-assets/assets/playback-CB909wM4.js./public/vue-assets/assets/AppButton-OYq5I1u7.js../public/vue-assets/assets/index.module-DoWLv01P.js../public/vue-assets/assets/intl-tel-input-C4VqCHzY.js../public/vue-assets/assets/team-insights-CPsGbra7.js../public/vue-assets/assets/popper-DC--DigQ.js../public/vue-assets/assets/PhoneField-DsfvGNK0.js./public/vue-assets/assets/live-DbuadCCc.js./public/vue-assets/assets/video-js-skin.less_vue_type_style_index_0_src_true_lang-D2hx_saf.js../public/vue-assets/assets/index-CV_ZMn85.js:./public/vue-assets/assets/logged-in-layout-qoUV-hWG.js-zshgzip:10.06kBgzip:9.39kBgz1p:10.18kBgzip:9.58kB9z1p:10.60kBgz1p:14.98kBgzip:12.70kB9z1p:12.68kBgzip:14.35kBgzip:16.46kBgzip:15.07kBgzip:13.28kBgz1p:20.08kBgzip:18.89kB9z1p:21.84kBgz1p:22.94kBgzip:22.63kB9z1p:28.18kBgzip:33.76kB9z1p:38.70 kB921p:34.16kBgzip:40.05kBgz1p:36.72kBgzip:52.24 kB9z1p:56.16kBgz1p:67.85kBgzip:61.61kB9z1p:68.66kBgz1p:64.16kB9z1p:60.30kBgzip:77.20 kBgzip:103.87kBgz1p:84.90kBgzip:97.04kBgzip: 202.81kBgz1p:72.44kBgzip: 438.06kB[plugin builtin:vite-reporter](!) Some chunks are larger than 500 kBafter minification. Consider:- Using dynamic import() to code-split the application- Use build.rolldownOptions.output.codeSplittingto improve chunking: https://rolldown.rs/reference/Output0ptions.codeSplitting- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.• built in 29.05slukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app/front-end (JY-18909-automated-reports-ask-jiminny) $I86-zshmaр:92.74kBmap:73.94kBmap:93.18kBтар :78.74kBтар:115.18kBmap:173.20kBтар :138.34kBтар:150.73 kBmap:150.62kBmaр:294.48kBтар:153.25kBmaр:65.85kBmap:239.59kBтар :219.27kBmар:201.33kBmap:244.72kBтар :300.68kBтар :292.79kBmap:308.10kBmaр:500.60kBтар:258.56kBmaр:410.48kBmap:266.15kBтар :831.82 kBтар:623.70kBmap:836.88kBтар :680.92kBmар :3,947.49 kBmap:1,108.20 kBmap:475.61kBтар:959.66kBmap: 1,245.28kBmap:849.05kBтар :792.41kBmар: 3,016.64 kBmap:436.28kBmaр: 6,282.82kB100% <47₴78Thu 16 Apr 13:04:46181* Unable to acce...O x8APP...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35734
|
|
35742
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35742
|
|
35743
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
PhpStormFileEditFV faVsco.js vProject vViewNavigateCodeLaravelRefactorToolsWindowHelp#11894 on JY-18909-automated-reports-ask-jiminny k>W testsO connect.lessV connect.vuedashboardDeallnsightserrorPagesexport-portalextension-installledinvitationvonconterenceWa VoUT• LiveCoachD LockedO login› C MeetingConsentO mobileConboard› _mocks_→ ItestsV MobileAppDownk0 Onboard.lessV Onboard vueTs useProvidersSyn‹D ondemandD playback• playlistsSettingssharedSoftphoneCoachSyglcons› _ leaminsights> M composables> M7 directives> M7 helpers> → locales> D pluginsrouterO storeServices+,o, c|v M DatabaseLEU<console S 124 m$v A jiminny@localhostL SFA HS_localY A PRODA console 10 sY d SlAGING4 console 2 s 670 ms→* Docken© ReportController.phpC TokenBuilder.php© TeamSetupController.php xphp api.php© SendReportJob.phpC AutomatedReportsCommand.phpAskJiminnykeporscontroller.ono© AutomatedReportsCommandTest.php© AutomatedReportsSendCommand.php© Team.php© AutomatedReportsRepository.php© AutomatedReportsService.php© TrackProviderInstalledEvent.phpC CreateActivityLoggedEvent.phpActivityLoagea.onp= custom.log4 console [EUl= laravel.logconsole PRODIA SF [jiminny@localhost]A console [STAGING]<SCrIOTmethods:async integrationAppOnClick() {const integrationApp = new IntegrationAppClient({token: this.crmToken,V connect.vue XV Onboard.vueA HS_local [jiminny@localhost]AutomatedRenortscallbackService.ono© RequestGenerateAskJiminnyReportJob.php© RequestGenerateReportJob.php© AutomatedReportResult.php(C AutomatedReport.phpclass TeamSetupController extends ControllerA4 ×2 ^public function integrationAppConnect(): JsonResponse/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);const connection = awalt integrationAppnteoramonuns, Locaurrovloer.nale)openNewConnectionsnowrowereaby. Talse,dLcownuccsoleconnecclons. Talse./** @var ?SocialAccount $socialAccount */F);$socialAccount = $user->getSocialAccount($crmProviderKey);if ($socialAccount === null) {$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.','team_id' => $team->getId(),'iapR-provider' => $realProviderKey,153provider' => $crmProviderKey,]):console.log(' [IntegrationApp] openNewConnection resolved:', JSON.stringify(connection) ;if (connection && connection.disconnected === false) {try 4const saveRequest = await axios.post("aouvianceorarion-aoo-connect"return response()->isondil157if (saveRequest.data && saveRequest.data.success === true) {success → Tause.203'message' => 'Something went wrong. Social account is cannot be found.',858/** If all is good refresh the page here */window. location = "/dashboard";204return;205->setStatusCode( code:JsonResponse::HTTP_FAILED_DEPENDENCY);206207209$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);$socialAccount->save);throw new Error(saveRequest.data.message);f catch (error) {console.log(error);showsnackoar.crorcnornauzze.cror.erroro.Sthis-›logger-›info('[IntegrationApp] Social account is connected.', [teal1o s sreali-voeclo.'iepR-provider' = $realProviderKey,'provider'=> $crmProviderKey,1691711172</script>… Outouilnnn # 1695rowsvid Y! sociable_id Y15021415DD1• provider_user_id Y1695 1001806069711583162871695 17ISLHPVSk2Lk3MK6K7BbgQGEA0!O provider_user_token Yya29.a0Aa7МYirt0MShIzyiTcZHvFQaJJXsV6R7_bFDR5beR601LSmTRkt_-ERT3N-1qХDRpYmKcbLPMd6v05DLy...eVZORIOLANUAMOISLIrSZVIOIKOINCYLnONOnLUNLISTILO CLlonKCMoIMoUO LInUCUTNUOYUS04.V1AVj Support Daily • in 1h 55 mAU AutomatedReportsCommandTestv100% 145Thu 16 Apr 13:05:16CascadeIntegration App ConneWhether it resolves as null/ undefined if the user closes the modalWhat the value is when OAuth completes successfullywIne key •uestionis wherner connection,dnsconnected === talse s acrualivthe right check — or if the SDK returns something like connection.connected== true (matching what you saw in the network response earlier).alaealttelatels eins"id"."69e0afe35890a00c8dd955b7" "name":"Zoho21607daf4623","tenantld":"69eOafd25ae490e8d05d8ca2" "isTest":false,")2f3a165e3c14d" "externalAppld"."66716516T09:46:11.197Z" "L16T09:57:59.175Z"."retryAttempts":0,"isDeactivated":false}The object uses connected: true - there is no disconnected field at all. Sothe current check connection.disconnected == false evaluates to undefined == false → false - meaning the POST to /api/v1/integration-app-connect S nevercallled anera success connection.This is the bug. Fix it in both connect.vue and Onboard.vue:• connect.vu+1-3Now fix the same bug in Onboard. vue:Read Onboard vue #L503-517D Onboard.vue+1 -1Both files fixed. Summary of the bug:The SDK returns connected: true — there is no disconnected fieldconnection.disconnected === false was undefined === false →alwavs talse → resnevertred• Same in Onboard. vue: connection.disconnected === true was undefined === true → always talse → always snowed the error snackoarNow after openNewConnection() resolves with connected: true, the POST /api/v1/integration-app-connect will be called and the SocialAccount will beAsk anything (&*L)+ @ CodeClaude Sonnet 4.6ID provider_refresh_token TeyszanLoLgAwmbawmslstnrslyLoLkninicyctwiandolgcuMedstmepzelotgvunwrnzorkLTewzUKCNDLKNy04.I expires17763361761729613615esolution rules in this tile. Error detalls: Unexpected token =. (16 minutes ago)winasur leams153:14uir-of 2 spaces...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35743
|
|
35744
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
17
1
Previous Highlighted Error
Next Highlighted Error
<template>
<WelcomeLayout
title="Account disconnected"
textPosition="center"
:icon="faUnlink"
:class="$style.layout"
>
<div :class="$style.container" v-if="providersLoaded">
<p>
<strong>
It looks like your {{ localProvider.displayName }} account has become
disconnected
</strong>
</p>
<p :class="$style.small">Please re-connect to continue</p>
<p v-if="isInIframe">
We'll open the {{ localProvider.displayName }} authentication in a new
tab. Please return here and refresh the page once complete
</p>
<GoogleLikeButton
v-if="localProvider.viaIntegrationApp && crmTokenLoaded"
as="a"
:key="localProvider.name"
:brand-logo="localProvider.name"
:class="$style.connectButton"
@click="integrationAppOnClick"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
<GoogleLikeButton
v-if="!localProvider.viaIntegrationApp"
as="a"
:key="localProvider.name"
:href="`/auth/redirect/${localProvider.name}`"
:target="target"
:brand-logo="localProvider.name"
:class="$style.connectButton"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
</div>
<BuildInfo />
<KioskBanner />
</WelcomeLayout>
</template>
<script>
import window from "window";
import axios from "axios";
import { faUnlink } from "@fortawesome/pro-regular-svg-icons";
import isInIframe from "@/utils/isInIframe";
import BuildInfo from "@/components/layout/BuildInfo/BuildInfo.vue";
import KioskBanner from "@/components/shared/KioskBanner/KioskBanner.vue";
import WelcomeLayout from "@/components/layout/WelcomeLayout/WelcomeLayout.vue";
import GoogleLikeButton from "@/components/shared/Buttons/GoogleLikeButton.vue";
import { showSnackbarError, normalizeError } from "@/utils/index";
import { IntegrationAppClient } from "@integration-app/sdk";
export default {
name: "ConnectPage",
components: {
BuildInfo,
KioskBanner,
WelcomeLayout,
GoogleLikeButton,
},
data() {
return {
...window.connectData,
crmToken: null,
faUnlink,
isInIframe,
providers: [],
providersLoaded: false,
crmTokenLoaded: false,
};
},
computed: {
localProvider() {
return this.providers.find((e) => e.name === this.provider);
},
target() {
return this.isInIframe ? "_blank" : null;
},
},
created() {
this.getProviders();
},
mounted() {
this.showErrors();
},
watch: {
providersLoaded() {
if (this.providersLoaded) {
this.prepareIntegrationAppConnection();
}
},
},
methods: {
showErrors() {
if (!this.error) return;
showSnackbarError(this.error, undefined, undefined, false);
},
unwrapEntityResponse({ data }) {
return data.map(({ icon, name, displayName, viaIntegrationApp }) => {
return { icon, name, displayName, viaIntegrationApp };
});
},
async getProviders() {
try {
const response = await axios.get("/api/v1/connect-providers");
this.providers = this.unwrapEntityResponse(response);
this.providersLoaded = true;
} catch {
showSnackbarError(
"An error occurred, while loading form data (connect providers).",
);
}
},
async prepareIntegrationAppConnection() {
if (this.localProvider.viaIntegrationApp) {
try {
const response = await axios.get("/api/v1/integration-app-token");
this.crmToken = response.data.token;
this.crmTokenLoaded = true;
} catch (error) {
console.log(error);
showSnackbarError(
`An error occurred while preparing the page.
Try refreshing, if the error persists get in touch with the Jiminny team.`,
);
}
}
},
async integrationAppOnClick() {
const integrationApp = new IntegrationAppClient({
token: this.crmToken,
});
const connection = await integrationApp
.integration(this.localProvider.name)
.openNewConnection({
showPoweredBy: false,
allowMultipleConnections: false,
});
console.log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));
[IntegrationApp] openNewConnection resolved: {"id":"69e0b41a67d0068c2ca0b48e","name":"Zoho CRM","userId":"1ece66c8-feb1-4df1-b321-21607daf4623","tenantId":"69e0b3faef3e7b6248189289","isTest":false,"connected":true,"state":"READY","errors":[],"integrationId":"66fe6c913202f3a165e3c14d","externalAppId":"6671653e7e2d642e4e41b0fa","authOptionKey":"","createdAt":"2026-04-16T10:04:10.420Z","updatedAt":"2026-04-16T10:04:10.575Z","retryAttempts":0,"isDeactivated":false}
if (connection && connection.disconnected === false) {
try {
const saveRequest = await axios.post(
"/api/v1/integration-app-connect",
);
if (saveRequest.data && saveRequest.data.success === true) {
/** If all is good refresh the page here */
window.location = "/dashboard";
return;
}
throw new Error(saveRequest.data.message);
} catch (error) {
console.log(error);
showSnackbarError(normalizeError(error));
}
}
},
},
};
</script>
<style module lang="less" src="./connect.less"></style>
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35744
|
|
35745
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
17
1
Previous Highlighted Error
Next Highlighted Error
<template>
<WelcomeLayout
title="Account disconnected"
textPosition="center"
:icon="faUnlink"
:class="$style.layout"
>
<div :class="$style.container" v-if="providersLoaded">
<p>
<strong>
It looks like your {{ localProvider.displayName }} account has become
disconnected
</strong>
</p>
<p :class="$style.small">Please re-connect to continue</p>
<p v-if="isInIframe">
We'll open the {{ localProvider.displayName }} authentication in a new
tab. Please return here and refresh the page once complete
</p>
<GoogleLikeButton
v-if="localProvider.viaIntegrationApp && crmTokenLoaded"
as="a"
:key="localProvider.name"
:brand-logo="localProvider.name"
:class="$style.connectButton"
@click="integrationAppOnClick"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
<GoogleLikeButton
v-if="!localProvider.viaIntegrationApp"
as="a"
:key="localProvider.name"
:href="`/auth/redirect/${localProvider.name}`"
:target="target"
:brand-logo="localProvider.name"
:class="$style.connectButton"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
</div>
<BuildInfo />
<KioskBanner />
</WelcomeLayout>
</template>
<script>
import window from "window";
import axios from "axios";
import { faUnlink } from "@fortawesome/pro-regular-svg-icons";
import isInIframe from "@/utils/isInIframe";
import BuildInfo from "@/components/layout/BuildInfo/BuildInfo.vue";
import KioskBanner from "@/components/shared/KioskBanner/KioskBanner.vue";
import WelcomeLayout from "@/components/layout/WelcomeLayout/WelcomeLayout.vue";
import GoogleLikeButton from "@/components/shared/Buttons/GoogleLikeButton.vue";
import { showSnackbarError, normalizeError } from "@/utils/index";
import { IntegrationAppClient } from "@integration-app/sdk";
export default {
name: "ConnectPage",
components: {
BuildInfo,
KioskBanner,
WelcomeLayout,
GoogleLikeButton,
},
data() {
return {
...window.connectData,
crmToken: null,
faUnlink,
isInIframe,
providers: [],
providersLoaded: false,
crmTokenLoaded: false,
};
},
computed: {
localProvider() {
return this.providers.find((e) => e.name === this.provider);
},
target() {
return this.isInIframe ? "_blank" : null;
},
},
created() {
this.getProviders();
},
mounted() {
this.showErrors();
},
watch: {
providersLoaded() {
if (this.providersLoaded) {
this.prepareIntegrationAppConnection();
}
},
},
methods: {
showErrors() {
if (!this.error) return;
showSnackbarError(this.error, undefined, undefined, false);
},
unwrapEntityResponse({ data }) {
return data.map(({ icon, name, displayName, viaIntegrationApp }) => {
return { icon, name, displayName, viaIntegrationApp };
});
},
async getProviders() {
try {
const response = await axios.get("/api/v1/connect-providers");
this.providers = this.unwrapEntityResponse(response);
this.providersLoaded = true;
} catch {
showSnackbarError(
"An error occurred, while loading form data (connect providers).",
);
}
},
async prepareIntegrationAppConnection() {
if (this.localProvider.viaIntegrationApp) {
try {
const response = await axios.get("/api/v1/integration-app-token");
this.crmToken = response.data.token;
this.crmTokenLoaded = true;
} catch (error) {
console.log(error);
showSnackbarError(
`An error occurred while preparing the page.
Try refreshing, if the error persists get in touch with the Jiminny team.`,
);
}
}
},
async integrationAppOnClick() {
const integrationApp = new IntegrationAppClient({
token: this.crmToken,
});
const connection = await integrationApp
.integration(this.localProvider.name)
.openNewConnection({
showPoweredBy: false,
allowMultipleConnections: false,
});
console.log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));
[IntegrationApp] openNewConnection resolved: {"id":"69e0b41a67d0068c2ca0b48e","name":"Zoho CRM","userId":"1ece66c8-feb1-4df1-b321-21607daf4623","tenantId":"69e0b3faef3e7b6248189289","isTest":false,"connected":true,"state":"READY","errors":[],"integrationId":"66fe6c913202f3a165e3c14d","externalAppId":"6671653e7e2d642e4e41b0fa","authOptionKey":"","createdAt":"2026-04-16T10:04:10.420Z","updatedAt":"2026-04-16T10:04:10.575Z","retryAttempts":0,"isDeactivated":false}
if (connection && connection.disconnected === false) {
try {
const saveRequest = await axios.post(
"/api/v1/integration-app-connect",
);
if (saveRequest.data && saveRequest.data.success === true) {
/** If all is good refresh the page here */
window.location = "/dashboard";
return;
}
throw new Error(saveRequest.data.message);
} catch (error) {
console.log(error);
showSnackbarError(normalizeError(error));
}
}
},
},
};
</script>
<style module lang="less" src="./connect.less"></style>
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35745
|
|
35759
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
Previous Highlighted Error
Next Highlighted Error
<template>
<WelcomeLayout
title="Account disconnected"
textPosition="center"
:icon="faUnlink"
:class="$style.layout"
>
<div :class="$style.container" v-if="providersLoaded">
<p>
<strong>
It looks like your {{ localProvider.displayName }} account has become
disconnected
</strong>
</p>
<p :class="$style.small">Please re-connect to continue</p>
<p v-if="isInIframe">
We'll open the {{ localProvider.displayName }} authentication in a new
tab. Please return here and refresh the page once complete
</p>
<GoogleLikeButton
v-if="localProvider.viaIntegrationApp && crmTokenLoaded"
as="a"
:key="localProvider.name"
:brand-logo="localProvider.name"
:class="$style.connectButton"
@click="integrationAppOnClick"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
<GoogleLikeButton
v-if="!localProvider.viaIntegrationApp"
as="a"
:key="localProvider.name"
:href="`/auth/redirect/${localProvider.name}`"
:target="target"
:brand-logo="localProvider.name"
:class="$style.connectButton"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
</div>
<BuildInfo />
<KioskBanner />
</WelcomeLayout>
</template>
<script>
import window from "window";
import axios from "axios";
import { faUnlink } from "@fortawesome/pro-regular-svg-icons";
import isInIframe from "@/utils/isInIframe";
import BuildInfo from "@/components/layout/BuildInfo/BuildInfo.vue";
import KioskBanner from "@/components/shared/KioskBanner/KioskBanner.vue";
import WelcomeLayout from "@/components/layout/WelcomeLayout/WelcomeLayout.vue";
import GoogleLikeButton from "@/components/shared/Buttons/GoogleLikeButton.vue";
import { showSnackbarError, normalizeError } from "@/utils/index";
import { IntegrationAppClient } from "@integration-app/sdk";
export default {
name: "ConnectPage",
components: {
BuildInfo,
KioskBanner,
WelcomeLayout,
GoogleLikeButton,
},
data() {
return {
...window.connectData,
crmToken: null,
faUnlink,
isInIframe,
providers: [],
providersLoaded: false,
crmTokenLoaded: false,
};
},
computed: {
localProvider() {
return this.providers.find((e) => e.name === this.provider);
},
target() {
return this.isInIframe ? "_blank" : null;
},
},
created() {
this.getProviders();
},
mounted() {
this.showErrors();
},
watch: {
providersLoaded() {
if (this.providersLoaded) {
this.prepareIntegrationAppConnection();
}
},
},
methods: {
showErrors() {
if (!this.error) return;
showSnackbarError(this.error, undefined, undefined, false);
},
unwrapEntityResponse({ data }) {
return data.map(({ icon, name, displayName, viaIntegrationApp }) => {
return { icon, name, displayName, viaIntegrationApp };
});
},
async getProviders() {
try {
const response = await axios.get("/api/v1/connect-providers");
this.providers = this.unwrapEntityResponse(response);
this.providersLoaded = true;
} catch {
showSnackbarError(
"An error occurred, while loading form data (connect providers).",
);
}
},
async prepareIntegrationAppConnection() {
if (this.localProvider.viaIntegrationApp) {
try {
const response = await axios.get("/api/v1/integration-app-token");
this.crmToken = response.data.token;
this.crmTokenLoaded = true;
} catch (error) {
console.log(error);
showSnackbarError(
`An error occurred while preparing the page.
Try refreshing, if the error persists get in touch with the Jiminny team.`,
);
}
}
},
async integrationAppOnClick() {
const integrationApp = new IntegrationAppClient({
token: this.crmToken,
});
const connection = await integrationApp
.integration(this.localProvider.name)
.openNewConnection({
showPoweredBy: false,
allowMultipleConnections: false,
});
console.log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));
// [IntegrationApp] openNewConnection resolved: {
// "id":"69e0b41a67d0068c2ca0b48e",
// "name":"Zoho CRM",
// "userId":"1ece66c8-feb1-4df1-b321-21607daf4623",
// "tenantId":"69e0b3faef3e7b6248189289",
// "isTest":false,
// "connected":true,
// "state":"READY",
// "errors":[],
// "integrationId":"66fe6c913202f3a165e3c14d",
// "externalAppId":"6671653e7e2d642e4e41b0fa",
// "authOptionKey":"",
// "createdAt":"2026-04-16T10:04:10.420Z",
// "updatedAt":"2026-04-16T10:04:10.575Z",
// "retryAttempts":0,
// "isDeactivated":false
// }
if (connection && connection.disconnected === false) {
try {
const saveRequest = await axios.post(
"/api/v1/integration-app-connect",
);
if (saveRequest.data && saveRequest.data.success === true) {
/** If all is good refresh the page here */
window.location = "/dashboard";
return;
}
throw new Error(saveRequest.data.message);
} catch (error) {
console.log(error);
showSnackbarError(normalizeError(error));
}
}
},
},
};
</script>
<style module lang="less" src="./connect.less"></style>
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35759
|
|
35760
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
FirefoxFileEditViewHistoryBookmarksProfilesToolsWindowHelp> 0ld6]-zsh• 28526.60kB26.87kB27.91kB30.75kB34.35kB39.49kB39.69kB41.87kB43.21kB47.84kB48.24kB55.13kB61.28kB62.98kB63.06kB64.62kB79.57kB94.84kB115.66kB117.59kB120.68 kB128.67kB129.28kB164.28 kB176.44kB180.40kB197.96kB210.96kB218.14kB264.94kB298.53kB307.13kB343.99kB367.43kB689.63kB825.14kB1,402.47kBSupport Daily - in 1h 54 mAAPP (-zsh)₴4DOCKER• 881DEV (docker)82APP (-zsh)X3ec2-user@ip-10-30-….../public/vue-assets/assets/GridView-vMogKjqT.js./public/vue-assets/assets/ondemand-DgxEX09i.js../public/vue-assets/assets/CrmLink-rTdmxqkp.js./public/vue-assets/assets/liquor-tree-DbetBeVs.js./public/vue-assets/assets/DealRiskList-COSgQDWm.js../public/vue-assets/assets/AskAnything-BiYJNLXH.js:/public/vue-assets/assets/lib-BPR1zwwF.js./public/vue-assets/assets/AppFormField-0eTkGyuH.js:./public/vue-assets/assets/deal-view-B4d9Fnc0.js../public/vue-assets/assets/exports-DIyAIXcT.js../public/vue-assets/assets/playlists-CJz1PCg2.js../public/vue-assets/assets/callScoringTemplates-DQc-joSr.js../public/vue-assets/assets/_copy0bject-DzIIjTZN.js./public/vue-assets/assets/pusher-CYYPj3Hn.js./public/vue-assets/assets/onboard-BGmZPXYh.js../public/vue-assets/assets/StatusBadge-D_dxGN0U.js./public/vue-assets/assets/kiosk-hoNoVi3Z.js./public/vue-assets/assets/deal-insights-CCCD53q2.js../public/vue-assets/assets/ListView-Daurhtak.js../public/vue-assets/assets/_plugin-vue_export-helper-sSs0rPyg.js./public/vue-assets/assets/WelcomeLayout-CI_AuldJ.js:./public/vue-assets/assets/dashboard-B-uDq9Qs.js../public/vue-assets/assets/emoji-input-D_ee3_TC.js../public/vue-assets/assets/sentry-unCNBfeg.js../public/vue-assets/assets/OrgSettingsLayout-Br7DRJ0o.js../public/vue-assets/assets/vuex.esm-bundler-CxmCn-TU.js../public/vue-assets/assets/playback-CB909wM4.js./public/vue-assets/assets/AppButton-OYq5I1u7.js../public/vue-assets/assets/index.module-DoWLv01P.js../public/vue-assets/assets/intl-tel-input-C4VqCHzY.js../public/vue-assets/assets/team-insights-CPsGbra7.js../public/vue-assets/assets/popper-DC--DigQ.js../public/vue-assets/assets/PhoneField-DsfvGNK0.js./public/vue-assets/assets/live-DbuadCCc.js./public/vue-assets/assets/video-js-skin.less_vue_type_style_index_0_src_true_lang-D2hx_saf.js../public/vue-assets/assets/index-CV_ZMn85.js:./public/vue-assets/assets/logged-in-layout-qoUV-hWG.js-zshgzip:10.06kBgzip:9.39kBgz1p:10.18kBgzip:9.58kB9z1p:10.60kBgz1p:14.98kBgzip:12.70kB9z1p:12.68kBgzip:14.35kBgzip:16.46kBgzip:15.07kBgzip:13.28kBgz1p:20.08kBgzip:18.89kB9z1p:21.84kBgz1p:22.94kBgzip:22.63kB9z1p:28.18kBgzip:33.76kB9z1p:38.70 kB921p:34.16kBgzip:40.05kBgz1p:36.72kBgzip:52.24 kB9z1p:56.16kBgz1p:67.85kBgzip:61.61kB9z1p:68.66kBgz1p:64.16kB9z1p:60.30kBgzip:77.20 kBgzip:103.87kBgz1p:84.90kBgzip:97.04kBgzip: 202.81kBgz1p:72.44kBgzip: 438.06kB[plugin builtin:vite-reporter](!) Some chunks are larger than 500 kBafter minification. Consider:- Using dynamic import() to code-split the application- Use build.rolldownOptions.output.codeSplittingto improve chunking: https://rolldown.rs/reference/Output0ptions.codeSplitting- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.• built in 29.05slukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app/front-end (JY-18909-automated-reports-ask-jiminny) $I86-zshmaр:92.74kBmap:73.94kBmap:93.18kBтар :78.74kBтар:115.18kBmap:173.20kBтар :138.34kBтар:150.73 kBmap:150.62kBmaр:294.48kBтар:153.25kBmaр:65.85kBmap:239.59kBтар :219.27kBmар:201.33kBmap:244.72kBтар :300.68kBтар :292.79kBmap:308.10kBmaр:500.60kBтар:258.56kBmaр:410.48kBmap:266.15kBтар :831.82 kBтар:623.70kBmap:836.88kBтар :680.92kBmар :3,947.49 kBmap:1,108.20 kBmap:475.61kBтар:959.66kBmap: 1,245.28kBmap:849.05kBтар :792.41kBmар: 3,016.64 kBmap:436.28kBmaр: 6,282.82kB100% <47O 878Thu 16 Apr 13:06:05181* Unable to acce...O x8APP...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35760
|
|
35761
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
PhpStormFileEditFV faVsco.js vProject v>W testsO connect.lessV connect.vuedashboardDeallnsightserrorPagesD export-portalextension-installledinvitationvonconterenceWa VoUT• LiveCoachD LockedO login› C MeetingConsentO mobileConboard› _mocks_→ ItestsV MobileAppDownk0 Onboard.lessV Onboard vueTs useProvidersSyn‹D ondemandD playback• playlistsSettingssharedSoftphoneCoachSyglcons› _ leaminsights> M composables> M7 directives> M7 helpers> → locales> D pluginsrouterO storeServices+, o, c|v M DatabaseLEU<console S 124 m$v A jiminny@localhostL SFA HS_localY A PRODA console 10 sY d SlAGING4 console 2 s 670 ms→* DockenViewNavigateCodeLaravelRefactorToolsWindowHelp#11894 on JY-18909-automated-reports-ask-lminny k© ReportController.phpC TokenBuilder.php© TeamSetupController.php xphp api.php© SendReportJob.phpC AutomatedReportsCommand.phpAskJiminnykeporscontroller.ono© AutomatedReportsCommandTest.php© AutomatedReportsSendCommand.php© Team.php© AutomatedReportsRepository.php© AutomatedReportsService.php© TrackProviderInstalledEvent.phpC CreateActivityLoggedEvent.phpActivityLoagea.onpAutomatedRenortscallbackService.ono© RequestGenerateAskJiminnyReportJob.php© RequestGenerateReportJob.php© AutomatedReportResult.php(C AutomatedReport.phpclass TeamSetupController extends ControllerA4 ×2 ^public function integrationAppConnect(): JsonResponse/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);= custom.log4 console [EUl= laravel.logA SF [jiminny@localhost]V connect.vue XV Onboard.vueA HS_local [jiminny@localhost]console PRODIA console [STAGING]<SCrIOTmethods:async integrationAppOnClick() {const integrationApp = new IntegrationAppClient({140token: this.crmToken,V1AVSupport Daily • in 1h 54 mA100% 145Thu 16 Apr 13:06:05L AutomatedReportsCommandTestvCascadeIntegration App ConneWhether it resolves as null/ undefined if the user closes the modalWhat the value is when OAuth completes successfullywIne key •uestionis wherner connection,dnsconnected === talse s acrualivthe right check — or if the SDK returns something like connection.connected== true (matching what you saw in the network response earlier).const connection = awalt integrationApplllaiseltetalatelsleinsnteoramonuns, Locaurrovloer.nale)openNewConnection21607daf4623","tenantld":"69eOafd25ae490e8d05d8ca2" "isTest":false,"snowrowereaby. Talse,dLcownuccsoleconnecclons. Talserationld":"66fe6c913202f3a165e3c14d"."externalAppid":"667165/** @var ?SocialAccount $socialAccount */F);16T09:46:11.197Z" "L$socialAccount = $user->getSocialAccount($crmProviderKey);16T09:57:59.175Z"."retryAttempts":0,"isDeactivated":false}if ($socialAccount === null) {console.log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));$this->logger-›error('[IntegrationApp] Unexpected error. Social account is missing.','team_id' => $team->getId(),'iapR-provider' => $realProviderKey,provider' => $crmProviderKey,The object uses connected: true - there is no disconnected field at all. Sothe current check connection.disconnected == false evaluates to undefined == false → false - meaning the POST to /api/v1/integration-app-connect S nevercallled atera successul connection.This is the bug. Fix it in both connect.vue and Onboard.vue:]):return response()->json([success → Tause.203204'message' => 'Something went wrong. Social account is cannot be found.',205->setStatusCode( code:JsonResponse::HTTP_FAILED_DEPENDENCY);206207209$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);$socialAccount->save);157100159160161162=Sthis-›logger-›info('[IntegrationApp] Social account is connected.', [teal1o s sreali-voeclo.'iepR-provider' = $realProviderKey,'provider'=> $crmProviderKey,1711172… Outouilnnn # 1695rowsvid Y15021415DD1! sociable_id Y• provider_user_id Y1695 1001806069711583162871695 17ISLHPVSk2Lk3MK6K7Bbg// [IntegrationApp] openNewConnection resolved: {"id" : "69e0b41a67d0068c2ca0b48e",////"name":"Zoho CRM","userId":"1ece66c8-feb1-4df1-b321-21607daf4623","tenantId": "69e0b3faef3e7b6248189289",→EERS=EIE"isTest": false,|"connected": true,"state": "READY","errors": [],"integrationId": "66fe6c913202f3a165e3c14d","externalAppId": "6671653e7e2d642e4e41b0fa","authoptionKey":"","createdAt": "2026-04-16T10:04:10.420Z","updatedAt": "2026-04-16T10:04:10.575Z","retryAttempts" :0,"isDeactivated" : falseif (connection && connection.disconnected === false) {try {const saveRequest = await axios.post(• connect.vu+1-3Now fix the same bug in Onboard. vue:Read Onboard vue #L503-517D Onboard.vue+1 -1Both files fixed. Summary of the bug:The SDK returns connected: true — there is no disconnected fieldconnection.disconnected === false was undefined === false →alwavs talse → resnevertred• Same in Onboard. vue: connection.disconnected === true was undefined === true → always talse → always snowed the error snackoarNow after openNewConnection() resolves with connected: true, the POST /api/v1/integration-app-connect will be called and the SocialAccount will beAsk anything (&*L)+ @ CodeClaude Sonnet 4.6QGEA00O provider_user_token TID provider_refresh_token Tya29.a0Aa7МYirt0MShIzyiTcZHvFQaJJXsV6R7_bFDR5beR60LLSmTRkt_-ERТ3N-1qХDRpYmKcbLPMd6v05DLy…..eVZORIOLANUAMOISLIrSZVIOIKOINCYLnONOnLUNLISTILO CLlonKCMoIMoUO LInUCUTNUOYUS04.eyszanLoLgAwmbawmslstnrslyLoLkninicyctwiandolgcuMedstmepzelotgvunwrnzorkLTewzUKCNDLKNy04.I expires Y17763361761729613615olution rules in this flle. Error detalls: Unexpectea token =. (1/ minutes ago)winasur leams15/:3uir-of 2 spaces...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35761
|
|
35762
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
Previous Highlighted Error
Next Highlighted Error
<template>
<WelcomeLayout
title="Account disconnected"
textPosition="center"
:icon="faUnlink"
:class="$style.layout"
>
<div :class="$style.container" v-if="providersLoaded">
<p>
<strong>
It looks like your {{ localProvider.displayName }} account has become
disconnected
</strong>
</p>
<p :class="$style.small">Please re-connect to continue</p>
<p v-if="isInIframe">
We'll open the {{ localProvider.displayName }} authentication in a new
tab. Please return here and refresh the page once complete
</p>
<GoogleLikeButton
v-if="localProvider.viaIntegrationApp && crmTokenLoaded"
as="a"
:key="localProvider.name"
:brand-logo="localProvider.name"
:class="$style.connectButton"
@click="integrationAppOnClick"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
<GoogleLikeButton
v-if="!localProvider.viaIntegrationApp"
as="a"
:key="localProvider.name"
:href="`/auth/redirect/${localProvider.name}`"
:target="target"
:brand-logo="localProvider.name"
:class="$style.connectButton"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
</div>
<BuildInfo />
<KioskBanner />
</WelcomeLayout>
</template>
<script>
import window from "window";
import axios from "axios";
import { faUnlink } from "@fortawesome/pro-regular-svg-icons";
import isInIframe from "@/utils/isInIframe";
import BuildInfo from "@/components/layout/BuildInfo/BuildInfo.vue";
import KioskBanner from "@/components/shared/KioskBanner/KioskBanner.vue";
import WelcomeLayout from "@/components/layout/WelcomeLayout/WelcomeLayout.vue";
import GoogleLikeButton from "@/components/shared/Buttons/GoogleLikeButton.vue";
import { showSnackbarError, normalizeError } from "@/utils/index";
import { IntegrationAppClient } from "@integration-app/sdk";
export default {
name: "ConnectPage",
components: {
BuildInfo,
KioskBanner,
WelcomeLayout,
GoogleLikeButton,
},
data() {
return {
...window.connectData,
crmToken: null,
faUnlink,
isInIframe,
providers: [],
providersLoaded: false,
crmTokenLoaded: false,
};
},
computed: {
localProvider() {
return this.providers.find((e) => e.name === this.provider);
},
target() {
return this.isInIframe ? "_blank" : null;
},
},
created() {
this.getProviders();
},
mounted() {
this.showErrors();
},
watch: {
providersLoaded() {
if (this.providersLoaded) {
this.prepareIntegrationAppConnection();
}
},
},
methods: {
showErrors() {
if (!this.error) return;
showSnackbarError(this.error, undefined, undefined, false);
},
unwrapEntityResponse({ data }) {
return data.map(({ icon, name, displayName, viaIntegrationApp }) => {
return { icon, name, displayName, viaIntegrationApp };
});
},
async getProviders() {
try {
const response = await axios.get("/api/v1/connect-providers");
this.providers = this.unwrapEntityResponse(response);
this.providersLoaded = true;
} catch {
showSnackbarError(
"An error occurred, while loading form data (connect providers).",
);
}
},
async prepareIntegrationAppConnection() {
if (this.localProvider.viaIntegrationApp) {
try {
const response = await axios.get("/api/v1/integration-app-token");
this.crmToken = response.data.token;
this.crmTokenLoaded = true;
} catch (error) {
console.log(error);
showSnackbarError(
`An error occurred while preparing the page.
Try refreshing, if the error persists get in touch with the Jiminny team.`,
);
}
}
},
async integrationAppOnClick() {
const integrationApp = new IntegrationAppClient({
token: this.crmToken,
});
const connection = await integrationApp
.integration(this.localProvider.name)
.openNewConnection({
showPoweredBy: false,
allowMultipleConnections: false,
});
console.log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));
// [IntegrationApp] openNewConnection resolved: {
// "id":"69e0b41a67d0068c2ca0b48e",
// "name":"Zoho CRM",
// "userId":"1ece66c8-feb1-4df1-b321-21607daf4623",
// "tenantId":"69e0b3faef3e7b6248189289",
// "isTest":false,
// "connected":true,
// "state":"READY",
// "errors":[],
// "integrationId":"66fe6c913202f3a165e3c14d",
// "externalAppId":"6671653e7e2d642e4e41b0fa",
// "authOptionKey":"",
// "createdAt":"2026-04-16T10:04:10.420Z",
// "updatedAt":"2026-04-16T10:04:10.575Z",
// "retryAttempts":0,
// "isDeactivated":false
// }
if (connection && connection.disconnected === false) {
try {
const saveRequest = await axios.post(
"/api/v1/integration-app-connect",
);
if (saveRequest.data && saveRequest.data.success === true) {
/** If all is good refresh the page here */
window.location = "/dashboard";
return;
}
throw new Error(saveRequest.data.message);
} catch (error) {
console.log(error);
showSnackbarError(normalizeError(error));
}
}
},
},
};
</script>
<style module lang="less" src="./connect.less"></style>
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35762
|
|
35772
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
FirefoxFileEditViewHistoryBookmarksProfilesToolsWindowHelp> 0Support Daily - in 1h 54 mAPP (-zsh)X3ec2-user@ip-10-30-….₴4-zshDOCKER• 81DEV (docker)82APP (-zsh)../public/vue-assets/assets/GridView-vMogKjqT.js./public/vue-assets/assets/ondemand-DgxEX09i.js../public/vue-assets/assets/CrmLink-rTdmxqkp.js./public/vue-assets/assets/liquor-tree-DbetBeVs.js./public/vue-assets/assets/DealRiskList-COSgQDWm.js../public/vue-assets/assets/AskAnything-BiYJNLXH.js:/public/vue-assets/assets/lib-BPR1zwwF.js./public/vue-assets/assets/AppFormField-0eTkGyuH.js:./public/vue-assets/assets/deal-view-B4d9Fnc0.js../public/vue-assets/assets/exports-DIyAIXcT.js../public/vue-assets/assets/playlists-CJz1PCg2.js../public/vue-assets/assets/callScoringTemplates-DQc-joSr.js../public/vue-assets/assets/_copy0bject-DzIIjTZN.js./public/vue-assets/assets/pusher-CYYPj3Hn.js./public/vue-assets/assets/onboard-BGmZPXYh.js../public/vue-assets/assets/StatusBadge-D_dxGN0U.js./public/vue-assets/assets/kiosk-hoNoVi3Z.js./public/vue-assets/assets/deal-insights-CCCD53q2.js../public/vue-assets/assets/ListView-Daurhtak.js../public/vue-assets/assets/_plugin-vue_export-helper-sSs0rPyg.js./public/vue-assets/assets/WelcomeLayout-CI_AuldJ.js:./public/vue-assets/assets/dashboard-B-uDq9Qs.js../public/vue-assets/assets/emoji-input-D_ee3_TC.js../public/vue-assets/assets/sentry-unCNBfeg.js../public/vue-assets/assets/OrgSettingsLayout-Br7DRJ0o.js../public/vue-assets/assets/vuex.esm-bundler-CxmCn-TU.js../public/vue-assets/assets/playback-CB909wM4.js./public/vue-assets/assets/AppButton-OYq5I1u7.js../public/vue-assets/assets/index.module-DoWLv01P.js../public/vue-assets/assets/intl-tel-input-C4VqCHzY.js../public/vue-assets/assets/team-insights-CPsGbra7.js../public/vue-assets/assets/popper-DC--DigQ.js../public/vue-assets/assets/PhoneField-DsfvGNK0.js./public/vue-assets/assets/live-DbuadCCc.js./public/vue-assets/assets/video-js-skin.less_vue_type_style_index_0_src_true_lang-D2hx_saf.js../public/vue-assets/assets/index-CV_ZMn85.js:./public/vue-assets/assets/logged-in-layout-qoUV-hWG.js• 28526.60kB26.87kB27.91kB30.75kB34.35kB39.49kB39.69kB41.87kB43.21kB47.84kB48.24kB55.13kB61.28kB62.98kB63.06kB64.62kB79.57kB94.84kB115.66kB117.59kB120.68 kB128.67kB129.28kB164.28 kB176.44kB180.40kB197.96kB210.96kB218.14kB264.94kB298.53kB307.13kB343.99kB367.43kB689.63kB825.14kB1,402.47kB-zshgzip:10.06kBgzip:9.39kBgz1p:10.18kBgzip:9.58kB9z1p:10.60kBgz1p:14.98kBgzip:12.70kB9z1p:12.68kBgzip:14.35kBgzip:16.46kBgzip:15.07kBgzip:13.28kBgz1p:20.08kBgzip:18.89kB9z1p:21.84kBgz1p:22.94kBgzip:22.63kB9z1p:28.18kBgzip:33.76kB9z1p:38.70 kB921p:34.16kBgzip:40.05kBgz1p:36.72kBgzip:52.24 kB9z1p:56.16kBgz1p:67.85kBgzip:61.61kB9z1p:68.66kBgz1p:64.16kB9z1p:60.30kBgzip:77.20 kBgzip:103.87kBgz1p:84.90kBgzip:97.04kBgzip: 202.81kBgz1p:72.44kBgzip: 438.06kB[plugin builtin:vite-reporter](!) Some chunks are larger than 500 kBafter minification. Consider:- Using dynamic import() to code-split the application- Use build.rolldownOptions.output.codeSplittingto improve chunking: https://rolldown.rs/reference/Output0ptions.codeSplitting- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.• built in 29.05slukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app/front-end (JY-18909-automated-reports-ask-jiminny) $IA86-zshmaр:92.74kBmap:73.94kBmap:93.18kBтар :78.74kBтар:115.18kBmap:173.20kBтар :138.34kBтар:150.73 kBmap:150.62kBmaр:294.48kBтар:153.25kBmaр:65.85kBmap:239.59kBтар :219.27kBmар:201.33kBmap:244.72kBтар :300.68kBтар :292.79kBmap:308.10kBmaр:500.60kBтар:258.56kBmaр:410.48kBmap:266.15kBтар :831.82 kBтар:623.70kBmap:836.88kBтар :680.92kBmар :3,947.49 kBmap:1,108.20 kBmap:475.61kBтар:959.66kBmap: 1,245.28kBmap:849.05kBтар :792.41kBmар: 3,016.64 kBmap:436.28kBmaр: 6,282.82kB100% <47O 878Thu 16 Apr 13:06:58181* Unable to acce...O x8APP...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35772
|
|
35773
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
PhpStormFileEditFV faVsco.js vProject v>W testsO connect.lessV connect.vuedashboardDeallnsightserrorPagesD export-portalextension-installledinvitationvonconterenceWa VoUT• LiveCoachD LockedO login› C MeetingConsentO mobileConboard› _mocks_→ ItestsV MobileAppDownk0 Onboard.lessV Onboard vueTs useProvidersSyn‹D ondemandD playback• playlistsSettingssharedSoftphoneCoachSyglcons› _ leaminsights> M composables> M7 directives> M7 helpers> → locales> D pluginsrouterO storeServices+, o, c|v M DatabaseLEU<console S 124 m$v A jiminny@localhostL SFA HS_localY A PRODA console 10 sY d SlAGING4 console 2 s 670 ms- DockerViewNavigateCodeLaravelRefactorToolsWindowHelp#11894 on JY-18909-automated-reports-ask-jiminny k© ReportController.phpC TokenBuilder.php© TeamSetupController.php xphp api.php© SendReportJob.phpC AutomatedReportsCommand.phpAskJiminnykeporscontroller.ono© AutomatedReportsCommandTest.php© AutomatedReportsSendCommand.php© Team.php© AutomatedReportsRepository.php© AutomatedReportsService.php© TrackProviderInstalledEvent.phpC CreateActivityLoggedEvent.phpActivityLoagea.onp)AutomatedRenortscallbackservice.onv© RequestGenerateAskJiminnyReportJob.php© RequestGenerateReportJob.php© AutomatedReportResult.php(C AutomatedReport.phpclass TeamSetupController extends ControllerA4 X2 ^V144public function integrationAppConnect(): JsonResponse/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);= custom.log4 console [EUl<SCrIOTmethods:async integrationAppOnClick() {Loken: ths.crnloken.7):consc conneccion - avalt intecracionapointegration(this.localProvider.name).openNewConnection({showPoweredBy: false,allowMultipleConnections: false,1):= laravel.logA SF [jiminny@localhost]V connect.vue >V Onboard.vueA HS_local [jiminny@localhost]console PRODA console [STAGING]V1 Л VSupport Daily • in 1h 54 mA100% 145Thu 16 Apr 13:06:58L AutomatedReportsCommandTestvCascadeIntegration App Conne+D ..Whether it resolves as null/ undefined if the user closes the modalWhat the value is when OAuth completes successfullywIne key •uestionis wherner connection,dnsconnected === talse s acrualivthe right check — or if the SDK returns something like connection.connected== true (matching what you saw in the network response earlier).alaealttelatels eins21607daf4623","te55b7","name":"Zoho":"69eOafd25ae490e8d05d8ca2","isTest":false,")2f3a165e3c14d" "externalAppld"."667165/** @var ?SocialAccount $socialAccount */$socialAccount = $user->getSocialAccount($crmProviderKey);if ($socialAccount === null) {$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.','team_id' => $team->getId(),'iapR-provider' => $realProviderKey,provider' => $crmProviderKey,console.log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));16T09:57:59.175Z"."retryAttempts":0,"isDeactivated":false}]):return response()->isondi203204205206207209success → Tause.'message' => 'Something went wrong. Social account is cannot be found.',->setStatusCode( code:JsonResponse::HTTP_FAILED_DEPENDENCY);$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED) ;$socialAccount->save);Sthis-›logger-›info('[IntegrationApp] Social account is connected.', [teal1o s sreali-voeclo.'iepR-provider' = $realProviderKey,'provider'=> $crmProviderKey,160161162163164100167=16816917€172173// [IntegrationApp] openNewConnection resolved: {"id" : "69e0b41a67d0068c2ca0b48e",//"name": "Zoho CRM",//"userId": "1ece66c8-feb1-4df1-b321-21607daf4623","tenantId" : "69e0b3faef3e7b6248189289","isTest" : false,"connected": true,"state": "READY","errors": [],"integrationId":"66fe6c913202f3a165e3c14d","externalAppId" : "6671653e7e2d642e4e41b0fa","authOptionKey":"","createdAt": "2026-04-16T10:04:10.420Z","updatedAt": "2026-04-16T10:04:10.575Z" ,|//"retryAttempts":0,//"isDeactivated" : false1/ 3if (connection && connection.connected === true) {// if (connection && connection.disconnected === false) {try {The object uses connected: true - there is no disconnected field at all. Sothe current check connection.disconnected == false evaluates to undefined == false → false - meaning the POST to /api/v1/integration-app-connect S nevercallled anera successul connectionThis is the bug. Fix it in both connect, vue and enboard, vue:• connect.vu+1-3Now fix the same bug in Onboard. vue:Read Onboard vue #L503-517D Onboard.vue+1 -1Both files fixed. Summary of the bug:• The SDK returns connected: true - there is no disconnected fieldconnection.disconnected === false was undefined === false →alwavs talse → resnevertred• Same in Onboard. vue: connection.disconnected === true was undefined === true → always talse → always snowed the error snackoarNow after openNewConnection() resolves with connected: true, the POST /api/v1/integration-app-connect will be called and the SocialAccount will beAsk anything (&*L)+ @ CodeClaude Sonnet 4.6const saveredlest = await axlos.dostt… Outouilnnn # 1695rowsvid Y15021415DD1! sociable_id Y• provider_user_id Y1695 1001806069711583162871695 17ISLHPVSk2Lk3MK6K7BbgQGEA0!O provider_User_token TID provider_refresh_token Tya29.a0Aa7МYirt0MShIzyiTcZHvFQaJJXsV6R7_bFDR5beR601LSmTRkt_-ERT3N-1qХDRpYmKcbLPMd6v05DLy...eVZORIOLANUAMOISLIrSZVIOIKOINCYLnONOnLUNLISTILO CLlonKCMoIMoUO LInUCUTNUOYUS04.eyszanLoLgAwmbawmslstnrslyLoLkninicyctwiandolgcuMedstmepzelotgvunwrnzorkLTewzUKCNDLKNy04.I expires Y17763361761729613615esolution rules in this tlle. Error detalls: Unexpected token =. (18 minutes ago)winasun leams165:03uir-of 2 spaces...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35773
|
|
35786
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
FirefoxFileEditViewHistoryBookmarksProfilesToolsWindowHelp> 0ld6]-zsh• 28526.60kB26.87kB27.91kB30.75kB34.35kB39.49kB39.69kB41.87kB43.21kB47.84kB48.24kB55.13kB61.28kB62.98kB63.06kB64.62kB79.57kB94.84kB115.66kB117.59kB120.68 kB128.67kB129.28kB164.28 kB176.44kB180.40kB197.96kB210.96kB218.14kB264.94kB298.53kB307.13kB343.99kB367.43kB689.63kB825.14kB1,402.47kBSupport Daily - in 1h 53 mAAPP (-zsh)₴4DOCKER• 881DEV (docker)82APP (-zsh)X3ec2-user@ip-10-30-...../public/vue-assets/assets/GridView-vMogKjqT.js./public/vue-assets/assets/ondemand-DgxEX09i.js../public/vue-assets/assets/CrmLink-rTdmxqkp.js./public/vue-assets/assets/liquor-tree-DbetBeVs.js./public/vue-assets/assets/DealRiskList-COSgQDWm.js../public/vue-assets/assets/AskAnything-BiYJNLXH.js:/public/vue-assets/assets/lib-BPR1zwwF.js./public/vue-assets/assets/AppFormField-0eTkGyuH.js:./public/vue-assets/assets/deal-view-B4d9Fnc0.js../public/vue-assets/assets/exports-DIyAIXcT.js../public/vue-assets/assets/playlists-CJz1PCg2.js../public/vue-assets/assets/callScoringTemplates-DQc-joSr.js../public/vue-assets/assets/_copy0bject-DzIIjTZN.js./public/vue-assets/assets/pusher-CYYPj3Hn.js./public/vue-assets/assets/onboard-BGmZPXYh.js../public/vue-assets/assets/StatusBadge-D_dxGN0U.js./public/vue-assets/assets/kiosk-hoNoVi3Z.js./public/vue-assets/assets/deal-insights-CCCD53q2.js../public/vue-assets/assets/ListView-Daurhtak.js../public/vue-assets/assets/_plugin-vue_export-helper-sSs0rPyg.js./public/vue-assets/assets/WelcomeLayout-CI_AuldJ.js:./public/vue-assets/assets/dashboard-B-uDq9Qs.js../public/vue-assets/assets/emoji-input-D_ee3_TC.js../public/vue-assets/assets/sentry-unCNBfeg.js../public/vue-assets/assets/OrgSettingsLayout-Br7DRJ0o.js../public/vue-assets/assets/vuex.esm-bundler-CxmCn-TU.js../public/vue-assets/assets/playback-CB909wM4.js./public/vue-assets/assets/AppButton-OYq5I1u7.js../public/vue-assets/assets/index.module-DoWLv01P.js../public/vue-assets/assets/intl-tel-input-C4VqCHzY.js../public/vue-assets/assets/team-insights-CPsGbra7.js../public/vue-assets/assets/popper-DC--DigQ.js../public/vue-assets/assets/PhoneField-DsfvGNK0.js./public/vue-assets/assets/live-DbuadCCc.js./public/vue-assets/assets/video-js-skin.less_vue_type_style_index_0_src_true_lang-D2hx_saf.js../public/vue-assets/assets/index-CV_ZMn85.js:./public/vue-assets/assets/logged-in-layout-qoUV-hWG.js-zshgzip:10.06kBgzip:9.39kBgz1p:10.18kBgzip:9.58kB9z1p:10.60kBgz1p:14.98kBgzip:12.70kB9z1p:12.68kBgzip:14.35kBgzip:16.46kBgzip:15.07kBgzip:13.28kBgz1p:20.08kBgzip:18.89kB9z1p:21.84kBgz1p:22.94kBgzip:22.63kB9z1p:28.18kBgzip:33.76kB9z1p:38.70 kB921p:34.16kBgzip:40.05kBgz1p:36.72kBgzip:52.24 kB9z1p:56.16kBgz1p:67.85kBgzip:61.61kB9z1p:68.66kBgz1p:64.16kB9z1p:60.30kBgzip:77.20 kBgzip:103.87kBgz1p:84.90kBgzip:97.04kBgzip: 202.81kBgz1p:72.44kBgzip: 438.06kB[plugin builtin:vite-reporter](!) Some chunks are larger than 500 kBafter minification. Consider:- Using dynamic import() to code-split the application- Use build.rolldownOptions.output.codeSplittingto improve chunking: https://rolldown.rs/reference/Output0ptions.codeSplitting- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.• built in 29.05slukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app/front-end (JY-18909-automated-reports-ask-jiminny) $I86-zshmaр:92.74kBmap:73.94kBmap:93.18kBтар :78.74kBтар:115.18kBmap:173.20kBтар :138.34kBтар:150.73 kBmap:150.62kBmaр:294.48kBтар:153.25kBmaр:65.85kBmap:239.59kBтар :219.27kBmар:201.33kBmap:244.72kBтар :300.68kBтар :292.79kBmap:308.10kBmaр:500.60kBтар:258.56kBmaр:410.48kBmap:266.15kBтар :831.82 kBтар:623.70kBmap:836.88kBтар :680.92kBmар :3,947.49 kBmap:1,108.20 kBmap:475.61kBтар:959.66kBmap: 1,245.28kBmap:849.05kBтар :792.41kBmар: 3,016.64 kBmap:436.28kBmaр: 6,282.82kB100% <47O 878Thu 16 Apr 13:07:47181* Unable to acce....O x8APP...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35786
|
|
35787
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
PhpStormFileEditFV faVsco.js vProject v>W testsO connect.lessV connect.vuedashboardDeallnsightserrorPagesD export-portalextension-installledinvitationvonconterenceWa VoUT• LiveCoachD LockedO loginMeetingConsentO mobileConboard› _mocks_→ ItestsV MobileAppDownk0 Onboard.lessV Onboard vueTs useProvidersSyn‹ondemandD playback• playlistsSettingssharedSoftphoneCoachSvgicons_leaminsights> M composables> M7 directives> M7 helpers> → locales> D pluginsrouterO storeViewNavigateCodeLaravelRefactorToolsWindowHelp#11894 on JY-18909-automated-reports-ask-lminny k© ReportController.phpC TokenBuilder.php© TeamSetupController.php xphp api.php© SendReportJob.phpC AutomatedReportsCommand.phpAskJiminnykeporscontroller.ono© AutomatedReportsCommandTest.php© AutomatedReportsSendCommand.php© Team.php© AutomatedReportsRepository.php© AutomatedReportsService.php© TrackProviderInstalledEvent.phpC CreateActivityLoggedEvent.phpActivityLoagea.onpServices+,o, c|v M DatabaseLEU<console S 124 m$v Ajiminny@localhostL SPA HS_localY A PRODA console 10 sY d SlAGING4 console 2 s 670 ms- Docker= custom.log4 console [EUldisc< script>setup() {= laravel.logA SF [jiminny@localhost]V connAft.vue xV Onboard.vue Xconsole PRODIconsole SlAGINGCc W.*379492AutomatedRenortscallbackService.ono© RequestGenerateAskJiminnyReportJob.php© RequestGenerateReportJob.php© AutomatedReportResult.php(C AutomatedReport.phpclass TeamSetupController extends Controller44X2 ^public function integrationAppConnect(): JsonResponse/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);474495496497498499500/** BEGTN: IntegrationAop related functionalitu */const crmToken = ref(null);/** @var ?SocialAccount $socialAccount */$socialAccount = $user->getSocialAccount($crmProviderKey);if ($socialAccount === null) {$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.','team_id' => $team->getId(),'iapR-provider' => $realProviderKey,provider' => $crmProviderKey,]):return response()->isondisuccess' → tauser'message' => 'Something went wrong. Social account is cannot be found.',50650%508509510511512513514515const crmConnectIntegrationApp = async function () {const integrationApp = new IntegrationAppClient({token: crmToken.valve,}):const connection = await integrationApp• Incegracion(crmbetalls.name)ovenmewconnecronl"snowrowereaby. Talse,allowMultipleConnections: false,F):if (Iconnection || connection.connected === true) {if (Iconnection || connection.disconnected === true) {showSnackbarError("A connection with your CRM could not be established",Cascade & [IBAN]->setStatusCode( code:JsonResponse::HTTP_FAILED_DEPENDENCY);return;209$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);$socialAccount->save);Sthis-›logger-›info('[IntegrationApp] Social account is connected.', [teal1o s sreali-voeclo.'iepR-provider' = $realProviderKey,'provider'=> $crmProviderKey,518=520521522523524try {const saveRequest = await axios.post("/api/v1/integration-app-connect");if (saveRequest.data && saveRequest.data.success === true) {/** If all is good refresh the page here */return location.reload();… Outouilnnn # 1695rowsvid Y! sociable_id Y15021415DD1• provider_user_id Y1695 1001806069711583162871695 17ISLHPVSk2Lk3MK6K7BbgQGEA0!O provider_user_token Yya29.a0Aa7МYirt0MShIzyiTcZHvFQaJJXsV6R7_bFDR5beR60LLSmTRkt_-ERТ3N-1qХDRpYmKcbLPMd6v05DLy…..eVZORIOLANUAMOISLIrSZVIOIKOINCYLnONOnLUNLISTILO CLlonKCMoIMoUO LInUCUTNUOYUS04.Support Daily • in 1h 53 mAU AutomatedReportsCommandTest-100% 145Thu 16 Apr 13:07:47A HS_local [jiminny@localhost]m | 06x1×9^CascadeIntegration App ConneWhether it resolves as null / undefined if the user closes the modallWhat the value is when OAuth completes successfullyIne key •uestionis wherner connection,dnsconnected === talse s acrualivthe right check — or if the SDK returns something like connection.connected== true (matching what you saw in the network response earlier).wCommand &1alaiselttalatelsleins1d955b7","name":"Zoho21607daf4623","tenantld":"69eOafd25ae490e8d05d8ca2","isTest"'false,"2f3a165e3c14d","externalAppld"."66716516T09:57:59.175Z"."retryAttempts":0,"isDeactivated":false}The object uses connected: true - there is no disconnected field at all. Sothe current check connection.disconnected == false evaluates to undefined == false → false - meaning the POST to /api/v1/integration-app-connect S nevercallled atera successul connection.This is the bug. Fix it in both connect.vue and Onboard.vue:• connect.vu+1-3Now fix the same bug in Onboard. vue:Read Onboard vue #L503-517D Onboard.vue+1 -1Both files fixed. Summary of the bug:The SDK returns connected: true — there is no disconnected fieldconnection.disconnected === false was undefined == false →alwavs talse → restnevertrecSame in Onboard. vue: connection.disconnected === true was undefined === true → always talse → always snowed the error snackoarNow after openNewConnection() resolves with connected: true, the POST /api/v1/integration-app-connect will be called and the SocialAccount will beAsk anything (&*L)+ @ CodeClaude Sonnet 4.6ID provider_refresh_token TeyszanLoLgAwmbawmslstnrslyLoLkninicyctwiandolgcuMedstmepzelotgvunwrnzorkLTewzUKCNDLKNy04.I expires17763361761729613615esolution rules in this tlle. Error detalls: Unexpected token =. (19 minutes ago)W Windsur leams510:05 4 charsuir-of 2 spaces...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35787
|
|
35813
|
App "Zoho CRM" • Kavita - Membrane - 16 Ap App "Zoho CRM" • Kavita - Membrane - 16 April 2026• 1 viewIntegrations / Zoho CRM • ReadyUl Code• TestKavita vNameZoho CRMQ(KK*DashboardKeyzoho-crmExplore0:26"Docs cAdminDescriptionAdd a description for this integrationLogohttps://static.integration.app/connectors/zoho-crm/logo.pngAppZoho CRMConnector2 Zoho CRMConnector VersionTest Integration Zoho CRMD Connects° Connection •• Logs 8User Inputco Connection: Zoho CRM •Account Type?L production+ Add to your productAgentWhat would you like to do?BetaRecent sessionsImplement this elementBuild me an action go get deleted Companies from HubspotList all the tools you have avaailable11 All sessions→→Logout0:27/ 0:30Il Loom - Screen Recorder & Screen Capture is sharing your screen.•H00Stop sharingHide© Comment...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35813
|
|
35817
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
Previous Highlighted Error...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35817
|
|
35874
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
FirefoxFileEditViewHistoryBookmarksProfilesToolsWindowHelp> 0APP (-zsh)₴4DOCKER• 881DEV (docker)82APP (-zsh)X3ec2-user@ip-10-30-...../public/vue-assets/assets/GridView-CJVxH4Dg.js./public/vue-assets/assets/ondemand-CBhkAD17.js../public/vue-assets/assets/CrmLink-rTdmxqkp.js./public/vue-assets/assets/liquor-tree-DbetBeVs.js./public/vue-assets/assets/DealRiskList-BnbcVBB8.js../public/vue-assets/assets/AskAnything-s720pn9E.js:/public/vue-assets/assets/lib-BPR1zwwF.js./public/vue-assets/assets/AppFormField-BgVfo6PN.js../public/vue-assets/assets/deal-view-Jn4yJ9Hz.js../public/vue-assets/assets/exports-DIyAIXcT.js../public/vue-assets/assets/playlists-DpSiCNMr.js../public/vue-assets/assets/callScoringTemplates-DQc-joSr.js../public/vue-assets/assets/_copy0bject-DzIIjTZN.js./public/vue-assets/assets/pusher-CYYPj3Hn.js./public/vue-assets/assets/onboard-DDojXW3c.js../public/vue-assets/assets/StatusBadge-BMn_k29a.js./public/vue-assets/assets/kiosk-nxpVorIV.js./public/vue-assets/assets/deal-insights-D5sbo4zZ.js../public/vue-assets/assets/ListView-D1HYjAvt.js../public/vue-assets/assets/_plugin-vue_export-helper-sSs0rPyg.js./public/vue-assets/assets/WelcomeLayout-B2BjjI5T.js:./public/vue-assets/assets/dashboard-CDcAQG1E.js../public/vue-assets/assets/emoji-input-D_ee3_TC.js../public/vue-assets/assets/sentry-h1XGLinV.js../public/vue-assets/assets/OrgSettingsLayout-1YAa0isa.js../public/vue-assets/assets/vuex.esm-bundler-CxmCn-TU.js../public/vue-assets/assets/playback-VJS8X-le.js./public/vue-assets/assets/AppButton-OYq5I1u7.js../public/vue-assets/assets/index.module-DoWLv01P.js../public/vue-assets/assets/intl-tel-input-C4VqCHzY.js../public/vue-assets/assets/team-insights-CrkL2M3g.js../public/vue-assets/assets/popper-DC--DigQ.js../public/vue-assets/assets/PhoneField-DsfvGNK0.js•/public/vue-assets/assets/live-DHZ3jGjw.js./public/vue-assets/assets/video-js-skin.less_vue_type_style_index_0_src_true_lang-D2hx_saf.js../public/vue-assets/assets/index-DVKeaTSE.js../public/vue-assets/assets/logged-in-layout-B0d2IU06.js-zsh• ₴5|26.60kB26.87kB27.91kB30.75kB34.35kB39.49kB39.69kB41.87kB43.21kB47.84kB48.24kB55.13kB61.28kB62.98kB63.05kB64.62kB79.57kB94.84kB115.66kB117.59kB120.68 kB128.67kB129.28kB164.28 kB176.44kB180.40kB197.96kB210.96kB218.14kB264.94kB298.53kB307.13kB343.99kB367.43kB689.63kB825.14kB1,402.47kB[plugin builtin:vite-reporter](!) Some chunks are larger than 500 kBafter minification. Consider:- Using dynamic import() to code-split the application- Use build.rolldownOptions.output.codeSplittingto improve chunking: https://rolldown.rs/reference/Output0ptions.codeSplitting- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.• built in 29.74slukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app/front-end (JY-18909-automated-reports-ask-jiminny) $I= Support Daily • in 1h 48 m-zshgzip:10.05kBgzip:9.38kBgz1p:10.18kBgzip:9.58kB9z1p:10.60kBgz1p:14.98kBgzip:12.70kB9z1p:12.68kBgzip:14.34kBgzip:16.46kBgzip:15.06kBgzip:13.28kBgz1p:20.08kBgzip:18.89kB9z1p:21.83kBgz1p:22.94kBgzip:22.63kB9z1p:28.17kBgzip:33.76kB9z1p:38.70 kB921p:34.16kBgzip:40.04kBgz1p:36.72kBgzip:52.24 kB9z1p:56.16kBgz1p:67.85kBgzip:61.61kB9z1p:68.66kBgz1p:64.16kB9z1p:60.30kBgzip:77.20 kBgzip:103.87kBgz1p:84.90kBgzip:97.04kBgzip: 202.81kBgz1p:72.44kBgzip: 438.06kB86-zshmaр:92.74kBmap:73.94kBmap:93.18kBтар :78.74kBтар:115.18kBmap:173.20kBтар :138.34kBтар:150.73 kBmap:150.62kBmaр:294.48kBтар:153.25kBmaр:65.85kBmap:239.59kBтар :219.27kBmар:201.39kBmap:244.72kBтар :300.68kBтар :292.79kBmap:308.10kBmaр:500.60kBтар:258.56kBmaр:410.48kBmap:266.15kBтар :831.82 kBтар:623.70kBmap:836.88kBтар :680.92kBmар :3,947.49 kBmap:1,108.20kBmap:475.61kBтар:959.66kBmap:1,245.28kBmap:849.05kBтар :792.41kBmар: 3,016.64 kBmap:436.28kBmaр: 6,282.82kB100% <47O 878Thu 16 Apr 13:12:28181* Unable to acce...O 88APP...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35874
|
|
35875
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
PhpStormFileEditFV faVsco.js vProject v>W testsO connect.lessV connect.vuedashboardDeallnsightserrorPagesD export-portalextension-installledinvitationvonconterenceWa VoUT• LiveCoachD LockedO login› C MeetingConsentO mobileConboard› _mocks_→ ItestsV MobileAppDownk0 Onboard.lessV Onboard vueTs useProvidersSyn‹ondemandD playback• playlistsSettingssharedSoftphoneCoachSyglcons› _ leaminsights> M composables> M7 directives> M7 helpers> → locales> D pluginsrouterO storeServices+,o, c|v M DatabaseLEU<console S 124 m$v A jiminny@localhostL SFA HS_localY A PRODA console 10 sY d SlAGING4 console 2 s 670 ms→* DockenViewNavigateCodeLaravelRefactorToolsWindowHelp#11894 on JY-18909-automated-reports-ask-lminny k© ReportController.phpC TokenBuilder.php© TeamSetupController.php xphp api.php© SendReportJob.phpC AutomatedReportsCommand.phpAskJiminnykeporscontroller.ono© AutomatedReportsCommandTest.php© AutomatedReportsSendCommand.php© Team.php© AutomatedReportsRepository.php© AutomatedReportsService.php© TrackProviderInstalledEvent.phpC CreateActivityLoggedEvent.phpActivityLoagea.onp)AutomatedRenortscallbackservice.onv© RequestGenerateAskJiminnyReportJob.php© RequestGenerateReportJob.php© AutomatedReportResult.php(C AutomatedReport.phpclass TeamSetupController extends Controller14744 X2 ^V144public function integrationAppConnect(): JsonResponse/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);= custom.log4 console [EUl<SCrIOTmethods:async integrationAppOnClick() {Loken: ths.crnloken.7):consc conneccion - avalt intecracionapointegration(this.localProvider.name).openNewConnection({showPoweredBy: false,allowMultipleConnections: false,1481):= laravel.logA SF [jiminny@localhost]V connect.vue >V Onboard.vueA HS_local [jiminny@localhost]console PRODA console [STAGING]V1ЛVSupport Daily • in 1h 48 mA100% 145Thu 16 Apr 13:12:28U AutomatedReportsCommandTest-CascadeIntegration App ConneWhether it resolves as null/ undefined if the user closes the modalWhat the value is when OAuth completes successfullywIne key •uestionis wherner connection,dnsconnected === talse s acrualivthe right check — or if the SDK returns something like connection.connected== true (matching what you saw in the network response earlier)./** @var ?SocialAccount $socialAccount */$socialAccount = $user->getSocialAccount($crmProviderKey);if ($socialAccount === null) {$this->logger-›error('[IntegrationApp] Unexpected error. Social account is missing.','team_id' => $team->getId(),'iapR-provider' => $realProviderKey,provider' => $crmProviderKey,]):return response()->isondil203204205206207209success' → tauser160'message' => 'Something went wrong. Social account is cannot be found.',161162->setStatusCode( code:JsonResponse::HTTP_FAILED_DEPENDENCY);163164$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);$socialAccount->save);Sthis-›logger-›info('[IntegrationApp] Social account is connected.', [teal1o s sreali-voeclo.'iepR-provider' = $realProviderKey,'provider'=> $crmProviderKey,172173… Outouilnnn # 1695rowsvid Y15021415DD1! sociable_id Y• provider_user_id Y1695 1001806069711583162871695 17ISLHPVSk2Lk3MK6K7Bbgconsole.log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));// [IntegrationApp] openNewConnection resolved: {"id" : "69e0b41a67d0068c2ca0b48e",////////"name": "Zoho CRM","userId": "1ece66c8-feb1-4df1-b321-21607daf4623","tenantId" : "69e0b3faef3e7b6248189289","isTest" : false,"connected": true,"state": "READY",DEEEIIIE"errors": [],"integrationId":"66fe6c913202f3a165e3c14d","externalAppId": "6671653e7e2d642e4e41b0fa","authuptlonkey"*"""createdAt": "2026-04-16T10:04:10.420Z" ,"updatedAt": "2026-04-16T10:04:10.575Z","retryAttempts" :0,"isDeactivated" : falseif (connection && connection.connected === true) {// if (connection && connection.disconnected === false) {try {const saveredlest = await axlos.dosttalaealttelatels eins55b7","name":"Zoho":"69eOafd25ae490e8d05d8ca2","isTest":false,")2f3a165e3c14d" "externalAppld"."66716516T09:57:59.175Z"."retryAttempts":0,"isDeactivated":false}The object uses connected: true - there is no disconnected field at all. Sothe current check connection.disconnected == false evaluates to undefined == false → false - meaning the POST to /api/v1/integration-app-connect S nevercallled atera successul connection.This is the bug. Fix it in both connect.vue and Onboard.vue:• connect.vu+1-3Now fix the same bug in Onboard. vue:Read Onboard vue #L503-517D Onboard.vue+1 -1Both files fixed. Summary of the bug:The SDK returns connected: true — there is no disconnected fieldconnection.disconnected === false was undefined === false →alwavs talse → resnevertred• Same in Onboard. vue: connection.disconnected === true was undefined === true → always talse → always snowed the error snackoarNow after openNewConnection() resolves with connected: true, the POST /api/v1/integration-app-connect will be called and the SocialAccount will beAsk anything (&*L)+ @ CodeClaude Sonnet 4.6® :QGEA0!O provider_User_token TID provider_refresh_token Tya29.a0Aa7МYirt0MShIzyiTcZHvFQaJJXsV6R7_bFDR5beR601LSmTRkt_-ERT3N-1qХDRpYmKcbLPMd6v05DLy...eVZORIOLANUAMOISLIrSZVIOIKOINCYLnONOnLUNLISTILO CLlonKCMoIMoUO LInUCUTNUOYUS04.eyszanLoLgAwmbawmslstnrslyLoLkninicyctwiandolgcuMedstmepzelotgvunwrnzorkLTewzUKCNDLKNy04.I expires Y17763361761729613615esolution rules in this tlle. Error detalls: Unexpected token =. (25 minutes ago)winasun leamsT61:buir-of 2 spaces...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35875
|
|
35936
|
Project: faVsco.js, menu
PhpStormFileViewNavigateC Project: faVsco.js, menu
PhpStormFileViewNavigateCodeLaravelRefactorToolsWindowHelpProject v› testsO connect.lessV connect.vuedashboardDeallnsightserrorPagesD export-portalextension-installledinvitationonconterenceWa VoUT• LiveCoachD LockedO login› C MeetingConsentO mobileConboard› _mocks_→ ItestsV MobileAppDownk0 Onboard.lessV Onboard vueTs useProvidersSyn‹ondemandplayback• playlistsSettingssharedSoftphoneCoachSyglcons_leaminsights> A composablesolrect ves> M7 helpers> → locales> D pluginsD routerstoreDutilsIs main.jsTs types.d.tsIS types.jsvite= owsersist=enveerauitO.gitignore.yalnic.yml© eslint.config.cjs<> index.htmlO jsconfig.jsonO package.jsonM.README mostats.nimiIs vite.config.jsvarn.lock= yarn-error.log> D lang.node modules library root→ phostan© ReportController.phpC TokenBuilder.php© TeamSetupController.php xphp api.php© SendReportJob.phpC AutomatedReportsCommand.phpAskJiminnykeporscontroller.ono© AutomatedReportsCommandTest.php© AutomatedReportsSendCommand.php© Team.php© AutomatedReportsRepository.php© AutomatedReportsService.php© TrackProviderInstalledEvent.phpC CreateActivityLoggedEvent.phpActivityLoagea.onp= custom.log4 console [EUl= laravel.logconsole PRODA SF [jiminny@localhost]A console [STAGING]<SCrIOTmethods:async integrationAppOnClick() {Loken: ths.crnloken.7):V connect.vue XV Onboard.vueA HS_local [jiminny@localhost]AutomatedRenortscallbackService.ono© RequestGenerateAskJiminnyReportJob.php© RequestGenerateReportJob.php147© AutomatedReportResult.php(C AutomatedReport.phpclass TeamSetupController extends ControllerA4 X2 ^V144public function integrationAppConnect(): JsonResponse/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);209229148/** @var ?SocialAccount $socialAccount */$socialAccount = $user->getSocialAccount($crmProviderKey);if ($socialAccount === null) {$this->logger-›error('[IntegrationApp] Unexpected error. Social account is missing.','team_id' => $team->getId(),'iapR-provider' => $realProviderKey,provider' => $crmProviderKey,]):return response()->json([success → Tause.'message' => 'Something went wrong. Social account is cannot be found.',->setStatusCode( code:JsonResponse: :HTTP_FAILED_DEPENDENCY);160161162164$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);$socialAccount->save):$this->logger→>info('[IntegrationApp] Social account is connected.', [teal1o s sreali-voeclo.'iepR-provider' = $realProviderKey,'provider'=> $crmProviderKey,'state' => SocialAccount::STATE_CONNECTED,[CREDIT_CARD]):$this->eventDispatcher-›dispatch(new SocialAccountConnected($socialAccount));return response()->json(['success' = true,"message" = sprinttlformal. s is successtully connecced,Providers: :getIntegrationAppProviderLabel($realProviderKey)->setStatusCode( code: JsonResponse: :HTTP_ACCEPTED) ;consc conneccion - avalt intecracionapointegration(this.localProvider.name).openNewConnection({showPoweredBy: false,allowMultipleConnections: false,1):console.log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));// [IntegrationApp] openNewConnection resolved: {"id" : "69e0b41a67d0068c2ca0b48e",////////1/////////////1/ 3"name": "Zoho CRM","userId": "1ece66c8-feb1-4df1-b321-21607daf4623","tenantId" : "69e0b3faef3e7b6248189289","isTest" : false,"connected": true,"'state": "READY","errors": [],"integrationId":"66fe6c913202f3a165e3c14d","externalAppId": "6671653e7e2d642e4e41b0fa","authuptlonkey"*"""createdAt": "2026-04-16T10:04:10.420Z" ,"updatedAt": "2026-04-16T10:04:10.575Z","retryAttempts" :0,"isDeactivated" : falseif (connection && connection.connected === true) €// if (connection && connection.disconnected === false) {try {const saveRequest = await axios.post("/api/v1/integration-app-connect",if (saveRequest.data && saveRequest.data.success === true) </** If all is good refresh the page here */window. location = "/dashboard";reLurnrthrow new Error(saveRequest.data.message);} catch (error) {console.log(error);snowshackpartrrorcnormal1zeerror(error):</script>‹style module Lang="less" src="./connect.less"></style>V1 Л VSupport Daily • in 1h 46 mA100% CThu 16 Apr 13:14:34CascadeIntegration App ConneWhether it resolves as null/ undefined if the user closes the modalWhat the value is when OAuth completes successfullyIne key •uestionis wherner connection, dnsconnected === talse sacrualvthe right check — or if the SDK returns something like connection.connected== true (matching what you saw in the network response earlier).alaselttelatelsein5b7","name":"Zohofd25ae490e8d05d8ca2","isTest":false,")2f3a165e3c14d","externalAppld":"66716516T09:57:59.175Z"."retryAttempts":0,"isDeactivated":false}The object uses connected: true - there is no disconnected field at all. Sothe current check connection.disconnected == false evaluates to undefined == false → false - meaning the POST to /api/v1/integration-app-connect S nevercallled anera success connection.This is the bug. Fix it in both connect.vue and Onboard.vue:• connect.vu+1-3Now fix the same bug in Onboard. vue:Read Onboard vue #L503-517D Onboard.vue+1 -1Both files fixed. Summary of the bug:The SDK returns connected: true — there is no disconnected fieldconnection.disconnected === false was undefined === false →alwavs talse → restnevertrec• Same in Onboard.vue: connection.disconnected === true was undefined === true → always talse → always snowed the error snackoarNow after openNewConnection() resolves with connected: true, the POST /api/v1/integration-app-connect will be called and the SocialAccount will bemarked as STATE_CONNECTED.Ask anything (84L)+ ‹> CodeClaude Sonnet 4.6Winasun leamuir-o( 2 spaces...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
35936
|
|
36085
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
iTerm2ShellEditViewSessionScriptsProfilesWindowHelpDOCKER981DEV (docker)882APP (-zsh)./public/vue-assets/assets/GridView-CJVxH4Dg.js/public/vue-assets/assets/ondemand-CBhkAD17.js/public/vue-assets/assets/CrmLink-rTdmxqkp.js/public/vue-assets/assets/liquor-tree-DbetBeVs.js:/public/vue-assets/assets/DealRiskList-BnbcVBB8.js./public/vue-assets/assets/AskAnything-s720pn9E.js./public/vue-assets/assets/lib-BPR1zwwF.js/public/vue-assets/assets/AppFormField-BgVfo6PN.js/public/vue-assets/assets/deal-view-Jn4yJ9Hz.js/public/vue-assets/assets/exports-DIyAIXcT.js:/public/vue-assets/assets/playlists-DpSiCNMr.js./public/vue-assets/assets/callScoringTemplates-DQc-joSr.js../public/vue-assets/assets/_copy0bject-DzIIjTZN.js:/public/vue-assets/assets/pusher-CYYPj3Hn.js/public/vue-assets/assets/onboard-DDojXW3c.jspublic/vue-assets/assets/StatusBadge-BMn_k29a.jspublic/vue-nscets/nssets/kinsk-nxnVorTV.is*[EMAIL] (-zsh)₴4-zshlth)₴526.60kB26.87kB27.91kB30.75kB34.35kB39.49kB39.69kB41.87kB43.21kB47.84kB48.24kB55.13kB61.28kB62.98kB63.05kB64.62kB7957 kR31180.40kB197.96kB210.96kB218.14kB264.94kB298.53kB307.13kB343.99kB367.43kB689.63kB825.14kB1,402.47kB= Support Daily • in 1h 39 mgzip:gzip:gz1p:gzip:9z1p:gz1p:gzip:9z1p:gz1p:gzip:gzip:gzip:gz1p:gzip:9z1p:gz1p:nzin:-zsh10.05kB9.38kB10.18kB9.58kB10.60kB14.98kB12.70kB12.68kB14.34kB16.46kB15.06kB13.28kB20.08kB18.89kB21.83kB22.94kB22 63kR86maр:map:map:map:тар:map:map:map:map:map:map :map:map:map:mар:map:man:A•))100% <8Thu 16 Apr 13:21:03181-zsh92.74kB73.94kB93.18kB78.74 kB115.18kB173.20kB138.34 kB150.73 kB150.62kB294.48kB153.25kB65.85kB239.59kB219.27kB201.39 kB244.72kB30068 KRO 87* Unable to acce...O x8APPN$IPSPhpStorm/public/vue-assets/assets/vuex.esm-bundler-CxmCn-TU.js./public/vue-assets/assets/playback-VJS8X-le.js./public/vue-assets/assets/AppButton-OYq5I1u7.js/public/vue-assets/assets/index.module-DoWLv01P.js/public/vue-assets/assets/intl-tel-input-C4VqCHzY.js/public/vue-assets/assets/team-insights-CrkL2M3g.js:/public/vue-assets/assets/popper-DC--DigQ.js./public/vue-assets/assets/PhoneField-DsfvGNK0.js/public/vue-assets/assets/Live-DHZ3jGjw.js/public/vue-assets/assets/video-js-skin.less_vue_type_style_index_0_src_true_lang-D2hx_saF.js/public/vue-assets/assets/index-DVKeaTSE.js/public/vue-assets/assets/logged-in-layout-B0d2IU06.jsgz1p:67.85kBgzip:61.61kB9z1p:68.66kBgz1p:64.16kB9z1p:60.30kBgz1p:77.20 kBgzip:103.87kBgz1p:84.90kBgzip:97.04kB9z1p:202.81kBgz1p:72.44kBgzip:438.06kB[plugin builtin:vite-reporter](!) Some chunks are largerthan 500 kBafter minification. Consider:Using dynamicimport)tocode-split the applicationUse build.rolldown0ptions.output.codeSplittingto improve chunking: https://rolldown.rs/reference/Output0ptions.codeSplittingAdjust chunk size limit for this warning via build.chunkSizeWarningLimit.• built in 29.74slukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app/front-end (JY-18909-automated-reports-ask-jiminny) $Imap:836.88kBтар :680.92kBmар :3,947.49 kBmap:1,108.20kBmap:475.61kBтар:959.66kBmap.1,245.28kBmap:849.05kBтар :792.41kBmaр:3,016.64 kBmap:436.28kBтар :6,282.82kB...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
36085
|
|
36086
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
PhpStormFileEditViewNavigateCodeLaravelRefactorToolsWindowHelpFV faVsco.js v#11894 on JY-18909-automated-reports-ask-iminny kProject v> D_tests_O connect.lessV connect.vuedashboardDeallnsightserrorPagesD export-portalextension-installledinvitationonconterenceWa VoUT• LiveCoachD LockedO login› C MeetingConsentO mobileConboard› _mocks_→ ItestsV MobileAppDownk0 Onboard.lessV Onboard vueTs useProvidersSyn‹ondemandplaybackplaylistsSettingssharedSoftphoneCoach> D Sygicons_leaminsightscomposaoes> M7 directives> M7 helpers> → locales> D pluginsD routerstoreDutilsIs main.jsTs types.d.tsIS types.jsvite= owsersist=enveerauitO.gitignore.yalnic.yml© eslint.config.cjs<> index.htmlO jsconfig.jsonO package.jsonM.README mostats.nimiIs vite.config.jsvarn.lock= yarn-error.log> D lang.node modules library root› D phpstanC Vite: Can't analyze // vite.config.js: coding© ReportController.phpC TokenBuilder.php© TeamSetupController.php xphp api.php© SendReportJob.phpC AutomatedReportsCommand.phpAskJiminnykeporscontroller.ono© AutomatedReportsCommandTest.php© AutomatedReportsSendCommand.php© Team.php© AutomatedReportsRepository.php© AutomatedReportsService.php© TrackProviderInstalledEvent.phpC CreateActivityLoggedEvent.phpActivityLoagea.onp= custom.log4 console [EUl= laravel.logconsole PROD<SCrIOTmethods:async integrationAppOnClick() {Loken: ths.crnloken.7):A SF [jiminny@localhost]V connect.vue >V Onboard.vueA HS_local [jiminny@localhost]A console [STAGING])AutomatedRenortscallbackservice.onv© RequestGenerateAskJiminnyReportJob.php© RequestGenerateReportJob.php© AutomatedReportResult.php(C AutomatedReport.phpclass TeamSetupController extends ControllerA4 ×2^V144public function integrationAppConnect(): JsonResponse/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);209229148/** @var ?SocialAccount $socialAccount */$socialAccount = $user->getSocialAccount($crmProviderKey);if ($socialAccount === null) {$this->logger-›error('[IntegrationApp] Unexpected error. Social account is missing.','team_id' => $team->getId(),'iapR-provider' => $realProviderKey,provider' => $crmProviderKey,]):return response()->json([success → Tause.160'message' => 'Something went wrong. Social account is cannot be found.',161162->setStatusCode( code:JsonResponse::HTTP_FAILED_DEPENDENCY);164$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);$socialAccount->save):$this->logger→>info('[IntegrationApp] Social account is connected.', [teal1o s sreali-voeclo.'iepR-provider' = $realProviderKey,'provider'=> $crmProviderKey,'state' => SocialAccount::STATE_CONNECTED,[CREDIT_CARD]):$this->eventDispatcher-›dispatch(new SocialAccountConnected($socialAccount));return response()->json(['success' = true,"message" = sprinttlformatl. s 1s suecesstully connectedProviders: :getIntegrationAppProviderLabel($realProviderKey)->setStatusCode( code: JsonResponse: :HTTP_ACCEPTED) ;consc conneccion - avalt intecracionapointegration(this.localProvider.name).openNewConnection({showPoweredBy: false,allowMultipleConnections: false,1):console.log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));// [IntegrationApp] openNewConnection resolved: {"id" : "69e0b41a67d0068c2ca0b48e",//"name": "Zoho CRM",//"userId": "1ece66c8-feb1-4df1-b321-21607daf4623","tenantId" : "69e0b3faef3e7b6248189289","isTest" : false,//"connected": true,//"'state": "READY",1///"errors": [],"integrationId":"66fe6c913202f3a165e3c14d","externalAppId": "6671653e7e2d642e4e41b0fa",//"authuptlonkey"*""////"createdAt": "2026-04-16T10:04:10.420Z" ,"updatedAt": "2026-04-16T10:04:10.575Z",//"retryAttempts" :0,//"isDeactivated" : false1/ 3if (connection && connection.connected === true) {// if (connection && connection.disconnected === false) {try {const saveRequest = await axios.post("/api/v1/integration-app-connect",if (saveRequest.data && saveRequest.data.success === true) </** If all is good refresh the page here */window. location = "/dashboard";reLurnrthrow new Error(saveRequest.data.message);} catch (error) {console.log(error);snowshackpartrrorcnormal1zeerror(error):</script>‹style module Lang="less" src="./connect.less"></style>n rules in this tlle. Error detalls: Unexpected token =. (32 minutes ago,V1 Л VSupport Daily • in 1h 39 mAL AutomatedReportsCommandTest100% 145Thu 16 Apr 13:21:04CascadeIntegration App ConneWhether it resolves as null / undefined if the user closes the modallWhat the value is when OAuth completes successfullywIne key •uestionis wherner connection, dnsconnected === talse sacrualvthe right check — or if the SDK returns something like connection.connected== true (matching what you saw in the network response earlier).alaselttelatelsein5b7","name":"ZohoeOafd25ae490e8d05d8ca2","isTest":false,")2f3a165e3c14d" "externalAppld"."66716516T09:57:59.175Z"."retryAttempts":0,"isDeactivated":false}The object uses connected: true - there is no disconnected field at all. Sothe current check connection.disconnected == false evaluates to undefined == false → false - meaning the POST to /api/v1/integration-app-connect S nevercallled anera success connection.This is the bug. Fix it in both connect.vue and Onboard.vue:• connect.vu+1-3Now fix the same bug in Onboard. vue:Read Onboard vue #L503-517D Onboard.vue+1 -1Both files fixed. Summary of the bug:The SDK returns connected: true — there is no disconnected fieldconnection.disconnected === false was undefined == false →alwavs talse → restnevertrec• Same in Onboard.vue: connection.disconnected === true was undefined === true → always talse → always snowed the error snackoarNow after openNewConnection() resolves with connected: true, the POST /api/v1/integration-app-connect will be called and the SocialAccount will bemarked as STATE_CONNECTED.Ask anything (884L)+ ‹› CodeClaude Sonnet 4.6winasun leamsT61:buir-of 2 spaces...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
36086
|
|
36296
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
PhpStormFileFV faVsco.jsProject vEdit> D_tests_O connect.lessV connect.vuedashboardDeallnsightserrorPages0 export-portalextension-installledinvitationvonconterenceMlavout• LiveCoachD LockedO login› C MeetingConsentO mobileConboard› _mocks_•U_tests_V MobileAppDownk1 Onboard.lessV Onboard vueTs useProvidersSyn‹ondemandplayback• playlistsSettingsO sharedSoftphoneCoach> D Sygicons_leaminsightscomposaoes> M directives> M7 helpers> locales> D pluginsD routerstoreDutilsIs main.jsTs types.d.tsIS types.jsvite= owsersist=enveerauitO.gitignore.yalnic.yml© eslint.config.cjs<> index.htmlO jsconfig.jsonO package.jsonM.README mostats.nimiIs vite.config.jsvarn.lock= yarn-error.log> D lang.node modules library root› D phpstanViewNavigateCodeLaravelRefactorToolsWindowHelp#11894 on JY-18909-automated-reports-ask-jiminny k© ReportController.phpC TokenBuilder.php© SendReportJob.php© AskJiminnyReportsController.php©TeamSetupController.phpxpip alione© AutomatedReportsCommandTest.phpAulomaleakeporissendcommand.ong© AutomatedReportsCommand.phpC Team.php© AutomatedReportsRepository.php© CreateHeldActivityEvent.php© TrackProviderInstalledEvent.php© CreateActivityLoggedEvent.phpUserPilotActivityListener.php© ActivityLogged.php© AutomatedReportsCallbackService.phpC RequestGenerateAskJiminnyReportJob.phpC RequestGenerateReportJob.phpC AutomatedReportResult.phpAutomaleakeportonoclass TeamSetupController extends Controllerououc runcmion anreoram onkoouonnect ysonresoonse186197198209228229251->setStatusCode ( code:JsonResponse: :HTTP_BAD_REQUEST);/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);/** @var ?SocialAccount $socialAccount */$socialAccount = Suser->getSocialAccount($crmProviderKey);if ($socialAccount === null) €$this->logger-›error('[IntegrationApp] Unexpected error. Social account is missing.', ['team_id' => $team->getid),'LaRp-provider' => $realProviderkey,provider' => $crmProviderKey,1);return response()->json(['success'=> false,'message''Something went wrong. Social account is cannot be found.',-sersraruscode codeJsonResponse: :HTTP_FAILED_DEPENDENCY);$socialAccount->setAttribute('state', SocialAccount: :STATE_CONNECTED):$socialAccount->save();$this->logger→>info('[IntegrationApp] Social account is connected.', ['team_id' => $team->getId(),'iapR_provider' => $realProviderKey,'provider'= $crmProviderKey,"state" = SoclaLAccoUnt..SIAIE_CUNNECIED,nunus-veventusoarcher-yo.soauch.nelsoca.rccountconnecteonsocauaccountereturn response()->json([success' = true,'message' => sprintf(format: "%s is successfully connected",Providers: :getIntegrationAppProviderLabel($realProviderKey)->setStatusCode( code: JsonResponse: :HTTP_ACCEPTED) ;A4/2 л= custom.logE laravel.logA SF ljiminny@localhost]< console SlAGING<SCrI0Dmethods:async integrationAppOnCZick() {Loken: ths.crnloken.7):145144145160161167168169170171const conneccion - avalt incecracionaoeintegration(this.localProvider.name)-openNewConnection({showPoweredBy: false,allowMultipleConnections: false,H):console. log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));// [IntegrationApp] openNewConnection resolved: {"id"': "69e0b41a67d0068c2ca0b48e",1///"name":"Zoho CRM","userId": "1ece66c8-feb1-4df1-b321-21607daf4623","tenantId" : "69e0b3faef3e7b6248189289",//"isTest" : false,"connected": true,"state": "READY",//"errors": [],////"integrationId":"66fe6c913202f3a165e3c14d", |"externalAppId": "6671653e7e2d642e4e41b0fa","authOptionKey":"",//1/1/1/ 3"createdAt": "2026-04-16T10:04:10.420Z" ,"updatedAt": "2026-04-16T10:04:10.575Z","retryAttempts" :0,"isDeactivated" : falseif (connection && connection.connected === true) {// if (connection & connection.disconnected === false) {try{const saveRequest = await axios.post("/api/v1/integration-app-connect",if (saveRequest.data && saveRequest.data.success === true) «/** If all is good refresh the page here *window.location = "/dashboard";reLurinthrow new Error(saveRequest.data.message);} catch (error) {console.log(error);snowsnackbarerror.normau1zetrror(error)),liblSupport Daily • in 1h 32 mAutomatedReportsCommandTestv100% [45lThu 16 Apr 13:28:41V connect.vue xV Onboard.vueA HS_local [jiminny@localhost]Al console [EU]A console [PROD]m V1л18518d104190191192193194</script>«style module lang="less" src="./connect.less"></style>ted token =. (40 minutes ago)winasun leams 161:uir-of 2 spaces...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
36296
|
|
36297
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
FirefoxFileEditViewHistoryBookmarksProfilesToolsWindowHelp(aolAPP (-zsh)DOCKER• 881DEV (docker)82APP (-zsh)X3ec2-user@ip-10-30-...₴4../public/vue-assets/assets/GridView-CJVxH4Dg.js./public/vue-assets/assets/ondemand-CBhkAD17.js../public/vue-assets/assets/CrmLink-rTdmxqkp.js./public/vue-assets/assets/liquor-tree-DbetBeVs.js./public/vue-assets/assets/DealRiskList-BnbcVBB8.js../public/vue-assets/assets/AskAnything-s720pn9E.js:/public/vue-assets/assets/lib-BPR1zwwF.js./public/vue-assets/assets/AppFormField-BgVfo6PN.js../public/vue-assets/assets/deal-view-Jn4yJ9Hz.js../public/vue-assets/assets/exports-DIyAIXcT.js../public/vue-assets/assets/playlists-DpSiCNMr.js../public/vue-assets/assets/callScoringTemplates-DQc-joSr.js../public/vue-assets/assets/_copy0bject-DzIIjTZN.js./public/vue-assets/assets/pusher-CYYPj3Hn.js./public/vue-assets/assets/onboard-DDojXW3c.js../public/vue-assets/assets/StatusBadge-BMn_k29a.js./public/vue-assets/assets/kiosk-nxpVorIV.js./public/vue-assets/assets/deal-insights-D5sbo4zZ.js../public/vue-assets/assets/ListView-D1HYjAvt.js../public/vue-assets/assets/_plugin-vue_export-helper-sSs0rPyg.js./public/vue-assets/assets/WelcomeLayout-B2BjjI5T.js:./public/vue-assets/assets/dashboard-CDcAQG1E.js../public/vue-assets/assets/emoji-input-D_ee3_TC.js../public/vue-assets/assets/sentry-h1XGLinV.js../public/vue-assets/assets/OrgSettingsLayout-1YAa0isa.js../public/vue-assets/assets/vuex.esm-bundler-CxmCn-TU.js../public/vue-assets/assets/playback-VJS8X-le.js./public/vue-assets/assets/AppButton-OYq5I1u7.js../public/vue-assets/assets/index.module-DoWLv01P.js../public/vue-assets/assets/intl-tel-input-C4VqCHzY.js../public/vue-assets/assets/team-insights-CrkL2M3g.js../public/vue-assets/assets/popper-DC--DigQ.js../public/vue-assets/assets/PhoneField-DsfvGNK0.js•/public/vue-assets/assets/live-DHZ3jGjw.js./public/vue-assets/assets/video-js-skin.less_vue_type_style_index_0_src_true_lang-D2hx_saf.js../public/vue-assets/assets/index-DVKeaTSE.js../public/vue-assets/assets/logged-in-layout-B0d2IU06.js-zsh• 28526.60kB26.87kB27.91kB30.75kB34.35kB39.49kB39.69kB41.87kB43.21kB47.84kB48.24kB55.13kB61.28kB62.98kB63.05kB64.62kB79.57kB94.84kB115.66kB117.59kB120.68 kB128.67kB129.28kB164.28 kB176.44kB180.40kB197.96kB210.96kB218.14kB264.94kB298.53kB307.13kB343.99kB367.43kB689.63kB825.14kB1,402.47kB[plugin builtin:vite-reporter](!) Some chunks are larger than 500 kBafter minification. Consider:- Using dynamic import() to code-split the application- Use build.rolldownOptions.output.codeSplittingto improve chunking: https://rolldown.rs/reference/Output0ptions.codeSplitting- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.• built in 29.74slukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app/front-end (JY-18909-automated-reports-ask-jiminny) $ISupport Daily - in 1h 32 m-zshgzip:10.05kBgzip:9.38kBgz1p:10.18kBgzip:9.58kB9z1p:10.60kBgz1p:14.98kBgzip:12.70kB9z1p:12.68kBgzip:14.34kBgzip:16.46kBgzip:15.06kBgzip:13.28kBgz1p:20.08kBgzip:18.89kB9z1p:21.83kBgz1p:22.94kBgzip:22.63kB9z1p:28.17kBgzip:33.76kB9z1p:38.70 kB921p:34.16kBgzip:40.04kBgz1p:36.72kBgzip:52.24 kB9z1p:56.16kBgz1p:67.85kBgzip:61.61kB9z1p:68.66kBgz1p:64.16kB9z1p:60.30kBgzip:77.20 kBgzip:103.87kBgz1p:84.90kBgzip:97.04kBgzip: 202.81kBgz1p:72.44kBgzip: 438.06kB86-zshmaр:92.74kBmap:73.94kBmap:93.18kBтар :78.74kBтар:115.18kBmap:173.20kBтар :138.34kBтар:150.73 kBmap:150.62kBmaр:294.48kBтар:153.25kBmaр:65.85kBmap:239.59kBтар :219.27kBmар:201.39kBmap:244.72kBтар :300.68kBтар :292.79kBmap:308.10kBmaр:500.60kBтар:258.56kBmaр:410.48kBmap:266.15kBтар :831.82 kBтар:623.70kBmap:836.88kBтар :680.92kBmар :3,947.49 kBmap:1,108.20kBmap:475.61kBтар:959.66kBmap:1,245.28kBmap:849.05kBтар :792.41kBmар: 3,016.64 kBmap:436.28kBmaр: 6,282.82kB100% <47O 878Thu 16 Apr 13:28:43181* Unable to acce...O x8APP...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
36297
|
|
36310
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
PhpStormFileFV faVsco.jsProject vEditSlackController.phpSupportController.pt© TeamSetupControlle© UserAutomatedRepc© WelcomeController.f→ Middleware>_Requests> M Resources> M Responses> M7 Serializers> D Transformers© Kernel.php€ PlaylistTrackResourceTG ValidateCrmConnection> D Integrations› D Interactionsv |Hobs> D Activity> M AiAutomationD AjReports> D Audiov M AutomatedReports(C) KecuestGeneraTeAS!© RequestGenerateRej© SendReportJob.php© SendReportMailJob.|> C Calendar>D ermDealRisks• Malloox> MMeetingBot> M Middleware> M Streaming> MTeam› _ TelephonyDUser© BaseProcessingJob.php© DummyJob.php© ImportRecallAlRecordin!© ImportRemoteTrackJob© Job.php© JobDispatcher.php• JobDispatcherInterface© PurgeSoftDeletedOppolG SqsVisibilityControl.php> D Listeners> D Mailv D Models› D Activity> DAI> D AskAnything> D Calendar> D Connection> M Contracts> D Crm> D ElasticSearch> FeatureD OpportunityD ParticipantViewNavigateCodeLaravelRefactorToolsWindowHelp#11894 on JY-18909-automated-reports-ask-jiminny© ReportController.phpC TokenBuilder.phpphp api.php© SendReportJob.php© AskJiminnyReportsController.php© TeamSetupController.php x© AutomatedReportsCommandTest.phpAulomaleakeporissendcommand.ong© AutomatedReportsCommand.phpC Team.php© AutomatedReportsRepository.php© CreateHeldActivityEvent.php© TrackProviderInstalledEvent.php© CreateActivityLoggedEvent.phpUserPilotActivityListener.php© ActivityLogged.phpC RequestGenerateAskJiminnyReportJob.phpC RequestGenerateReportJob.phpC AutomatedReportResult.phpAutomaleakeportonoclass TeamSetupController extends ControllerpubLie tunccion incegrattonAppconnectl. Jsonkesponse->setStatusCode ( code:JsonResponse: :HTTP_BAD_REQUEST);192193197198199223225227228/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);/** @var ?SocialAccount $socialAccount */$socialAccount = Suser->getSocialAccount($crmProviderKey);if ($socialAccount === null) {$this->logger-›error('[IntegrationApp] Unexpected error. Social account is missing.', ['team_id' = $team->getId,'LaRp-provider' => $realProviderKey,provider' => $crmProviderKey,1);return response()->json(['success' => false,'message' =>'Something went wrong. Social account is cannot be found.',->setStatusCode( code: JsonResponse: :HTTP_FAILED_DEPENDENCY) ;#socialAccount->setAttribute('state', SocialAccount: :STATE_CONNECTED);$socialAccount->save();$this->logger→>info('[IntegrationApp] Social account is connected.', ['team_id' => Steam->getId(),'iapR_provider' => $realProviderKey,'provider'= $crmProviderKey,"state" = SoclaLAccoUnt..SIAIE_CUNNECIED,nunus-veventusoarcher-yo.soauch.nelsoca.rccountconnecteonsocauaccountereturn response()->json([success' = true,'message' => sprintf(format: "%s is successfully connected",Providers::getIntegrationAppProviderLabel($realProviderKey)->setStatusCode( code: JsonResponse: :HTTP_ACCEPTED) ;loblj Support Daily - in 1h 31mAutomatedReportsCommandTestv100% [Z5lThu 16 Apr 13:29:08V connect.vue xV Onboard.vueA HS_local [jiminny@localhost]Al console [EU]A console [PROD]A4 X2 ^= custom.log= laravel.logA SF ljiminny@localhost]< console SlAGING<SCrI0Dmethods:async integrationAppOnCZick() {Loken: ths.crnloken.7):144145160161162104167168169170171180181182183184185186const conneccion - avalt incecracionaoeintegration(this.localProvider.name)-openNewConnection({showPoweredBy: false,allowMultipleConnections: false,H):console. log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));// [IntegrationApp] openNewConnection resolved: {"id"': "69e0b41a67d0068c2ca0b48e",1/"name":"Zoho CRM","userId": "1ece66c8-feb1-4df1-b321-21607daf4623","tenantId" : "69e0b3faef3e7b6248189289",//"isTest" : false,"connected": true,"state": "READY",//"errors": [],////"integrationId": "66fe6c913202f3a165e3c14d","externalAppId": "6671653e7e2d642e4e41b0fa","authOptionKey":"",////1/1/ 3"createdAt": "2026-04-16T10:04:10.420Z" ,"updatedAt": "2026-04-16T10:04:10.575Z","retryAttempts" :0,"isDeactivated" : falseif (connection && connection.connected === true) {// if (connection & connection.disconnected === false) {try{const saverequest = awalt axios.posc"/api/v1/integration-app-connect",if (saveRequest.data && saveRequest.data.success === true) «/** If all is good refresh the page here *window.location = "/dashboard";reLurinthrow new Error(saveRequest.data.message);} catch (error) {console.log(error);snowsnackbarerror.normau1zetrror(error)),18d104190191192193194</script>«style module lang="less" src="./connect.less"></style>tea token =. (40 minutes ago)winasun leamsuir-o( 4 spaces...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
36310
|
|
36665
|
Project: faVsco.js, menu
PhoStormEditViewNavigateC Project: faVsco.js, menu
PhoStormEditViewNavigateCodelaravellRefactonRunlToolsWindowHeloDMsAchivityFilesMoreJiminny ...= Unreadse) Threads6d Huddles012• Drafts & sent:8 DirectoriesEh External connections* Starred@ liminny-x-integrati..platform-inner-team(# Channels# ai-chapten# alerts# backends contlicion-clnid# curiosity lab# engineering# frontendi# general# infra-changes#: liminny-bg# platrorm-uckets#: product launchesac random#: releases# sofa-ofhce# supportac thank-vous# the people of iimi....Direct messages• Nikolay Nikolov€. Vasil.... Galva DimitrovaNikolay Ivanov0 Aneliva Angelova3 Aneliya Angelova, ..Stoyan Tanev5- VegStelivan Georgiev3 Adelina Petrova, Ili..Adelina Petrova#:Apps6 d Huddle with Vasil VasilevSearch Jiminny Inc0. Nikolay NikolovMessages4P FilesChanges:~ 4 new messages• Return talse to skip remote engagement update with a secondary activity dataComments•jiminny/app|Apr 8th Added by GitHubThursday, April 9thvNikolay Nikolov 10:50 AMOautha mi изгърмя: [URL_WITH_CREDENTIALS] в дооде с негоИзкарва ми бутон за Recoverrelo he ree caraneMessage Nikolay NikolovAaQV- Al Notes: OfiLukas KovalikScreen shareAl Notes: OffLeave&alalSuooort Dailv . in 1h 9m100% 41• Thu 16 Aor 13:52:01nd Huddle with Vasil Vasileyw Hep →0Al Support Daty- in1h9m A 5Leave...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
36665
|
|
36672
|
PhoStormEditViewNavigateCodelaravellRefactonRunlTo PhoStormEditViewNavigateCodelaravellRefactonRunlToolsWindowHeloDMsAchivityFilesMoreJiminny ...= Unreadse) Threads6d Huddles012• Drafts & sent:8 DirectoriesEh External connections* Starred@ liminny-x-integrati..platform-inner-team(# Channels# ai-chapten# alerts# backends contlicion-clinia# curiosity lab# engineering# frontendi# general# infra-changes#: liminny-bg# platrorm-uckets#: product launchesac random#: releases# sofa-ofhce# supportac thank-vous# the people of iimi....Direct messages• Nikolay Nikolov€. Vasil.... Galva DimitrovaNikolay Ivanov0 Aneliva Angelova3 Aneliya Angelova, ..Stoyan Tanev5- VegStelivan Georgiev3 Adelina Petrova, Ili..Adelina Petrova#:Apps6 d Huddle with Vasil VasilevSearch Jiminny Inc0. Nikolay NikolovMessages4P FilesChanges:~ 4 new messages• Return talse to skip remote engagement update with a secondary activity dataComments•jiminny/app|Apr 8th Added by GitHubThursday, April 9thvNikolay Nikolov 10:50 AMOautha mi изгърмя: [URL_WITH_CREDENTIALS] в дооде с негоИзкарва ми бутон за Recoverreno ha ree caraneMessage Nikolay NikolovAaQV- Al Notes: OfiLukas KovalikScreen shareAl Notes: OffLeave&Suooort Dailv . in 1h 8m100% 4• Thu 16 Apr 13:52:32nd Huddle with Vasil Vasileyow Hep →0 Al Support Daly• in1h8m A 5Leave...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
36672
|
|
36679
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
1
Previous Highlighted Error
Next Highlighted Error
<template>
<WelcomeLayout
title="Account disconnected"
textPosition="center"
:icon="faUnlink"
:class="$style.layout"
>
<div :class="$style.container" v-if="providersLoaded">
<p>
<strong>
It looks like your {{ localProvider.displayName }} account has become
disconnected
</strong>
</p>
<p :class="$style.small">Please re-connect to continue</p>
<p v-if="isInIframe">
We'll open the {{ localProvider.displayName }} authentication in a new
tab. Please return here and refresh the page once complete
</p>
<GoogleLikeButton
v-if="localProvider.viaIntegrationApp && crmTokenLoaded"
as="a"
:key="localProvider.name"
:brand-logo="localProvider.name"
:class="$style.connectButton"
@click="integrationAppOnClick"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
<GoogleLikeButton
v-if="!localProvider.viaIntegrationApp"
as="a"
:key="localProvider.name"
:href="`/auth/redirect/${localProvider.name}`"
:target="target"
:brand-logo="localProvider.name"
:class="$style.connectButton"
>
Sign in with {{ localProvider.displayName }}
</GoogleLikeButton>
</div>
<BuildInfo />
<KioskBanner />
</WelcomeLayout>
</template>
<script>
import window from "window";
import axios from "axios";
import { faUnlink } from "@fortawesome/pro-regular-svg-icons";
import isInIframe from "@/utils/isInIframe";
import BuildInfo from "@/components/layout/BuildInfo/BuildInfo.vue";
import KioskBanner from "@/components/shared/KioskBanner/KioskBanner.vue";
import WelcomeLayout from "@/components/layout/WelcomeLayout/WelcomeLayout.vue";
import GoogleLikeButton from "@/components/shared/Buttons/GoogleLikeButton.vue";
import { showSnackbarError, normalizeError } from "@/utils/index";
import { IntegrationAppClient } from "@integration-app/sdk";
export default {
name: "ConnectPage",
components: {
BuildInfo,
KioskBanner,
WelcomeLayout,
GoogleLikeButton,
},
data() {
return {
...window.connectData,
crmToken: null,
faUnlink,
isInIframe,
providers: [],
providersLoaded: false,
crmTokenLoaded: false,
};
},
computed: {
localProvider() {
return this.providers.find((e) => e.name === this.provider);
},
target() {
return this.isInIframe ? "_blank" : null;
},
},
created() {
this.getProviders();
},
mounted() {
this.showErrors();
},
watch: {
providersLoaded() {
if (this.providersLoaded) {
this.prepareIntegrationAppConnection();
}
},
},
methods: {
showErrors() {
if (!this.error) return;
showSnackbarError(this.error, undefined, undefined, false);
},
unwrapEntityResponse({ data }) {
return data.map(({ icon, name, displayName, viaIntegrationApp }) => {
return { icon, name, displayName, viaIntegrationApp };
});
},
async getProviders() {
try {
const response = await axios.get("/api/v1/connect-providers");
this.providers = this.unwrapEntityResponse(response);
this.providersLoaded = true;
} catch {
showSnackbarError(
"An error occurred, while loading form data (connect providers).",
);
}
},
async prepareIntegrationAppConnection() {
if (this.localProvider.viaIntegrationApp) {
try {
const response = await axios.get("/api/v1/integration-app-token");
this.crmToken = response.data.token;
this.crmTokenLoaded = true;
} catch (error) {
console.log(error);
showSnackbarError(
`An error occurred while preparing the page.
Try refreshing, if the error persists get in touch with the Jiminny team.`,
);
}
}
},
async integrationAppOnClick() {
const integrationApp = new IntegrationAppClient({
token: this.crmToken,
});
const connection = await integrationApp
.integration(this.localProvider.name)
.openNewConnection({
showPoweredBy: false,
allowMultipleConnections: false,
});
console.log('[IntegrationApp] openNewConnection resolved:', JSON.stringify(connection));
// [IntegrationApp] openNewConnection resolved: {
// "id":"69e0b41a67d0068c2ca0b48e",
// "name":"Zoho CRM",
// "userId":"1ece66c8-feb1-4df1-b321-21607daf4623",
// "tenantId":"69e0b3faef3e7b6248189289",
// "isTest":false,
// "connected":true,
// "state":"READY",
// "errors":[],
// "integrationId":"66fe6c913202f3a165e3c14d",
// "externalAppId":"6671653e7e2d642e4e41b0fa",
// "authOptionKey":"",
// "createdAt":"2026-04-16T10:04:10.420Z",
// "updatedAt":"2026-04-16T10:04:10.575Z",
// "retryAttempts":0,
// "isDeactivated":false
// }
// if (connection && connection.connected === true) {
if (connection && connection.disconnected === false) {
try {
const saveRequest = await axios.post(
"/api/v1/integration-app-connect",
);
if (saveRequest.data && saveRequest.data.success === true) {
/** If all is good refresh the page here */
window.location = "/dashboard";
return;
}
throw new Error(saveRequest.data.message);
} catch (error) {
console.log(error);
showSnackbarError(normalizeError(error));
}
}
},
},
};
</script>
<style module lang="less" src="./connect.less"></style>
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
36679
|
|
36694
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
DMsAchivityFilesMoreJiminny ...= Unreadse) Threads6d Huddles012• Drafts & sent:8 DirectoriesEh External connections* Starred@ iminny-x-integrati..platform-inner-team(# Channels# ai-chapten# alerts# backends contlicion-clnid# curiosity lab# engineering# frontendi# general# infra-changes#: liminny-bg# platrorm-uckets#: product launchesac random#: releases# sofa-ofhce# supportac thank-vous# the people of iimi....Direct messages• Nikolay Nikolovo Vasil.... Galva DimitrovaNikolay Ivanov0 Aneliva Angelova3 Aneliya Angelova, ..Stoyan Tanev5- VegStelivan Georgiev3 Adelina Petrova, Ili..Adelina Petrova#:Appshd Huddle with Vasil VasileySearch Jiminny Inc2 0. Nikolay NikolovMessages4P FilesChanges:~ 4 new messages• Return talse to skip remote engagement update with a secondary activity dataComments•jiminny/app|Apr 8th Added by GitHubThursday, April 9thvNikolay Nikolov 10:50 AMOautha mi изгърмя: [URL_WITH_CREDENTIALS] в дооде с негоИзкарва ми бутон за Recoverrelo he ree caraneMessage Nikolay NikolovAaQV- Al Notes: OfiLukas KovalikScreen shareAl Notes: OffLeave&lohlSuooort Dailv . in 1h.6mA100% 4• Thu 16 Aor 13:54:07nd Huddle with Vasil Vasileyw Hep →0Al Support Daty- in 1h6m A 5Leave...
|
PhpStorm
|
faVsco.js – ~/jiminny/app/front-end/src/components faVsco.js – ~/jiminny/app/front-end/src/components/connect/connect.vue...
|
NULL
|
36694
|