|
47686
|
1010
|
54
|
2026-04-17T11:55:24.027960+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-17/1776 /Users/lukas/.screenpipe/data/data/2026-04-17/1776426924027_m2.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Firefox FileEoitViewHistory BookmarksProfilesTools Firefox FileEoitViewHistory BookmarksProfilesToolsWindow Help•••)• -° app.dev.jiminny.com/onboardDevelopers | HubSpotM'inbox (1,576) - lukas.kovalik@jiminM 120216 is your HubSpot Log In CodFa CloudWatch | eu-west-1New Tabz Configure SSH access to multiple. fix-cache-for-business-processes4 [JY-20692] Issue with reconnectirg JiminnyAuth ProxyDashooard • Jiminny • MemoraneApo " 4oho CkM" • Jiminny • Meme+ New TabJIMInNYUpdate your informationGENERALTIMEZONDEurope/Sofia (UTC +03:00)LANGUAGES SPOKEN DURING CALLSDEFAULT SPOKEN LANGUAGEEnglish (United Kingdom)If the language isn't detected we'll default to this oneAdd languageCONNECT/SYNC SETTINGSConnect Zoho CRMSign in with Zoho CRMImport CalendarMeetingsSign in with GoogleLet's Get Started!Support Daily - in 5mA100% CS•Fri 17 Apr 14:55:23InspectorP Integr• ConsoleD DebuggerN Network{) Style EditorA Performance: Memory& Storagei Accessibility88 Application1706 hidden &3Errors Warnings Info Logs Debug© 22CSSneyuosts© GET https://ui.integration.app/assets/RefreshConnectionPopup- TE0hDXU.csgGET https://ui.integration.app/assets/Routes-CDlw4D3f.is• GEl https://u1.integration.app/assets/1ndex-/Ka1N6p0.19uoct mueosaeauee encetratson,age assets ettela toncoшect onguаle-crvoлso.® GET https://ui.integration.app/assets/ResetButton-B5vGAh3h.is•oel nicos:u1, incedracion.app/assers/1ndex-vooyuonr.g© GET https://ui.integration.app/assets/Routes-DHLymKVa.css® GET https://ui.integration.app/assets/flow-instance-context-CJr s6BC.isGlH3200 OnsIHTTP/3 200)HIIP/S 269[HTTP/3 (200 0ms][HTTP/3 (200) 0mslMlIr3 200IНTTP/3 200ІНTTP/3 200]MlIrS 200oel aetosae aptate temorane, coш acetd сдouszonoetUrtions aLepsae dod, deulellotale, com antegta Ltons cолoct• GET https://static.integration.app/connectors/zoho-crm/logo.pnghttps://static.1nteqration.app/connectors/zoho-crm/logo.onghttps://static.integration.app/files/d8bde402-9a7b-4627-bf08-ab2cfcdc4a43.pngGET https://api.getmembrane.com/integrations/zohocrmJPllons ntcos:ap1.cecmenbrane.com 1ncecrations zonochmotl nctos:ap1.qecmembrane.com/1ncecracions/bbreocy15z0zt5albsesc140OPTIONS https://api.getmembrane.com/integrations/66fe6c913202f3a165e3c14dIHTTP/3.200 179ms]IHTTP/2 204 46msl[HTTP/2. 20074mslHUTP/1.1 200 OK Oms.IHTTP/3.(200 27msl[HTTP/3 (200)91mslMIIF3: 20445ms]MlIr3 200yyms[HTTP/3_(204 59mslIHTTP/3.200 173msl[HTTP/3 204 46msl[IntegrationAppl onBoard openNewConnection resolved: nullonboard-BENebgvU.is:12:32191inteoratzonAvo connection condition matchedonboard-b=vebawu.1s:12:32324Navigated to https://app.dev.jiminny.com/onboard® GET https://ui.integration.app/embed/integrations/zohocrm/connect?token=ey]0eXAi0¡JKV1QiLCJhbGci0iJIUz[1NiJ9.eyJpZCI6IjFLY2U2NmM4LWZLYjEtNGRmMS1iMzIxLTIxNjA3ZGFmNDYyMyIsIm5hbWUi0i.. [HTTР/2_ (200 94msl0GEl https://u1.lntegration.app/assets/1ndex-DCou JW.1SHITPS 200• GET https://ui.integration.app/assets/index-DQ2tm1dS.cssIHTTP/3 200236ms]® None of the "sha384" hashes in the integrity attribute match the content of the subresource at "https://fonts.googleapis.com/css22family=IBM+Plex+Serifsdisplay=swap". The computed hash is"oxprsPitevorxso/owensool tooxiKklcsrPc Wiy ruMEwk alNd LOUMe oN".connect© GET https://ui.integration.app/assets/index-DRKPh6L6.igIHTTP/3200 0ms]GET https://ui.integration.app/assets/RefreshConnectionPopup-P6RvdGCd.is[HTTP/3 200 0mslGEl https://w1.integratzon.app/assets/RefreshConnectzo0Popup-1EUhDxU.cssHIIP/S 269• GET https://ui.integration.app/assets/Routes-CDWw4D3f.is[HTTP/3 (200 0ms]• GET https://ui.integration.app/assets/index-7Ka1N6pQ-is[HTTP/3 200 0msl• GEl https://u1.lntegration.app/assets/Inteqratzonconnectzonguard-LarvoH9g.1sHIIP/S 269• GET https://ui.integration.app/assets/ResetButton-B5vGAh3h.is[HTTP/3 (200 0ms]® GET https://ui.integration.app/assets/index-Dg8yQbmF.is[HTTP/3 (200O gel nicosiluh, ancegracion.app/assets/uLoweinstance-contextelr Sobl.lsMlIrS 200• GET https://ui.integration.app/assets/Routes-DHLymKVa.css[HTTP/3200) 0ms]© GET https://ui.integration.app/assets/index-BhMG5VAy.cssIHTTP/3 200 0mslMIIr3 200 14/mS.© XHR OPTIONS https://api.getmembrane.com/integrations/zohocrm[HTTP/2 (204 46mslGET https://static.integration.app/connectors/zoho=crm/logo.png[HTTP/2 200 60mslO GEUhttps:/static.1nteqration.app/connectors/zoho-crm/logo.ong• GET https://static.integration.app/files/d8bde402-9a7b=4627-bf08-ab2cfcdc4a43.pngIHTTP/3.(200 26ms]O XHR GET https://app.dev.jiminny.com/api/v1/integration-app-token[HTTP/2 200 6543msl* Navigated to https://app.dev.jiminny.com/onboard© Navigated to https://app.dev.jiminny.com/auth/redirect/google?redirectUrl=https&3A&2F%2Fapp.dev.jiminny.com$2Fonboard© GET https://accouopendo ellast protdle netpsabawww.togleapis.com/auth/calendar https://www.googleapis.com/auth/gmail.readonly&access type=ofIHTTP/2_302_408ms]* Navigated to https://accounts.google.com/signin/oauth/consent?as=S237994199%3A1776426897216672&authuser=1&cLient_id=827025697740-bFGIOiALelzFH9zP097HF7B85bBBWDi2Tpy7s7s-2umM0mmP9uaiVQsruEx20cii4Z3KPol-8S8z 6COhk166VVhrkTPTFPY.75Wk7F041SXLZLAWWDgebHKOBJ11g-2msswxolsBn424VocecLooLMLasxe2J04F2WHBJOuLUCgLBAyVLmSSSOCKOEWYZLSZrOUSSJOD/AULVGnULKSGUPVWKELCKULOEDVzKBGOSXPSUCo-kogtc4ontTreognonz-1NGrt5T-AuKwegne c.cc9AaronZo4enwnggn-a_owensosynNcuoouNoP xekWainTo4axsTTFAcycwcwuSzLz DTeNZOnT UyU-BSAL770420090541UGCc6FJpfAIYEwog573a0A9plyi1byqNB&rapt=AEjHL4NsQari2msKLi2I0YOCLSEnKDOWULONIESNZYOMY4UTYUNLL34NLISLCUYARDCnV TaCLOLWDG9NaW42LCULGGKNGGVKUZNVCGVZA ODLTNND GUUAGHYL LOSLINULAGLYAYNOVXUSL оLанKUсHMOХUYCLXHR GET https://app.dev.iiminny.com/api/v1/inteqration-app-token© Local Network Accessdetected:top-level site "https://app.dev.jiminny.com/onboard*,ration-app-token" ([IP_ADDRESS]:443) via xhr. Secure context: True, prompt action: (null)IHTTP/2.200 1883ms]initiator "https://app.dev.jiminny.com/onboard", accessing target "https://app.dev.jiminny.com/api/v1/integ onboardTop :...
|
NULL
|
-8554224369895112910
|
NULL
|
visual_change
|
ocr
|
NULL
|
Firefox FileEoitViewHistory BookmarksProfilesTools Firefox FileEoitViewHistory BookmarksProfilesToolsWindow Help•••)• -° app.dev.jiminny.com/onboardDevelopers | HubSpotM'inbox (1,576) - lukas.kovalik@jiminM 120216 is your HubSpot Log In CodFa CloudWatch | eu-west-1New Tabz Configure SSH access to multiple. fix-cache-for-business-processes4 [JY-20692] Issue with reconnectirg JiminnyAuth ProxyDashooard • Jiminny • MemoraneApo " 4oho CkM" • Jiminny • Meme+ New TabJIMInNYUpdate your informationGENERALTIMEZONDEurope/Sofia (UTC +03:00)LANGUAGES SPOKEN DURING CALLSDEFAULT SPOKEN LANGUAGEEnglish (United Kingdom)If the language isn't detected we'll default to this oneAdd languageCONNECT/SYNC SETTINGSConnect Zoho CRMSign in with Zoho CRMImport CalendarMeetingsSign in with GoogleLet's Get Started!Support Daily - in 5mA100% CS•Fri 17 Apr 14:55:23InspectorP Integr• ConsoleD DebuggerN Network{) Style EditorA Performance: Memory& Storagei Accessibility88 Application1706 hidden &3Errors Warnings Info Logs Debug© 22CSSneyuosts© GET https://ui.integration.app/assets/RefreshConnectionPopup- TE0hDXU.csgGET https://ui.integration.app/assets/Routes-CDlw4D3f.is• GEl https://u1.integration.app/assets/1ndex-/Ka1N6p0.19uoct mueosaeauee encetratson,age assets ettela toncoшect onguаle-crvoлso.® GET https://ui.integration.app/assets/ResetButton-B5vGAh3h.is•oel nicos:u1, incedracion.app/assers/1ndex-vooyuonr.g© GET https://ui.integration.app/assets/Routes-DHLymKVa.css® GET https://ui.integration.app/assets/flow-instance-context-CJr s6BC.isGlH3200 OnsIHTTP/3 200)HIIP/S 269[HTTP/3 (200 0ms][HTTP/3 (200) 0mslMlIr3 200IНTTP/3 200ІНTTP/3 200]MlIrS 200oel aetosae aptate temorane, coш acetd сдouszonoetUrtions aLepsae dod, deulellotale, com antegta Ltons cолoct• GET https://static.integration.app/connectors/zoho-crm/logo.pnghttps://static.1nteqration.app/connectors/zoho-crm/logo.onghttps://static.integration.app/files/d8bde402-9a7b-4627-bf08-ab2cfcdc4a43.pngGET https://api.getmembrane.com/integrations/zohocrmJPllons ntcos:ap1.cecmenbrane.com 1ncecrations zonochmotl nctos:ap1.qecmembrane.com/1ncecracions/bbreocy15z0zt5albsesc140OPTIONS https://api.getmembrane.com/integrations/66fe6c913202f3a165e3c14dIHTTP/3.200 179ms]IHTTP/2 204 46msl[HTTP/2. 20074mslHUTP/1.1 200 OK Oms.IHTTP/3.(200 27msl[HTTP/3 (200)91mslMIIF3: 20445ms]MlIr3 200yyms[HTTP/3_(204 59mslIHTTP/3.200 173msl[HTTP/3 204 46msl[IntegrationAppl onBoard openNewConnection resolved: nullonboard-BENebgvU.is:12:32191inteoratzonAvo connection condition matchedonboard-b=vebawu.1s:12:32324Navigated to https://app.dev.jiminny.com/onboard® GET https://ui.integration.app/embed/integrations/zohocrm/connect?token=ey]0eXAi0¡JKV1QiLCJhbGci0iJIUz[1NiJ9.eyJpZCI6IjFLY2U2NmM4LWZLYjEtNGRmMS1iMzIxLTIxNjA3ZGFmNDYyMyIsIm5hbWUi0i.. [HTTР/2_ (200 94msl0GEl https://u1.lntegration.app/assets/1ndex-DCou JW.1SHITPS 200• GET https://ui.integration.app/assets/index-DQ2tm1dS.cssIHTTP/3 200236ms]® None of the "sha384" hashes in the integrity attribute match the content of the subresource at "https://fonts.googleapis.com/css22family=IBM+Plex+Serifsdisplay=swap". The computed hash is"oxprsPitevorxso/owensool tooxiKklcsrPc Wiy ruMEwk alNd LOUMe oN".connect© GET https://ui.integration.app/assets/index-DRKPh6L6.igIHTTP/3200 0ms]GET https://ui.integration.app/assets/RefreshConnectionPopup-P6RvdGCd.is[HTTP/3 200 0mslGEl https://w1.integratzon.app/assets/RefreshConnectzo0Popup-1EUhDxU.cssHIIP/S 269• GET https://ui.integration.app/assets/Routes-CDWw4D3f.is[HTTP/3 (200 0ms]• GET https://ui.integration.app/assets/index-7Ka1N6pQ-is[HTTP/3 200 0msl• GEl https://u1.lntegration.app/assets/Inteqratzonconnectzonguard-LarvoH9g.1sHIIP/S 269• GET https://ui.integration.app/assets/ResetButton-B5vGAh3h.is[HTTP/3 (200 0ms]® GET https://ui.integration.app/assets/index-Dg8yQbmF.is[HTTP/3 (200O gel nicosiluh, ancegracion.app/assets/uLoweinstance-contextelr Sobl.lsMlIrS 200• GET https://ui.integration.app/assets/Routes-DHLymKVa.css[HTTP/3200) 0ms]© GET https://ui.integration.app/assets/index-BhMG5VAy.cssIHTTP/3 200 0mslMIIr3 200 14/mS.© XHR OPTIONS https://api.getmembrane.com/integrations/zohocrm[HTTP/2 (204 46mslGET https://static.integration.app/connectors/zoho=crm/logo.png[HTTP/2 200 60mslO GEUhttps:/static.1nteqration.app/connectors/zoho-crm/logo.ong• GET https://static.integration.app/files/d8bde402-9a7b=4627-bf08-ab2cfcdc4a43.pngIHTTP/3.(200 26ms]O XHR GET https://app.dev.jiminny.com/api/v1/integration-app-token[HTTP/2 200 6543msl* Navigated to https://app.dev.jiminny.com/onboard© Navigated to https://app.dev.jiminny.com/auth/redirect/google?redirectUrl=https&3A&2F%2Fapp.dev.jiminny.com$2Fonboard© GET https://accouopendo ellast protdle netpsabawww.togleapis.com/auth/calendar https://www.googleapis.com/auth/gmail.readonly&access type=ofIHTTP/2_302_408ms]* Navigated to https://accounts.google.com/signin/oauth/consent?as=S237994199%3A1776426897216672&authuser=1&cLient_id=827025697740-bFGIOiALelzFH9zP097HF7B85bBBWDi2Tpy7s7s-2umM0mmP9uaiVQsruEx20cii4Z3KPol-8S8z 6COhk166VVhrkTPTFPY.75Wk7F041SXLZLAWWDgebHKOBJ11g-2msswxolsBn424VocecLooLMLasxe2J04F2WHBJOuLUCgLBAyVLmSSSOCKOEWYZLSZrOUSSJOD/AULVGnULKSGUPVWKELCKULOEDVzKBGOSXPSUCo-kogtc4ontTreognonz-1NGrt5T-AuKwegne c.cc9AaronZo4enwnggn-a_owensosynNcuoouNoP xekWainTo4axsTTFAcycwcwuSzLz DTeNZOnT UyU-BSAL770420090541UGCc6FJpfAIYEwog573a0A9plyi1byqNB&rapt=AEjHL4NsQari2msKLi2I0YOCLSEnKDOWULONIESNZYOMY4UTYUNLL34NLISLCUYARDCnV TaCLOLWDG9NaW42LCULGGKNGGVKUZNVCGVZA ODLTNND GUUAGHYL LOSLINULAGLYAYNOVXUSL оLанKUсHMOХUYCLXHR GET https://app.dev.iiminny.com/api/v1/inteqration-app-token© Local Network Accessdetected:top-level site "https://app.dev.jiminny.com/onboard*,ration-app-token" ([IP_ADDRESS]:443) via xhr. Secure context: True, prompt action: (null)IHTTP/2.200 1883ms]initiator "https://app.dev.jiminny.com/onboard", accessing target "https://app.dev.jiminny.com/api/v1/integ onboardTop :...
|
47685
|
|
66557
|
1495
|
38
|
2026-04-21T14:46:59.399869+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-21/1776 /Users/lukas/.screenpipe/data/data/2026-04-21/1776782819399_m1.jpg...
|
PhpStorm
|
faVsco.js – AutomatedReportsService.php
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
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
AutomatedReportsRepositoryTest
Run 'AutomatedReportsRepositoryTest'
Debug 'AutomatedReportsRepositoryTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
1
103
3
34
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Kiosk\AutomatedReports;
use Carbon\CarbonImmutable;
use Carbon\CarbonInterface;
use Carbon\Exceptions\InvalidFormatException;
use DateTime;
use DateTimeInterface;
use DateTimeZone;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Jiminny\Component\ActivitySearch\FilterDefinition\InputTypeEnum;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Component\AskAnything\Dtos\AskAnythingPromptDto;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Repositories\UserRepository;
use Jiminny\Exceptions\ApplicationException;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\ModelNotFoundException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateReportJob;
use Jiminny\Models\Activity\Search;
use Jiminny\Models\AskAnything\AskAnythingPrompt;
use Jiminny\Models\AskAnything\AskAnythingPromptTarget;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AskAnythingRepository;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\StageRepository;
use Throwable;
class AutomatedReportsService
{
public const string TYPE_LOSS_ANALYSIS = 'loss_analysis';
public const string TYPE_ASK_JIMINNY = 'ask_jiminny';
/**
* Standard report types (used by kiosk for existing automated reports).
*/
// @TODO this will add filter, however if we need to control feature by FF we need conditional logic
public const array TYPES = [
['id' => 'exec_summary', 'name' => 'Exec Summary'],
['id' => 'coaching_profiles', 'name' => 'Coaching Profiles'],
['id' => 'product_feedback', 'name' => 'Product Feedback'],
['id' => self::TYPE_LOSS_ANALYSIS, 'name' => 'Loss Analysis'],
// ['id' => 'questions', 'name' => 'Questions'],
// ['id' => 'statistical_quant', 'name' => 'Statistical Quantitative'],
];
public const array ALL_TYPES = [
...self::TYPES,
['id' => self::TYPE_ASK_JIMINNY, 'name' => 'Ask Jiminny'],
];
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
/**
* Frequencies for standard (non-Ask Jiminny) reports.
*/
public const array FREQUENCIES = [
['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],
['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],
['id' => self::FREQUENCY_QUARTERLY, 'name' => 'Quarterly'],
['id' => self::FREQUENCY_ONE_OFF, 'name' => 'One-off'],
];
/**
* Frequencies for Ask Jiminny reports.
*/
public const array ASK_JIMINNY_FREQUENCIES = [
['id' => self::FREQUENCY_DAILY, 'name' => 'Daily'],
['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],
['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],
];
public const string MEDIA_TYPE_PDF = 'pdf';
public const string MEDIA_TYPE_PODCAST = 'podcast';
public const array MEDIA_TYPES = [self::MEDIA_TYPE_PDF, self::MEDIA_TYPE_PODCAST];
public const array MEDIA_TYPE_OBJECT_PDF = ['id' => self::MEDIA_TYPE_PDF, 'name' => 'PDF'];
public const array MEDIA_TYPE_OBJECT_PODCAST = ['id' => self::MEDIA_TYPE_PODCAST, 'name' => 'Podcast'];
public const array MEDIA_TYPE_OBJECTS = [self::MEDIA_TYPE_OBJECT_PDF, self::MEDIA_TYPE_OBJECT_PODCAST];
public const array CALL_TYPE_CONFERENCE = ['id' => 'conference', 'name' => 'Conference'];
public const array CALL_TYPE_DIALER = ['id' => 'dialer', 'name' => 'Dialer'];
public const int SENT_REPORT_AT_HOURS = 5;
public const string PDF_KEY = 'pdf';
public const string AUDIO_KEY = 'audio';
private const array ALL_FREQUENCIES = [
['id' => self::FREQUENCY_DAILY, 'name' => 'Daily'],
['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],
['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],
['id' => self::FREQUENCY_QUARTERLY, 'name' => 'Quarterly'],
['id' => self::FREQUENCY_ONE_OFF, 'name' => 'One-off'],
];
private const string S3_DIR = 'reports';
private const array FILE_EXTENSIONS_VARIANTS = ['html', 'MD', 'pdf'];
private const array FILE_PODCAST_EXTENSIONS_VARIANTS = ['json', 'mp3', 'ssml'];
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly GroupRepository $groupRepository,
private readonly UserRepository $userRepository,
private readonly StageRepository $stageRepository,
private readonly DealStagesService $dealStagesService,
private readonly RecipientsService $recipientsService,
private readonly AutomatedReportsRepository $automatedReportsRepository,
private readonly Webhook $webhookService,
private readonly BusDispatcher $dispatcher,
private readonly ActivityTypeService $activityTypeService,
private readonly PlaybookCategoryRepository $playbookCategoryRepository,
private readonly AskAnythingPromptService $askAnythingPromptService,
private readonly SearchRepository $activitySearchRepository,
private readonly AskAnythingRepository $askAnythingRepository,
) {
}
public static function getTypes(): array
{
$types = self::TYPES;
return array_map(static function ($type) {
return $type['id'];
}, $types);
}
public static function getCallTypes(): array
{
return array_map(static function ($callType) {
return $callType['id'];
}, [self::CALL_TYPE_CONFERENCE, self::CALL_TYPE_DIALER]);
}
public static function getFrequencies(): array
{
return array_map(static function ($frequency) {
return $frequency['id'];
}, self::FREQUENCIES);
}
// front-facing structure
public function getReportEnabledFieldData(bool $value = false): array
{
return [
'id' => 'report_enabled',
'label' => '',
'inputType' => InputTypeEnum::TOGGLE,
'value' => $value,
];
}
// Organizations = Teams
public function getOrganizationFieldData(?string $value = null, bool $shortVersion = false): array
{
$options = $this->getTeams();
if ($shortVersion) {
return [
'id' => 'organization',
'label' => 'Organization',
'options' => $options,
];
}
return [
'id' => 'organization',
'label' => 'Organization',
'inputType' => InputTypeEnum::DROPDOWN,
'required' => true,
'placeholder' => 'Select',
'options' => $options,
'value' => $value,
'dependencies' => [
'teams',
'deal_stage_at_call',
'current_deal_stage',
'recipients',
ActivityTypeService::PLAYBOOK_CATEGORIES_KEY,
],
'dependsOn' => [],
];
}
// Teams = Groups
public function getTeamFieldData(array $options = [], array $value = [], bool $shortVersion = false): array
{
if ($shortVersion) {
return [
'id' => 'teams',
'label' => 'Team',
'options' => $options,
];
}
return [
'id' => 'teams',
'label' => 'Team',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'required' => false,
'placeholder' => 'Select',
'options' => $options,
'value' => $value, // value should be an array of objects {id, name}
'dependencies' => [ActivityTypeService::PLAYBOOK_CATEGORIES_KEY],
'dependsOn' => [],
];
}
public function getReportTypeFieldData(?string $value = null, bool $shortVersion = false, ?Team $team = null): array
{
$types = [];
if ($team instanceof Team) {
if ($team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {
$types = self::TYPES;
}
if ($team->hasFeature(FeatureEnum::ASK_JIMINNY_REPORTS)) {
$types[] = ['id' => self::TYPE_ASK_JIMINNY, 'name' => 'Ask Jiminny'];
}
} else {
$types = self::TYPES;
}
if ($shortVersion) {
return [
'id' => 'report_type',
'label' => 'Report Type',
'options' => $types,
];
}
return [
'id' => 'report_type',
'label' => 'Report Type',
'inputType' => InputTypeEnum::DROPDOWN,
'required' => true,
'placeholder' => 'Select',
'options' => $types,
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getFrequencyFieldData(?string $value = null): array
{
return [
'id' => 'frequency',
'label' => 'Frequency',
'inputType' => InputTypeEnum::DROPDOWN,
'required' => true,
'placeholder' => 'Select',
'options' => self::FREQUENCIES,
'value' => $value,
'dependencies' => ['period'],
'dependsOn' => [],
];
}
public function getPeriodFieldData(?string $valueStartDate = null, ?string $valueEndDate = null): array
{
return [
'id' => 'period',
'label' => 'Select one-off period',
'inputType' => InputTypeEnum::DATE_RANGE,
'required' => true,
'placeholder' => 'Select',
'value' => ['startDate' => $valueStartDate, 'endDate' => $valueEndDate],
'queryParams' => [
'startDate' => 'start_date_period',
'endDate' => 'end_date_period',
],
'dependencies' => [],
'dependsOn' => ['frequency'],
];
}
public function getActivityTypesFieldData(?Team $team = null, array $value = [], array $teamsFilter = []): array
{
return $this->activityTypeService->getActivityTypeFieldData(team: $team, value: $value, groupIds: $teamsFilter);
}
public function getDealStageAtCallFieldData(?Team $team = null, array $value = []): array
{
return $this->dealStagesService->getDealStageAtCallFieldData(team: $team, value: $value);
}
public function getCurrentDealStageFieldData(?Team $team = null, array $value = []): array
{
return $this->dealStagesService->getCurrentDealStageFieldData(team: $team, value: $value);
}
public function getDealValueFieldData(?int $valueMin = null, ?int $valueMax = null): array
{
return [
'id' => 'deal_value',
'label' => 'Deal Value',
'inputType' => InputTypeEnum::INTEGER_RANGE,
'required' => false,
'value' => ['min' => $valueMin, 'max' => $valueMax],
'queryParams' => [
'min' => 'min_deal_value',
'max' => 'max_deal_value',
],
'dependencies' => [],
'dependsOn' => [],
];
}
public function getCallTypeFieldData(bool $conferenceOn = false, bool $dialerOn = false): array
{
$value = [];
$conferenceOn && $value[] = self::CALL_TYPE_CONFERENCE;
$dialerOn && $value[] = self::CALL_TYPE_DIALER;
return [
'id' => 'call_type',
'label' => 'Call Type',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'required' => true,
'options' => [
self::CALL_TYPE_CONFERENCE,
self::CALL_TYPE_DIALER,
],
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getMediaTypeFieldData(?AutomatedReport $report = null): array
{
$value = [];
if ($report) {
$value = $this->transformMediaTypes($report);
}
return [
'id' => 'media_types',
'label' => 'Export as',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'required' => true,
'options' => self::MEDIA_TYPE_OBJECTS,
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getCallDurationFieldData(?int $valueMin = null, ?int $valueMax = null): array
{
return [
'id' => 'call_duration',
'label' => 'Call Duration',
'inputType' => InputTypeEnum::INTEGER_RANGE,
'required' => false,
'value' => ['min' => $valueMin, 'max' => $valueMax],
'queryParams' => [
'min' => 'min_call_duration',
'max' => 'max_call_duration',
],
'dependencies' => [],
'dependsOn' => [],
];
}
public function getRecipientsFieldData(?Team $team = null, array $value = []): array
{
return $this->recipientsService->getRecipientsFieldData(team: $team, value: $value);
}
public function getJiminnyRecipientsFieldData(array $value = []): array
{
return $this->recipientsService->getJiminnyRecipientsFieldData($value);
}
public function getAdditionalPromptInputFieldData(?string $value = null): array
{
return [
'id' => 'additional_prompt_input',
'label' => 'Special requirements',
'inputType' => InputTypeEnum::TEXTAREA,
'required' => false,
'placeholder' => 'What should be the focus of the report?',
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getCustomReportNameFieldData(?string $value = null): array
{
return [
'id' => 'custom_name',
'label' => 'Custom report name',
'inputType' => InputTypeEnum::TEXT,
'required' => false,
'placeholder' => 'Enter custom name',
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
// data providers
public function getTeams(): array
{
$teams = $this->teamRepository->getTeamsForKiosk(status: Team::STATUS_ACTIVE);
$teamData = [];
foreach ($teams as $team) {
if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {
continue;
}
$teamData[] = $this->transformTeam($team);
}
return $teamData;
}
public function getTeamGroups(string $teamUuid): array
{
$data = [];
$team = $this->getTeam($teamUuid);
if ($team !== null) {
$groups = $team->groups()->get();
foreach ($groups as $group) {
$data[] = [
'id' => $group->getUuid(),
'name' => $group->getName(),
];
}
}
return $data;
}
public function getTeamsGroupsOptions(array $filterTeamUuids = []): array
{
$data = [];
$teams = $this->getTeams();
foreach ($teams as $team) {
if (! empty($filterTeamUuids) && ! in_array($team['id'], $filterTeamUuids, true)) {
continue;
}
$data[] = [
'label' => $team['name'],
'groups' => $this->getTeamGroups($team['id']),
];
}
return $data;
}
public function getTeam(string $teamUuid): ?Team
{
return $this->teamRepository->idOrUuid($teamUuid);
}
public function getTeamById(int $teamId): ?Team
{
return $this->teamRepository->find($teamId);
}
public function getGroupsUuids(AutomatedReport $report): array
{
$uuids = [];
$reportGroups = $report->getGroups();
foreach ($reportGroups as $groupId) {
if ($group = $this->groupRepository->find($groupId)) {
$uuids[] = $group->getUuid();
}
}
return $uuids;
}
public function getPlaybookCategoriesUuids(AutomatedReport $report): array
{
$uuids = [];
$playbookCategories = $report->getPlaybookCategories();
foreach ($playbookCategories as $id) {
if ($category = $this->playbookCategoryRepository->find($id)) {
$uuids[] = $category->getUuid();
}
}
return $uuids;
}
public function getDealAtCallStagesUuids(AutomatedReport $report): array
{
$uuids = [];
$reportStages = $report->getDealAtCallStages();
foreach ($reportStages as $id) {
if ($stage = $this->stageRepository->find($id)) {
$uuids[] = $stage->getUuid();
}
}
return $uuids;
}
public function getCurrentDealStagesUuids(AutomatedReport $report): array
{
$uuids = [];
$reportStages = $report->getCurrentDealStages();
foreach ($reportStages as $id) {
if ($stage = $this->stageRepository->find($id)) {
$uuids[] = $stage->getUuid();
}
}
return $uuids;
}
public function getUsersUuids(AutomatedReport $report): array
{
return $this->extractUserUuids($report->getRecipients());
}
public function getJiminnyUsersUuids(AutomatedReport $report): array
{
return $this->extractUserUuids($report->getJiminnyRecipients());
}
/**
* @param array<string, mixed> $recipients
*/
private function extractUserUuids(array $recipients): array
{
$userIds = $recipients['users'] ?? [];
return collect($userIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->map(fn (UserContract $user) => $user->getUuid())
->values()
->all();
}
// get mail data
public function getRecipientUsers(AutomatedReport $report): array
{
return $this->buildRecipientUsers($report->getRecipients());
}
/**
* @return array<UserContract>
*/
public function getRecipientUserObjects(AutomatedReport $report): array
{
$userIds = $report->getRecipients()['users'] ?? [];
return collect($userIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->values()
->all();
}
private function getJiminnyRecipientUsers(AutomatedReport $report): array
{
return $this->buildRecipientUsers($report->getJiminnyRecipients());
}
/**
* @param array<string, mixed> $recipients
*/
private function buildRecipientUsers(array $recipients): array
{
$userIds = $recipients['users'] ?? [];
return collect($userIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->map(fn (UserContract $user) => [
'email' => $user->getEmailAddress(),
'name' => $user->getName(),
'timezone' => $user->getTimezone()->getName(),
])
->values()
->all();
}
public function getValidRecipientUsers(AutomatedReport $report, bool $includeJiminny = false): array
{
if ($report->isAskJiminnyReport()) {
$recipients = $this->resolveAskJiminnyRecipients($report);
} else {
$recipients = $this->getRecipientUsers($report);
if ($includeJiminny) {
$recipients = array_merge($recipients, $this->getJiminnyRecipientUsers($report));
}
}
$emails = [];
return array_values(array_filter(
$recipients,
static function ($recipient) use (&$emails) {
if (empty($recipient['email']) || in_array($recipient['email'], $emails, true)) {
return false;
}
$emails[] = $recipient['email'];
return true;
}
));
}
private function resolveAskJiminnyRecipients(AutomatedReport $report): array
{
$recipients = [];
$creator = $report->getCreator();
if ($creator !== null) {
$recipients[] = [
'email' => $creator->getEmailAddress(),
'name' => $creator->getName(),
'timezone' => $creator->getTimezone()->getName(),
];
}
return array_merge(
$recipients,
$this->buildRecipientUsers($report->getRecipients()),
$this->getGroupRecipientUsers($report),
);
}
private function getGroupRecipientUsers(AutomatedReport $report): array
{
$users = [];
foreach ($report->getGroups() as $groupId) {
$group = $this->groupRepository->find($groupId);
if ($group === null) {
continue;
}
foreach ($group->getMembers() as $member) {
$users[] = [
'email' => $member->getEmailAddress(),
'name' => $member->getName(),
'timezone' => $member->getTimezone()->getName(),
];
}
}
return $users;
}
public function getReportTypeName(AutomatedReportResult $report): string
{
$type = $report->getReport()->getType();
$getType = $this->transformReportType($type);
return $getType['name'];
}
public function getReportPeriodName(AutomatedReportResult $report): string
{
$from = $report->getFromDate();
$to = $report->getToDate();
$frequency = $report->getReport()->getFrequency();
if ($from === null || $to === null) {
if (! $report->getReport()->isAskJiminnyReport()) {
$invalidPeriod = $from === null ? 'from' : 'to';
throw new ApplicationException('Report period is invalid: ' . $invalidPeriod);
}
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
}
return $this->formatReportPeriodName($frequency, $from, $to);
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function getReportTeamsName(AutomatedReportResult $report): string
{
$groups = $report->getGroups();
if (empty($groups)) {
return 'All';
}
// Get group names from repository
$groupNames = [];
foreach ($groups as $groupId) {
$group = $this->groupRepository->find($groupId);
if ($group) {
$groupNames[] = $group->getName();
}
}
if (count($groupNames) === 1) {
// Single team format
$teamsName = $groupNames[0];
} else {
// Multiple teams format
$teamsName = implode(', ', $groupNames);
}
return $teamsName;
}
public function getReportFileName(AutomatedReportResult $report): string
{
$customName = $report->getReport()->getCustomName();
$periodName = $this->getReportPeriodName($report);
$filenameSuffix = $this->getFilenameSuffix($report);
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
return $this->sanitizeFileName("{$customName} - {$periodName}");
}
$baseName = $this->getReportTypeName($report);
if ($filenameSuffix) {
$baseName .= " {$filenameSuffix}";
}
return $this->sanitizeFileName("{$baseName} - {$periodName} - {$this->getReportTeamsName($report)}");
}
public function getReportFileNameWithExtension(AutomatedReportResult $result): string
{
$extension = $this->getMediaTypeMetadata($result)['extension'];
return $this->getReportFileName($result) . '.' . $extension;
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
public function isUserRecipientOfReport(User $user, AutomatedReport $report): bool
{
$recipients = array_map('intval', $report->getRecipients()['users'] ?? []);
return in_array($user->getId(), $recipients);
}
public function transformReportResults(Collection $automatedReportResults): array
{
$data = [];
foreach ($automatedReportResults as $automatedReportResult) {
/** @var AutomatedReportResult $automatedReportResult */
$report = $automatedReportResult->getReport();
$createdBy = $report->getCreator();
$creator = [
'id' => $createdBy?->getUuid(),
'name' => $createdBy?->getName(),
'email' => $createdBy?->getEmailAddress(),
'photoUrl' => $createdBy?->getPhotoUrl(),
];
$recipients = $this->buildRecipients($report);
$data[] = [
'id' => $automatedReportResult->getUuid(),
'name' => $automatedReportResult->getName(),
'frequency' => $this->transformFrequency($report->getFrequency()),
'recipients' => [
...array_values($this->transformGroups(team: $report->getTeam(), groupsIds: $report->getGroups())),
...array_values($this->transformRecipients($report->getRecipients())),
],
'report_type' => $this->transformReportType($report->getType()),
'media_type' => $automatedReportResult->getMediaType(),
'downloadUrl' => $this->generateReportResultDownloadUrl($automatedReportResult),
'viewUrl' => $this->generateReportResultViewUrl($automatedReportResult),
'generated_at' => $automatedReportResult->getGeneratedAt()?->toIso8601String(),
'creator' => $creator,
];
}
return $data;
}
public function hasCallTypeConference(AutomatedReport $report): bool
{
return in_array(self::CALL_TYPE_CONFERENCE['id'], $report->getCallTypes(), true);
}
public function hasCallTypeDialer(AutomatedReport $report): bool
{
return in_array(self::CALL_TYPE_DIALER['id'], $report->getCallTypes(), true);
}
// transformers
private function transformTeam(Team $team): array
{
if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {
return [];
}
return [
'id' => $team->getUuid(),
'name' => $team->getName(),
];
}
private function transformReportFullView(AutomatedReport $report): array
{
$base = $this->transformReportBase($report);
return $report->getType() === self::TYPE_ASK_JIMINNY
? $base + $this->transformAskJiminnyFields($report)
: $base + $this->transformStandardReportFields($report);
}
private function transformReportBase(AutomatedReport $report): array
{
return [
'id' => $report->getUuid(),
'organization' => $this->transformOrganization(team: $report->getTeam()),
'report_type' => $this->transformReportType($report->getType()),
'frequency' => $this->transformFrequency($report->getFrequency()),
];
}
private function transformStandardReportFields(AutomatedReport $report): array
{
$team = $report->getTeam();
return [
'report_enabled' => $report->getStatus(),
'start_date_period' => $report->getFrom()?->format('Y-m-d H:i:s'),
'end_date_period' => $report->getTo()?->format('Y-m-d H:i:s'),
'deal_value_min' => $report->getDealValueMin(),
'deal_value_max' => $report->getDealValueMax(),
'call_types' => $this->transformCallType($report->getCallTypes()),
'media_types' => $this->transformMediaTypes($report),
'call_duration_min' => $this->transformDurationToMinutes($report->getCallDurationMin()),
'call_duration_max' => $this->transformDurationToMinutes($report->getCallDurationMax()),
'teams' => $this->transformGroups(team: $team, groupsIds: $report->getGroups()),
'deal_at_call_stages' => $this->transformStages(team: $team, stagesIds: $report->getDealAtCallStages()),
'current_deal_stages' => $this->transformStages(team: $team, stagesIds: $report->getCurrentDealStages()),
'recipients' => $this->transformRecipients($report->getRecipients()),
'created_by' => $this->transformCreator($report->getCreator()),
'additional_prompt_input' => $report->getAdditionalPromptInput(),
'custom_name' => $report->getCustomName(),
'created_at' => $report->getCreatedAt()->format('Y-m-d H:i:s'),
'updated_at' => $report->getUpdatedAt()->format('Y-m-d H:i:s'),
'deleted_at' => $report->getDeletedAt()?->format('Y-m-d H:i:s'),
];
}
private function transformAskJiminnyFields(AutomatedReport $report): array
{
$team = $report->getTeam();
$creatorId = $report->getAttribute('created_by');
$explicitUserIds = array_values(array_filter(
$report->getRecipients()['users'] ?? [],
static fn ($id) => $id !== $creatorId
));
return [
'report_name' => $report->getCustomName(),
'enabled' => $report->getStatus(),
'share_teams' => $this->transformGroups(team: $team, groupsIds: $report->getGroups()),
'share_users' => $this->transformRecipients(['users' => $explicitUserIds]),
'saved_search' => $this->transformSafeSearch($report->getSavedSearch()),
'ask_jiminny_prompt' => $this->transformAskJiminnyPrompt($report->getAskAnythingPrompt()),
'expires_on' => $report->getExpiresAt()?->format('Y-m-d'),
];
}
private function transformOrganization(?Team $team): array
{
return [
'id' => $team?->getUuid(),
'name' => $team?->getName(),
];
}
private function transformReportType(string $type): array
{
foreach (self::ALL_TYPES as $typeItem) {
if ($typeItem['id'] === $type) {
return $typeItem;
}
}
return [];
}
private function transformCallType(array $types): array
{
$result = [];
$callTypes = [self::CALL_TYPE_CONFERENCE, self::CALL_TYPE_DIALER];
foreach ($types as $type) {
foreach ($callTypes as $callTypeItem) {
if ($callTypeItem['id'] === $type) {
$result[] = $callTypeItem;
break;
}
}
}
return $result;
}
private function transformMediaTypes(AutomatedReport $report): array
{
$values = [];
foreach ($report->getMediaTypes() as $mediaType) {
if (! in_array($mediaType, self::MEDIA_TYPES, true)) {
continue;
}
$values[] = match ($mediaType) {
self::MEDIA_TYPE_PDF => self::MEDIA_TYPE_OBJECT_PDF,
self::MEDIA_TYPE_PODCAST => self::MEDIA_TYPE_OBJECT_PODCAST,
};
}
return $values;
}
private function transformFrequency(string $frequency): array
{
foreach (self::ALL_FREQUENCIES as $frequencyItem) {
if ($frequencyItem['id'] === $frequency) {
return $frequencyItem;
}
}
return [];
}
public function transformDurationToMinutes(?int $duration): ?int
{
if (! $duration) {
return null;
}
return (int) ($duration / 60);
}
private function transformGroups(?Team $team, array $groupsIds): array
{
if (empty($groupsIds) || ! $team) {
return [];
}
$data = [];
foreach ($groupsIds as $groupId) {
$group = $team->groups()->where('id', $groupId)->first();
if ($group) {
$data[] = [
'id' => $group->getUuid(),
'name' => $group->getName(),
'photoUrl' => $group->getPhotoUrl(),
];
}
}
return $data;
}
private function transformStages(?Team $team, array $stagesIds): array
{
if (empty($stagesIds) || ! $team) {
return [];
}
$data = [];
foreach ($stagesIds as $stageId) {
$stage = $team->stages()->where('id', $stageId)->first();
if ($stage) {
$data[] = [
'id' => $stage->getUuid(),
'name' => $stage->getName(),
];
}
}
return $data;
}
private function transformRecipients(array $recipients): array
{
$users = [];
foreach ($recipients['users'] ?? [] as $userId) {
$users[] = $this->transformUser($userId);
}
return $users;
}
private function transformCreator(?User $user): ?array
{
if ($user === null) {
return null;
}
return $this->transformUser($user->getId());
}
private function transformAskJiminnyPrompt(?AskAnythingPrompt $prompt): ?array
{
if ($prompt === null) {
return null;
}
return [
'id' => $prompt->getUuid(),
'name' => $prompt->getTitle(),
];
}
private function transformSafeSearch(?Search $search): ?array
{
if ($search === null) {
return null;
}
return [
'id' => $search->getUuid(),
'name' => $search->getName(),
];
}
private function transformUser(int $userId): array
{
/* @var ?User $user */
$user = $this->userRepository->find($userId);
return [
'id' => $user?->getUuid(),
'name' => $user?->getName(),
'email' => $user?->getEmailAddress(),
'photoUrl' => $user?->getPhotoUrl(),
];
}
public function create(array $data): array
{
$validatedData = $this->validateAndTransformData($data);
$validatedData['created_by'] = auth()->id();
$automatedReport = $this->automatedReportsRepository->create($validatedData);
$this->generateOneOffReport($automatedReport);
return $this->transformReportFullView($automatedReport);
}
public function update(string $uuid, array $data): array
{
$validatedData = $this->validateAndTransformData($data);
$report = $this->automatedReportsRepository->findByUuid($uuid);
if (! $report) {
throw new InvalidArgumentException('Report not found');
}
$oldCustomName = $report->getCustomName();
$automatedReport = $this->automatedReportsRepository->update($report, $validatedData);
if ($oldCustomName !== $automatedReport->getCustomName()) {
$this->updateResultNames($automatedReport);
}
$this->generateOneOffReport($automatedReport);
return $this->transformReportFullView($automatedReport);
}
/**
* Create an Ask Jiminny report.
*/
public function createAskJiminnyReport(array $data, User $creator): array
{
$validatedData = $this->validateAskJiminnyReportData($data, $creator);
$validatedData['created_by'] = $creator->getId();
$automatedReport = $this->automatedReportsRepository->create($validatedData);
return $this->transformReportFullView($automatedReport);
}
/**
* Update an Ask Jiminny report.
*/
public function updateAskJiminnyReport(AutomatedReport $report, array $data, User $user): array
{
if (! $report->isAskJiminnyReport()) {
throw new InvalidArgumentException('Report is not an Ask Jiminny report');
}
$validatedData = $this->validateAskJiminnyReportData($data, $user);
$oldCustomName = $report->getCustomName();
$automatedReport = $this->automatedReportsRepository->update($report, $validatedData);
if ($oldCustomName !== $automatedReport->getCustomName()) {
$this->updateResultNames($automatedReport);
}
return $this->transformReportFullView($automatedReport);
}
public function updateAskJiminnyReportStatus(AutomatedReport $report, bool $status): array
{
$this->automatedReportsRepository->update($report, ['status' => $status]);
return $this->transformReportFullView($report->fresh());
}
/**
* Validate and transform data for Ask Jiminny reports.
*/
private function validateAskJiminnyReportData(array $data, User $user): array
{
// Validate name
$name = trim($data['report_name'] ?? '');
if (empty($name)) {
throw new InvalidArgumentException('Report name is required');
}
if (mb_strlen($name) > 50) {
throw new InvalidArgumentException('Report name must be 50 characters or less');
}
// Validate frequency (only daily, weekly, monthly for Ask Jiminny)
$frequency = $data['frequency'] ?? null;
$askJiminnyFrequencies = [self::FREQUENCY_DAILY, self::FREQUENCY_WEEKLY, self::FREQUENCY_MONTHLY];
if (! in_array($frequency, $askJiminnyFrequencies, true)) {
throw new InvalidArgumentException('Frequency must be daily, weekly, or monthly');
}
// Validate expiration date
$expiresAt = $data['expires_on'] ?? null;
if (empty($expiresAt)) {
throw new InvalidArgumentException('Expiration date is required');
}
try {
$expiresAtDate = Carbon::parse($expiresAt);
} catch (InvalidFormatException $e) {
throw new InvalidArgumentException('Expiration date format is invalid');
}
$maxExpiration = Carbon::now()->addYear()->endOfDay();
if ($expiresAtDate->gt($maxExpiration)) {
throw new InvalidArgumentException('Expiration date cannot be more than 1 year from now');
}
if ($expiresAtDate->isPast()) {
throw new InvalidArgumentException('Expiration date cannot be in the past');
}
// Validate saved search
$activitySearchId = $data['saved_search'] ?? null;
if (empty($activitySearchId)) {
throw new InvalidArgumentException('Saved search is required');
}
$savedSearch = $this->activitySearchRepository->findByUuidAndUser($activitySearchId, $user);
if (! $savedSearch) {
throw new InvalidArgumentException('Saved search not found or does not belong to you');
}
// Validate saved prompt
$askAnythingPromptId = $data['ask_jiminny_prompt'] ?? null;
if (empty($askAnythingPromptId)) {
throw new InvalidArgumentException('Ask Jiminny prompt is required');
}
$prompt = $this->askAnythingRepository->getPromptByUuid($askAnythingPromptId);
if (! $prompt) {
throw new InvalidArgumentException('Ask Jiminny prompt not found');
}
// Validate status
$status = $data['enabled'] ?? false;
$recipientUserIds = [$user->getId()];
if (! empty($data['share_users'])) {
$sharedUserIds = $this->validateAndGetUserIdsByTeam(
$user->team,
(array) $data['share_users']
);
$recipientUserIds = array_merge($recipientUserIds, $sharedUserIds);
}
$sharedGroupIds = [];
if (! empty($data['share_teams'])) {
$sharedGroupIds = $this->validateAndGetGroupIds($user->team, (array) $data['share_teams']);
}
$recipientUserIds = array_values(array_unique($recipientUserIds));
return [
'team_id' => $user->getTeamId(),
'type' => self::TYPE_ASK_JIMINNY,
'status' => (bool) $status,
'frequency' => $frequency,
'custom_name' => $name,
'activity_search_id' => $savedSearch->getId(),
'ask_anything_prompt_id' => $prompt->getId(),
'expires_at' => $expiresAtDate->toDateString(),
'media_types' => [self::MEDIA_TYPE_PDF],
'call_types' => [],
'recipients' => ['users' => $recipientUserIds],
'groups' => $sharedGroupIds,
];
}
public static function getAskJiminnyFrequencies(): array
{
return array_map(static function ($frequency) {
return $frequency['id'];
}, self::ASK_JIMINNY_FREQUENCIES);
}
public function getAskJiminnyReportFilters(User $user): array
{
$savedSearches = $this->activitySearchRepository->findByUserOrderedByName($user)
->map(fn (Search $search) => [
'id' => $search->getUuid(),
'name' => $search->getName(),
])
->values()->all();
$prompts = collect(
$this->askAnythingPromptService->get($user, AskAnythingPromptTarget::on_demand)
)->map(fn (AskAnythingPromptDto $prompt) => [
'id' => $prompt->id,
'name' => $prompt->title,
])->values()->all();
return [
[
'id' => 'prompt',
'label' => 'Prompt',
'options' => $prompts,
],
[
'id' => 'saved_search',
'label' => 'Saved Search',
'options' => $savedSearches,
],
];
}
public function getAskJiminnyReportFormData(User $user, ?AutomatedReport $report = null): array
{
$team = $user->getTeam();
$userTimezone = $user->getTimezone();
$savedSearches = $this->activitySearchRepository->findByUserOrderedByName($user)
->map(fn (Search $search) => [
'id' => $search->getUuid(),
'name' => $search->getName(),
])
->values()->all();
$prompts = collect(
$this->askAnythingPromptService->get($user, AskAnythingPromptTarget::on_demand)
)->map(fn (AskAnythingPromptDto $prompt) => [
'id' => $prompt->id,
'name' => $prompt->title,
])->values()->all();
$teamGroups = $this->groupRepository->getAllByTeam($team)->map(fn ($group) => [
'id' => $group->getUuid(),
'name' => $group->getName(),
])->values()->all();
$shareUsers = $this->recipientsService->getRecipientsFieldData(team: $team)['options'] ?? [];
$sharedTeamsValue = [];
$sharedUsersValue = [];
if ($report) {
$sharedTeamsValue = $this->transformGroups($team, $report->getGroups());
$recipientUserIds = $report->getRecipients()['users'] ?? [];
$creatorId = $report->getAttribute('created_by');
$sharedUserIds = array_values(array_filter(
$recipientUserIds,
static fn ($id) => $id !== $creatorId
));
$sharedUsersValue = collect($sharedUserIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->map(fn (User $u) => [
'id' => $u->getUuid(),
'name' => $u->getName(),
])
->values()
->all();
}
return [
'fields' => [
[
'id' => 'enabled',
'inputType' => InputTypeEnum::TOGGLE,
'label' => '',
'value' => $report?->getStatus() ?? false,
],
[
'id' => 'report_name',
'inputType' => InputTypeEnum::TEXT,
'label' => 'Name',
'placeholder' => 'Enter name',
'required' => true,
'validation' => ['maxLength' => 50],
'value' => $report?->getCustomName() ?? '',
],
[
'id' => 'frequency',
'inputType' => InputTypeEnum::DROPDOWN,
'label' => 'Frequency',
'required' => true,
'placeholder' => 'Select',
'options' => self::ASK_JIMINNY_FREQUENCIES,
'value' => $report ? $this->transformFrequency($report->getFrequency()) : null,
],
[
'id' => 'expires_on',
'inputType' => InputTypeEnum::DATE,
'label' => 'Expires on',
'required' => true,
'placeholder' => 'Select',
'validation' => [
'minDate' => now($userTimezone)->toDateString(),
'maxDate' => now($userTimezone)->addYear()->toDateString(),
],
'value' => $report?->getExpiresAt()?->toDateString(),
],
[
'id' => 'share_teams',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'label' => 'Team',
...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#11894 on JY-18909-automated-reports-ask-jiminny, menu","depth":5,"help_text":"Pull request #11894 exists for current branch JY-18909-automated-reports-ask-jiminny","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AutomatedReportsRepositoryTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AutomatedReportsRepositoryTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AutomatedReportsRepositoryTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"103","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"34","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Kiosk\\AutomatedReports;\n\nuse Carbon\\CarbonImmutable;\nuse Carbon\\CarbonInterface;\nuse Carbon\\Exceptions\\InvalidFormatException;\nuse DateTime;\nuse DateTimeInterface;\nuse DateTimeZone;\nuse Illuminate\\Contracts\\Bus\\Dispatcher as BusDispatcher;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Support\\Carbon;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\Log;\nuse Illuminate\\Support\\Facades\\Storage;\nuse Jiminny\\Component\\ActivitySearch\\FilterDefinition\\InputTypeEnum;\nuse Jiminny\\Component\\AskAnything\\AskAnythingPromptService;\nuse Jiminny\\Component\\AskAnything\\Dtos\\AskAnythingPromptDto;\nuse Jiminny\\Component\\UrlGenerator\\Webhook;\nuse Jiminny\\Contracts\\Repositories\\PlaybookCategoryRepository;\nuse Jiminny\\Contracts\\Repositories\\TeamRepository;\nuse Jiminny\\Contracts\\Repositories\\UserRepository;\nuse Jiminny\\Exceptions\\ApplicationException;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Exceptions\\ModelNotFoundException;\nuse Jiminny\\Jobs\\AutomatedReports\\RequestGenerateReportJob;\nuse Jiminny\\Models\\Activity\\Search;\nuse Jiminny\\Models\\AskAnything\\AskAnythingPrompt;\nuse Jiminny\\Models\\AskAnything\\AskAnythingPromptTarget;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AskAnythingRepository;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\GroupRepository;\nuse Jiminny\\Repositories\\SearchRepository;\nuse Jiminny\\Repositories\\StageRepository;\nuse Throwable;\n\nclass AutomatedReportsService\n{\n public const string TYPE_LOSS_ANALYSIS = 'loss_analysis';\n public const string TYPE_ASK_JIMINNY = 'ask_jiminny';\n\n /**\n * Standard report types (used by kiosk for existing automated reports).\n */\n // @TODO this will add filter, however if we need to control feature by FF we need conditional logic\n public const array TYPES = [\n ['id' => 'exec_summary', 'name' => 'Exec Summary'],\n ['id' => 'coaching_profiles', 'name' => 'Coaching Profiles'],\n ['id' => 'product_feedback', 'name' => 'Product Feedback'],\n ['id' => self::TYPE_LOSS_ANALYSIS, 'name' => 'Loss Analysis'],\n// ['id' => 'questions', 'name' => 'Questions'],\n// ['id' => 'statistical_quant', 'name' => 'Statistical Quantitative'],\n ];\n\n public const array ALL_TYPES = [\n ...self::TYPES,\n ['id' => self::TYPE_ASK_JIMINNY, 'name' => 'Ask Jiminny'],\n ];\n\n public const string FREQUENCY_DAILY = 'daily';\n public const string FREQUENCY_WEEKLY = 'weekly';\n public const string FREQUENCY_MONTHLY = 'monthly';\n public const string FREQUENCY_QUARTERLY = 'quarterly';\n public const string FREQUENCY_ONE_OFF = 'one_off';\n\n /**\n * Frequencies for standard (non-Ask Jiminny) reports.\n */\n public const array FREQUENCIES = [\n ['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],\n ['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],\n ['id' => self::FREQUENCY_QUARTERLY, 'name' => 'Quarterly'],\n ['id' => self::FREQUENCY_ONE_OFF, 'name' => 'One-off'],\n ];\n\n /**\n * Frequencies for Ask Jiminny reports.\n */\n public const array ASK_JIMINNY_FREQUENCIES = [\n ['id' => self::FREQUENCY_DAILY, 'name' => 'Daily'],\n ['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],\n ['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],\n ];\n\n public const string MEDIA_TYPE_PDF = 'pdf';\n public const string MEDIA_TYPE_PODCAST = 'podcast';\n public const array MEDIA_TYPES = [self::MEDIA_TYPE_PDF, self::MEDIA_TYPE_PODCAST];\n public const array MEDIA_TYPE_OBJECT_PDF = ['id' => self::MEDIA_TYPE_PDF, 'name' => 'PDF'];\n public const array MEDIA_TYPE_OBJECT_PODCAST = ['id' => self::MEDIA_TYPE_PODCAST, 'name' => 'Podcast'];\n public const array MEDIA_TYPE_OBJECTS = [self::MEDIA_TYPE_OBJECT_PDF, self::MEDIA_TYPE_OBJECT_PODCAST];\n\n public const array CALL_TYPE_CONFERENCE = ['id' => 'conference', 'name' => 'Conference'];\n public const array CALL_TYPE_DIALER = ['id' => 'dialer', 'name' => 'Dialer'];\n public const int SENT_REPORT_AT_HOURS = 5;\n public const string PDF_KEY = 'pdf';\n public const string AUDIO_KEY = 'audio';\n\n private const array ALL_FREQUENCIES = [\n ['id' => self::FREQUENCY_DAILY, 'name' => 'Daily'],\n ['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],\n ['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],\n ['id' => self::FREQUENCY_QUARTERLY, 'name' => 'Quarterly'],\n ['id' => self::FREQUENCY_ONE_OFF, 'name' => 'One-off'],\n ];\n private const string S3_DIR = 'reports';\n private const array FILE_EXTENSIONS_VARIANTS = ['html', 'MD', 'pdf'];\n private const array FILE_PODCAST_EXTENSIONS_VARIANTS = ['json', 'mp3', 'ssml'];\n\n public function __construct(\n private readonly TeamRepository $teamRepository,\n private readonly GroupRepository $groupRepository,\n private readonly UserRepository $userRepository,\n private readonly StageRepository $stageRepository,\n private readonly DealStagesService $dealStagesService,\n private readonly RecipientsService $recipientsService,\n private readonly AutomatedReportsRepository $automatedReportsRepository,\n private readonly Webhook $webhookService,\n private readonly BusDispatcher $dispatcher,\n private readonly ActivityTypeService $activityTypeService,\n private readonly PlaybookCategoryRepository $playbookCategoryRepository,\n private readonly AskAnythingPromptService $askAnythingPromptService,\n private readonly SearchRepository $activitySearchRepository,\n private readonly AskAnythingRepository $askAnythingRepository,\n ) {\n }\n\n public static function getTypes(): array\n {\n $types = self::TYPES;\n\n return array_map(static function ($type) {\n return $type['id'];\n }, $types);\n }\n\n public static function getCallTypes(): array\n {\n return array_map(static function ($callType) {\n return $callType['id'];\n }, [self::CALL_TYPE_CONFERENCE, self::CALL_TYPE_DIALER]);\n }\n\n public static function getFrequencies(): array\n {\n return array_map(static function ($frequency) {\n return $frequency['id'];\n }, self::FREQUENCIES);\n }\n\n // front-facing structure\n public function getReportEnabledFieldData(bool $value = false): array\n {\n return [\n 'id' => 'report_enabled',\n 'label' => '',\n 'inputType' => InputTypeEnum::TOGGLE,\n 'value' => $value,\n ];\n }\n\n // Organizations = Teams\n public function getOrganizationFieldData(?string $value = null, bool $shortVersion = false): array\n {\n $options = $this->getTeams();\n\n if ($shortVersion) {\n return [\n 'id' => 'organization',\n 'label' => 'Organization',\n 'options' => $options,\n ];\n }\n\n return [\n 'id' => 'organization',\n 'label' => 'Organization',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => $options,\n 'value' => $value,\n 'dependencies' => [\n 'teams',\n 'deal_stage_at_call',\n 'current_deal_stage',\n 'recipients',\n ActivityTypeService::PLAYBOOK_CATEGORIES_KEY,\n ],\n 'dependsOn' => [],\n ];\n }\n\n // Teams = Groups\n public function getTeamFieldData(array $options = [], array $value = [], bool $shortVersion = false): array\n {\n if ($shortVersion) {\n return [\n 'id' => 'teams',\n 'label' => 'Team',\n 'options' => $options,\n ];\n }\n\n return [\n 'id' => 'teams',\n 'label' => 'Team',\n 'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,\n 'required' => false,\n 'placeholder' => 'Select',\n 'options' => $options,\n 'value' => $value, // value should be an array of objects {id, name}\n 'dependencies' => [ActivityTypeService::PLAYBOOK_CATEGORIES_KEY],\n 'dependsOn' => [],\n ];\n }\n\n public function getReportTypeFieldData(?string $value = null, bool $shortVersion = false, ?Team $team = null): array\n {\n $types = [];\n if ($team instanceof Team) {\n if ($team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {\n $types = self::TYPES;\n }\n if ($team->hasFeature(FeatureEnum::ASK_JIMINNY_REPORTS)) {\n $types[] = ['id' => self::TYPE_ASK_JIMINNY, 'name' => 'Ask Jiminny'];\n }\n } else {\n $types = self::TYPES;\n }\n\n if ($shortVersion) {\n return [\n 'id' => 'report_type',\n 'label' => 'Report Type',\n 'options' => $types,\n ];\n }\n\n return [\n 'id' => 'report_type',\n 'label' => 'Report Type',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => $types,\n 'value' => $value,\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getFrequencyFieldData(?string $value = null): array\n {\n return [\n 'id' => 'frequency',\n 'label' => 'Frequency',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => self::FREQUENCIES,\n 'value' => $value,\n 'dependencies' => ['period'],\n 'dependsOn' => [],\n ];\n }\n\n public function getPeriodFieldData(?string $valueStartDate = null, ?string $valueEndDate = null): array\n {\n return [\n 'id' => 'period',\n 'label' => 'Select one-off period',\n 'inputType' => InputTypeEnum::DATE_RANGE,\n 'required' => true,\n 'placeholder' => 'Select',\n 'value' => ['startDate' => $valueStartDate, 'endDate' => $valueEndDate],\n 'queryParams' => [\n 'startDate' => 'start_date_period',\n 'endDate' => 'end_date_period',\n ],\n 'dependencies' => [],\n 'dependsOn' => ['frequency'],\n ];\n }\n\n public function getActivityTypesFieldData(?Team $team = null, array $value = [], array $teamsFilter = []): array\n {\n return $this->activityTypeService->getActivityTypeFieldData(team: $team, value: $value, groupIds: $teamsFilter);\n }\n\n public function getDealStageAtCallFieldData(?Team $team = null, array $value = []): array\n {\n return $this->dealStagesService->getDealStageAtCallFieldData(team: $team, value: $value);\n }\n\n public function getCurrentDealStageFieldData(?Team $team = null, array $value = []): array\n {\n return $this->dealStagesService->getCurrentDealStageFieldData(team: $team, value: $value);\n }\n\n public function getDealValueFieldData(?int $valueMin = null, ?int $valueMax = null): array\n {\n return [\n 'id' => 'deal_value',\n 'label' => 'Deal Value',\n 'inputType' => InputTypeEnum::INTEGER_RANGE,\n 'required' => false,\n 'value' => ['min' => $valueMin, 'max' => $valueMax],\n 'queryParams' => [\n 'min' => 'min_deal_value',\n 'max' => 'max_deal_value',\n ],\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getCallTypeFieldData(bool $conferenceOn = false, bool $dialerOn = false): array\n {\n $value = [];\n $conferenceOn && $value[] = self::CALL_TYPE_CONFERENCE;\n $dialerOn && $value[] = self::CALL_TYPE_DIALER;\n\n return [\n 'id' => 'call_type',\n 'label' => 'Call Type',\n 'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,\n 'required' => true,\n 'options' => [\n self::CALL_TYPE_CONFERENCE,\n self::CALL_TYPE_DIALER,\n ],\n 'value' => $value,\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getMediaTypeFieldData(?AutomatedReport $report = null): array\n {\n $value = [];\n\n if ($report) {\n $value = $this->transformMediaTypes($report);\n }\n\n return [\n 'id' => 'media_types',\n 'label' => 'Export as',\n 'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,\n 'required' => true,\n 'options' => self::MEDIA_TYPE_OBJECTS,\n 'value' => $value,\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getCallDurationFieldData(?int $valueMin = null, ?int $valueMax = null): array\n {\n return [\n 'id' => 'call_duration',\n 'label' => 'Call Duration',\n 'inputType' => InputTypeEnum::INTEGER_RANGE,\n 'required' => false,\n 'value' => ['min' => $valueMin, 'max' => $valueMax],\n 'queryParams' => [\n 'min' => 'min_call_duration',\n 'max' => 'max_call_duration',\n ],\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getRecipientsFieldData(?Team $team = null, array $value = []): array\n {\n return $this->recipientsService->getRecipientsFieldData(team: $team, value: $value);\n }\n\n public function getJiminnyRecipientsFieldData(array $value = []): array\n {\n return $this->recipientsService->getJiminnyRecipientsFieldData($value);\n }\n\n public function getAdditionalPromptInputFieldData(?string $value = null): array\n {\n return [\n 'id' => 'additional_prompt_input',\n 'label' => 'Special requirements',\n 'inputType' => InputTypeEnum::TEXTAREA,\n 'required' => false,\n 'placeholder' => 'What should be the focus of the report?',\n 'value' => $value,\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getCustomReportNameFieldData(?string $value = null): array\n {\n return [\n 'id' => 'custom_name',\n 'label' => 'Custom report name',\n 'inputType' => InputTypeEnum::TEXT,\n 'required' => false,\n 'placeholder' => 'Enter custom name',\n 'value' => $value,\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n // data providers\n public function getTeams(): array\n {\n $teams = $this->teamRepository->getTeamsForKiosk(status: Team::STATUS_ACTIVE);\n\n $teamData = [];\n foreach ($teams as $team) {\n if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {\n continue;\n }\n\n $teamData[] = $this->transformTeam($team);\n }\n\n return $teamData;\n }\n\n public function getTeamGroups(string $teamUuid): array\n {\n $data = [];\n $team = $this->getTeam($teamUuid);\n\n if ($team !== null) {\n $groups = $team->groups()->get();\n\n foreach ($groups as $group) {\n $data[] = [\n 'id' => $group->getUuid(),\n 'name' => $group->getName(),\n ];\n }\n }\n\n return $data;\n }\n\n public function getTeamsGroupsOptions(array $filterTeamUuids = []): array\n {\n $data = [];\n $teams = $this->getTeams();\n\n foreach ($teams as $team) {\n if (! empty($filterTeamUuids) && ! in_array($team['id'], $filterTeamUuids, true)) {\n continue;\n }\n\n $data[] = [\n 'label' => $team['name'],\n 'groups' => $this->getTeamGroups($team['id']),\n ];\n }\n\n return $data;\n }\n\n public function getTeam(string $teamUuid): ?Team\n {\n return $this->teamRepository->idOrUuid($teamUuid);\n }\n\n public function getTeamById(int $teamId): ?Team\n {\n return $this->teamRepository->find($teamId);\n }\n\n public function getGroupsUuids(AutomatedReport $report): array\n {\n $uuids = [];\n $reportGroups = $report->getGroups();\n foreach ($reportGroups as $groupId) {\n if ($group = $this->groupRepository->find($groupId)) {\n $uuids[] = $group->getUuid();\n }\n }\n\n return $uuids;\n }\n\n public function getPlaybookCategoriesUuids(AutomatedReport $report): array\n {\n $uuids = [];\n $playbookCategories = $report->getPlaybookCategories();\n foreach ($playbookCategories as $id) {\n if ($category = $this->playbookCategoryRepository->find($id)) {\n $uuids[] = $category->getUuid();\n }\n }\n\n return $uuids;\n }\n\n public function getDealAtCallStagesUuids(AutomatedReport $report): array\n {\n $uuids = [];\n $reportStages = $report->getDealAtCallStages();\n foreach ($reportStages as $id) {\n if ($stage = $this->stageRepository->find($id)) {\n $uuids[] = $stage->getUuid();\n }\n }\n\n return $uuids;\n }\n\n public function getCurrentDealStagesUuids(AutomatedReport $report): array\n {\n $uuids = [];\n $reportStages = $report->getCurrentDealStages();\n foreach ($reportStages as $id) {\n if ($stage = $this->stageRepository->find($id)) {\n $uuids[] = $stage->getUuid();\n }\n }\n\n return $uuids;\n }\n\n public function getUsersUuids(AutomatedReport $report): array\n {\n return $this->extractUserUuids($report->getRecipients());\n }\n\n public function getJiminnyUsersUuids(AutomatedReport $report): array\n {\n return $this->extractUserUuids($report->getJiminnyRecipients());\n }\n\n /**\n * @param array<string, mixed> $recipients\n */\n private function extractUserUuids(array $recipients): array\n {\n $userIds = $recipients['users'] ?? [];\n\n return collect($userIds)\n ->map(fn ($id) => $this->userRepository->find((int) $id))\n ->filter()\n ->map(fn (UserContract $user) => $user->getUuid())\n ->values()\n ->all();\n }\n\n // get mail data\n public function getRecipientUsers(AutomatedReport $report): array\n {\n return $this->buildRecipientUsers($report->getRecipients());\n }\n\n /**\n * @return array<UserContract>\n */\n public function getRecipientUserObjects(AutomatedReport $report): array\n {\n $userIds = $report->getRecipients()['users'] ?? [];\n\n return collect($userIds)\n ->map(fn ($id) => $this->userRepository->find((int) $id))\n ->filter()\n ->values()\n ->all();\n }\n\n private function getJiminnyRecipientUsers(AutomatedReport $report): array\n {\n return $this->buildRecipientUsers($report->getJiminnyRecipients());\n }\n\n /**\n * @param array<string, mixed> $recipients\n */\n private function buildRecipientUsers(array $recipients): array\n {\n $userIds = $recipients['users'] ?? [];\n\n return collect($userIds)\n ->map(fn ($id) => $this->userRepository->find((int) $id))\n ->filter()\n ->map(fn (UserContract $user) => [\n 'email' => $user->getEmailAddress(),\n 'name' => $user->getName(),\n 'timezone' => $user->getTimezone()->getName(),\n ])\n ->values()\n ->all();\n }\n\n public function getValidRecipientUsers(AutomatedReport $report, bool $includeJiminny = false): array\n {\n if ($report->isAskJiminnyReport()) {\n $recipients = $this->resolveAskJiminnyRecipients($report);\n } else {\n $recipients = $this->getRecipientUsers($report);\n if ($includeJiminny) {\n $recipients = array_merge($recipients, $this->getJiminnyRecipientUsers($report));\n }\n }\n\n $emails = [];\n\n return array_values(array_filter(\n $recipients,\n static function ($recipient) use (&$emails) {\n if (empty($recipient['email']) || in_array($recipient['email'], $emails, true)) {\n return false;\n }\n $emails[] = $recipient['email'];\n\n return true;\n }\n ));\n }\n\n private function resolveAskJiminnyRecipients(AutomatedReport $report): array\n {\n $recipients = [];\n\n $creator = $report->getCreator();\n if ($creator !== null) {\n $recipients[] = [\n 'email' => $creator->getEmailAddress(),\n 'name' => $creator->getName(),\n 'timezone' => $creator->getTimezone()->getName(),\n ];\n }\n\n return array_merge(\n $recipients,\n $this->buildRecipientUsers($report->getRecipients()),\n $this->getGroupRecipientUsers($report),\n );\n }\n\n private function getGroupRecipientUsers(AutomatedReport $report): array\n {\n $users = [];\n foreach ($report->getGroups() as $groupId) {\n $group = $this->groupRepository->find($groupId);\n if ($group === null) {\n continue;\n }\n foreach ($group->getMembers() as $member) {\n $users[] = [\n 'email' => $member->getEmailAddress(),\n 'name' => $member->getName(),\n 'timezone' => $member->getTimezone()->getName(),\n ];\n }\n }\n\n return $users;\n }\n\n public function getReportTypeName(AutomatedReportResult $report): string\n {\n $type = $report->getReport()->getType();\n\n $getType = $this->transformReportType($type);\n\n return $getType['name'];\n }\n\n public function getReportPeriodName(AutomatedReportResult $report): string\n {\n $from = $report->getFromDate();\n $to = $report->getToDate();\n $frequency = $report->getReport()->getFrequency();\n\n if ($from === null || $to === null) {\n if (! $report->getReport()->isAskJiminnyReport()) {\n $invalidPeriod = $from === null ? 'from' : 'to';\n\n throw new ApplicationException('Report period is invalid: ' . $invalidPeriod);\n }\n\n $period = $this->calculateFromAndToDatePeriod($frequency);\n $from = $period['fromDate'];\n $to = $period['toDate'];\n }\n\n return $this->formatReportPeriodName($frequency, $from, $to);\n }\n\n private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string\n {\n $fromYear = $from->format('Y');\n $toYear = $to->format('Y');\n $differentYears = $fromYear !== $toYear;\n\n switch ($frequency) {\n case self::FREQUENCY_DAILY:\n return $from->format('j M Y');\n\n case self::FREQUENCY_QUARTERLY:\n // 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ\n $startMonth = $from->format('M');\n $endMonth = $to->copy()->subMonth();\n $endMonthName = $endMonth->format('M');\n $endMonthYear = $endMonth->format('Y');\n\n if ($differentYears) {\n return \"{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}\";\n }\n\n return \"{$startMonth} - {$endMonthName} {$toYear}\";\n\n case self::FREQUENCY_MONTHLY:\n // 'May 2025' - monthly reports are always within the same year\n return $from->format('M Y');\n\n case self::FREQUENCY_WEEKLY:\n // '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ\n $startDay = $from->format('j');\n $endDay = $to->format('j');\n $startMonth = $from->format('M');\n $endMonth = $to->format('M');\n\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n if ($startMonth !== $endMonth) {\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n return \"{$startDay} - {$endDay} {$endMonth} {$toYear}\";\n\n case self::FREQUENCY_ONE_OFF:\n // '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ\n $startDay = $from->format('j');\n $startMonth = $from->format('M');\n $endDay = $to->format('j');\n $endMonth = $to->format('M');\n\n // If same month and year, use a format like '2-31 May 2025'\n if ($startMonth === $endMonth && ! $differentYears) {\n return \"{$startDay} - {$endDay} {$startMonth} {$toYear}\";\n }\n\n // If different years, include both years\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n // Same year but different months\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n\n default:\n // Default format for unknown frequencies\n return $from->format('j M Y') . ' - ' . $to->format('j M Y');\n }\n }\n\n public function getReportTeamsName(AutomatedReportResult $report): string\n {\n $groups = $report->getGroups();\n\n if (empty($groups)) {\n return 'All';\n }\n\n // Get group names from repository\n $groupNames = [];\n foreach ($groups as $groupId) {\n $group = $this->groupRepository->find($groupId);\n if ($group) {\n $groupNames[] = $group->getName();\n }\n }\n\n if (count($groupNames) === 1) {\n // Single team format\n $teamsName = $groupNames[0];\n } else {\n // Multiple teams format\n $teamsName = implode(', ', $groupNames);\n }\n\n return $teamsName;\n }\n\n public function getReportFileName(AutomatedReportResult $report): string\n {\n $customName = $report->getReport()->getCustomName();\n $periodName = $this->getReportPeriodName($report);\n $filenameSuffix = $this->getFilenameSuffix($report);\n\n if ($customName) {\n if ($filenameSuffix) {\n $customName .= \" {$filenameSuffix}\";\n }\n\n return $this->sanitizeFileName(\"{$customName} - {$periodName}\");\n }\n\n $baseName = $this->getReportTypeName($report);\n\n if ($filenameSuffix) {\n $baseName .= \" {$filenameSuffix}\";\n }\n\n return $this->sanitizeFileName(\"{$baseName} - {$periodName} - {$this->getReportTeamsName($report)}\");\n }\n\n public function getReportFileNameWithExtension(AutomatedReportResult $result): string\n {\n $extension = $this->getMediaTypeMetadata($result)['extension'];\n\n return $this->getReportFileName($result) . '.' . $extension;\n }\n\n public function sanitizeFileName(string $fileName): string\n {\n return str_replace(['/', '\\\\'], '-', $fileName);\n }\n\n public function isUserRecipientOfReport(User $user, AutomatedReport $report): bool\n {\n $recipients = array_map('intval', $report->getRecipients()['users'] ?? []);\n\n return in_array($user->getId(), $recipients);\n }\n\n public function transformReportResults(Collection $automatedReportResults): array\n {\n $data = [];\n foreach ($automatedReportResults as $automatedReportResult) {\n /** @var AutomatedReportResult $automatedReportResult */\n\n $report = $automatedReportResult->getReport();\n\n $createdBy = $report->getCreator();\n $creator = [\n 'id' => $createdBy?->getUuid(),\n 'name' => $createdBy?->getName(),\n 'email' => $createdBy?->getEmailAddress(),\n 'photoUrl' => $createdBy?->getPhotoUrl(),\n ];\n\n $recipients = $this->buildRecipients($report);\n\n $data[] = [\n 'id' => $automatedReportResult->getUuid(),\n 'name' => $automatedReportResult->getName(),\n 'frequency' => $this->transformFrequency($report->getFrequency()),\n 'recipients' => [\n ...array_values($this->transformGroups(team: $report->getTeam(), groupsIds: $report->getGroups())),\n ...array_values($this->transformRecipients($report->getRecipients())),\n ],\n 'report_type' => $this->transformReportType($report->getType()),\n 'media_type' => $automatedReportResult->getMediaType(),\n 'downloadUrl' => $this->generateReportResultDownloadUrl($automatedReportResult),\n 'viewUrl' => $this->generateReportResultViewUrl($automatedReportResult),\n 'generated_at' => $automatedReportResult->getGeneratedAt()?->toIso8601String(),\n 'creator' => $creator,\n ];\n }\n\n return $data;\n }\n\n public function hasCallTypeConference(AutomatedReport $report): bool\n {\n return in_array(self::CALL_TYPE_CONFERENCE['id'], $report->getCallTypes(), true);\n }\n\n public function hasCallTypeDialer(AutomatedReport $report): bool\n {\n return in_array(self::CALL_TYPE_DIALER['id'], $report->getCallTypes(), true);\n }\n\n // transformers\n private function transformTeam(Team $team): array\n {\n if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {\n return [];\n }\n\n return [\n 'id' => $team->getUuid(),\n 'name' => $team->getName(),\n ];\n }\n\n private function transformReportFullView(AutomatedReport $report): array\n {\n $base = $this->transformReportBase($report);\n\n return $report->getType() === self::TYPE_ASK_JIMINNY\n ? $base + $this->transformAskJiminnyFields($report)\n : $base + $this->transformStandardReportFields($report);\n }\n\n private function transformReportBase(AutomatedReport $report): array\n {\n return [\n 'id' => $report->getUuid(),\n 'organization' => $this->transformOrganization(team: $report->getTeam()),\n 'report_type' => $this->transformReportType($report->getType()),\n 'frequency' => $this->transformFrequency($report->getFrequency()),\n ];\n }\n\n private function transformStandardReportFields(AutomatedReport $report): array\n {\n $team = $report->getTeam();\n\n return [\n 'report_enabled' => $report->getStatus(),\n 'start_date_period' => $report->getFrom()?->format('Y-m-d H:i:s'),\n 'end_date_period' => $report->getTo()?->format('Y-m-d H:i:s'),\n 'deal_value_min' => $report->getDealValueMin(),\n 'deal_value_max' => $report->getDealValueMax(),\n 'call_types' => $this->transformCallType($report->getCallTypes()),\n 'media_types' => $this->transformMediaTypes($report),\n 'call_duration_min' => $this->transformDurationToMinutes($report->getCallDurationMin()),\n 'call_duration_max' => $this->transformDurationToMinutes($report->getCallDurationMax()),\n 'teams' => $this->transformGroups(team: $team, groupsIds: $report->getGroups()),\n 'deal_at_call_stages' => $this->transformStages(team: $team, stagesIds: $report->getDealAtCallStages()),\n 'current_deal_stages' => $this->transformStages(team: $team, stagesIds: $report->getCurrentDealStages()),\n 'recipients' => $this->transformRecipients($report->getRecipients()),\n 'created_by' => $this->transformCreator($report->getCreator()),\n 'additional_prompt_input' => $report->getAdditionalPromptInput(),\n 'custom_name' => $report->getCustomName(),\n 'created_at' => $report->getCreatedAt()->format('Y-m-d H:i:s'),\n 'updated_at' => $report->getUpdatedAt()->format('Y-m-d H:i:s'),\n 'deleted_at' => $report->getDeletedAt()?->format('Y-m-d H:i:s'),\n ];\n }\n\n private function transformAskJiminnyFields(AutomatedReport $report): array\n {\n $team = $report->getTeam();\n $creatorId = $report->getAttribute('created_by');\n $explicitUserIds = array_values(array_filter(\n $report->getRecipients()['users'] ?? [],\n static fn ($id) => $id !== $creatorId\n ));\n\n return [\n 'report_name' => $report->getCustomName(),\n 'enabled' => $report->getStatus(),\n 'share_teams' => $this->transformGroups(team: $team, groupsIds: $report->getGroups()),\n 'share_users' => $this->transformRecipients(['users' => $explicitUserIds]),\n 'saved_search' => $this->transformSafeSearch($report->getSavedSearch()),\n 'ask_jiminny_prompt' => $this->transformAskJiminnyPrompt($report->getAskAnythingPrompt()),\n 'expires_on' => $report->getExpiresAt()?->format('Y-m-d'),\n ];\n }\n\n private function transformOrganization(?Team $team): array\n {\n return [\n 'id' => $team?->getUuid(),\n 'name' => $team?->getName(),\n ];\n }\n\n private function transformReportType(string $type): array\n {\n foreach (self::ALL_TYPES as $typeItem) {\n if ($typeItem['id'] === $type) {\n return $typeItem;\n }\n }\n\n return [];\n }\n\n private function transformCallType(array $types): array\n {\n $result = [];\n $callTypes = [self::CALL_TYPE_CONFERENCE, self::CALL_TYPE_DIALER];\n\n foreach ($types as $type) {\n foreach ($callTypes as $callTypeItem) {\n if ($callTypeItem['id'] === $type) {\n $result[] = $callTypeItem;\n\n break;\n }\n }\n }\n\n return $result;\n }\n\n private function transformMediaTypes(AutomatedReport $report): array\n {\n $values = [];\n\n foreach ($report->getMediaTypes() as $mediaType) {\n if (! in_array($mediaType, self::MEDIA_TYPES, true)) {\n continue;\n }\n\n $values[] = match ($mediaType) {\n self::MEDIA_TYPE_PDF => self::MEDIA_TYPE_OBJECT_PDF,\n self::MEDIA_TYPE_PODCAST => self::MEDIA_TYPE_OBJECT_PODCAST,\n };\n }\n\n return $values;\n }\n\n private function transformFrequency(string $frequency): array\n {\n foreach (self::ALL_FREQUENCIES as $frequencyItem) {\n if ($frequencyItem['id'] === $frequency) {\n return $frequencyItem;\n }\n }\n\n return [];\n }\n\n public function transformDurationToMinutes(?int $duration): ?int\n {\n if (! $duration) {\n return null;\n }\n\n return (int) ($duration / 60);\n }\n\n private function transformGroups(?Team $team, array $groupsIds): array\n {\n if (empty($groupsIds) || ! $team) {\n return [];\n }\n\n $data = [];\n foreach ($groupsIds as $groupId) {\n $group = $team->groups()->where('id', $groupId)->first();\n\n if ($group) {\n $data[] = [\n 'id' => $group->getUuid(),\n 'name' => $group->getName(),\n 'photoUrl' => $group->getPhotoUrl(),\n ];\n }\n }\n\n return $data;\n }\n\n private function transformStages(?Team $team, array $stagesIds): array\n {\n if (empty($stagesIds) || ! $team) {\n return [];\n }\n\n $data = [];\n foreach ($stagesIds as $stageId) {\n $stage = $team->stages()->where('id', $stageId)->first();\n\n if ($stage) {\n $data[] = [\n 'id' => $stage->getUuid(),\n 'name' => $stage->getName(),\n ];\n }\n }\n\n return $data;\n }\n\n private function transformRecipients(array $recipients): array\n {\n $users = [];\n foreach ($recipients['users'] ?? [] as $userId) {\n $users[] = $this->transformUser($userId);\n }\n\n return $users;\n }\n\n private function transformCreator(?User $user): ?array\n {\n if ($user === null) {\n return null;\n }\n\n return $this->transformUser($user->getId());\n }\n\n private function transformAskJiminnyPrompt(?AskAnythingPrompt $prompt): ?array\n {\n if ($prompt === null) {\n return null;\n }\n\n return [\n 'id' => $prompt->getUuid(),\n 'name' => $prompt->getTitle(),\n ];\n }\n\n private function transformSafeSearch(?Search $search): ?array\n {\n if ($search === null) {\n return null;\n }\n\n return [\n 'id' => $search->getUuid(),\n 'name' => $search->getName(),\n ];\n }\n\n private function transformUser(int $userId): array\n {\n /* @var ?User $user */\n $user = $this->userRepository->find($userId);\n\n return [\n 'id' => $user?->getUuid(),\n 'name' => $user?->getName(),\n 'email' => $user?->getEmailAddress(),\n 'photoUrl' => $user?->getPhotoUrl(),\n ];\n }\n\n public function create(array $data): array\n {\n $validatedData = $this->validateAndTransformData($data);\n $validatedData['created_by'] = auth()->id();\n\n $automatedReport = $this->automatedReportsRepository->create($validatedData);\n\n $this->generateOneOffReport($automatedReport);\n\n return $this->transformReportFullView($automatedReport);\n }\n\n public function update(string $uuid, array $data): array\n {\n $validatedData = $this->validateAndTransformData($data);\n $report = $this->automatedReportsRepository->findByUuid($uuid);\n\n if (! $report) {\n throw new InvalidArgumentException('Report not found');\n }\n\n $oldCustomName = $report->getCustomName();\n\n $automatedReport = $this->automatedReportsRepository->update($report, $validatedData);\n\n if ($oldCustomName !== $automatedReport->getCustomName()) {\n $this->updateResultNames($automatedReport);\n }\n\n $this->generateOneOffReport($automatedReport);\n\n return $this->transformReportFullView($automatedReport);\n }\n\n /**\n * Create an Ask Jiminny report.\n */\n public function createAskJiminnyReport(array $data, User $creator): array\n {\n $validatedData = $this->validateAskJiminnyReportData($data, $creator);\n $validatedData['created_by'] = $creator->getId();\n\n $automatedReport = $this->automatedReportsRepository->create($validatedData);\n\n return $this->transformReportFullView($automatedReport);\n }\n\n /**\n * Update an Ask Jiminny report.\n */\n public function updateAskJiminnyReport(AutomatedReport $report, array $data, User $user): array\n {\n if (! $report->isAskJiminnyReport()) {\n throw new InvalidArgumentException('Report is not an Ask Jiminny report');\n }\n\n $validatedData = $this->validateAskJiminnyReportData($data, $user);\n\n $oldCustomName = $report->getCustomName();\n\n $automatedReport = $this->automatedReportsRepository->update($report, $validatedData);\n\n if ($oldCustomName !== $automatedReport->getCustomName()) {\n $this->updateResultNames($automatedReport);\n }\n\n return $this->transformReportFullView($automatedReport);\n }\n\n public function updateAskJiminnyReportStatus(AutomatedReport $report, bool $status): array\n {\n $this->automatedReportsRepository->update($report, ['status' => $status]);\n\n return $this->transformReportFullView($report->fresh());\n }\n\n /**\n * Validate and transform data for Ask Jiminny reports.\n */\n private function validateAskJiminnyReportData(array $data, User $user): array\n {\n // Validate name\n $name = trim($data['report_name'] ?? '');\n if (empty($name)) {\n throw new InvalidArgumentException('Report name is required');\n }\n if (mb_strlen($name) > 50) {\n throw new InvalidArgumentException('Report name must be 50 characters or less');\n }\n\n // Validate frequency (only daily, weekly, monthly for Ask Jiminny)\n $frequency = $data['frequency'] ?? null;\n $askJiminnyFrequencies = [self::FREQUENCY_DAILY, self::FREQUENCY_WEEKLY, self::FREQUENCY_MONTHLY];\n if (! in_array($frequency, $askJiminnyFrequencies, true)) {\n throw new InvalidArgumentException('Frequency must be daily, weekly, or monthly');\n }\n\n // Validate expiration date\n $expiresAt = $data['expires_on'] ?? null;\n if (empty($expiresAt)) {\n throw new InvalidArgumentException('Expiration date is required');\n }\n\n try {\n $expiresAtDate = Carbon::parse($expiresAt);\n } catch (InvalidFormatException $e) {\n throw new InvalidArgumentException('Expiration date format is invalid');\n }\n $maxExpiration = Carbon::now()->addYear()->endOfDay();\n if ($expiresAtDate->gt($maxExpiration)) {\n throw new InvalidArgumentException('Expiration date cannot be more than 1 year from now');\n }\n if ($expiresAtDate->isPast()) {\n throw new InvalidArgumentException('Expiration date cannot be in the past');\n }\n\n // Validate saved search\n $activitySearchId = $data['saved_search'] ?? null;\n if (empty($activitySearchId)) {\n throw new InvalidArgumentException('Saved search is required');\n }\n $savedSearch = $this->activitySearchRepository->findByUuidAndUser($activitySearchId, $user);\n if (! $savedSearch) {\n throw new InvalidArgumentException('Saved search not found or does not belong to you');\n }\n\n // Validate saved prompt\n $askAnythingPromptId = $data['ask_jiminny_prompt'] ?? null;\n if (empty($askAnythingPromptId)) {\n throw new InvalidArgumentException('Ask Jiminny prompt is required');\n }\n $prompt = $this->askAnythingRepository->getPromptByUuid($askAnythingPromptId);\n if (! $prompt) {\n throw new InvalidArgumentException('Ask Jiminny prompt not found');\n }\n\n // Validate status\n $status = $data['enabled'] ?? false;\n\n $recipientUserIds = [$user->getId()];\n\n if (! empty($data['share_users'])) {\n $sharedUserIds = $this->validateAndGetUserIdsByTeam(\n $user->team,\n (array) $data['share_users']\n );\n $recipientUserIds = array_merge($recipientUserIds, $sharedUserIds);\n }\n\n $sharedGroupIds = [];\n if (! empty($data['share_teams'])) {\n $sharedGroupIds = $this->validateAndGetGroupIds($user->team, (array) $data['share_teams']);\n }\n\n $recipientUserIds = array_values(array_unique($recipientUserIds));\n\n return [\n 'team_id' => $user->getTeamId(),\n 'type' => self::TYPE_ASK_JIMINNY,\n 'status' => (bool) $status,\n 'frequency' => $frequency,\n 'custom_name' => $name,\n 'activity_search_id' => $savedSearch->getId(),\n 'ask_anything_prompt_id' => $prompt->getId(),\n 'expires_at' => $expiresAtDate->toDateString(),\n 'media_types' => [self::MEDIA_TYPE_PDF],\n 'call_types' => [],\n 'recipients' => ['users' => $recipientUserIds],\n 'groups' => $sharedGroupIds,\n ];\n }\n\n public static function getAskJiminnyFrequencies(): array\n {\n return array_map(static function ($frequency) {\n return $frequency['id'];\n }, self::ASK_JIMINNY_FREQUENCIES);\n }\n\n public function getAskJiminnyReportFilters(User $user): array\n {\n $savedSearches = $this->activitySearchRepository->findByUserOrderedByName($user)\n ->map(fn (Search $search) => [\n 'id' => $search->getUuid(),\n 'name' => $search->getName(),\n ])\n ->values()->all();\n\n $prompts = collect(\n $this->askAnythingPromptService->get($user, AskAnythingPromptTarget::on_demand)\n )->map(fn (AskAnythingPromptDto $prompt) => [\n 'id' => $prompt->id,\n 'name' => $prompt->title,\n ])->values()->all();\n\n return [\n [\n 'id' => 'prompt',\n 'label' => 'Prompt',\n 'options' => $prompts,\n ],\n [\n 'id' => 'saved_search',\n 'label' => 'Saved Search',\n 'options' => $savedSearches,\n ],\n ];\n }\n\n public function getAskJiminnyReportFormData(User $user, ?AutomatedReport $report = null): array\n {\n $team = $user->getTeam();\n $userTimezone = $user->getTimezone();\n\n $savedSearches = $this->activitySearchRepository->findByUserOrderedByName($user)\n ->map(fn (Search $search) => [\n 'id' => $search->getUuid(),\n 'name' => $search->getName(),\n ])\n ->values()->all();\n\n $prompts = collect(\n $this->askAnythingPromptService->get($user, AskAnythingPromptTarget::on_demand)\n )->map(fn (AskAnythingPromptDto $prompt) => [\n 'id' => $prompt->id,\n 'name' => $prompt->title,\n ])->values()->all();\n\n $teamGroups = $this->groupRepository->getAllByTeam($team)->map(fn ($group) => [\n 'id' => $group->getUuid(),\n 'name' => $group->getName(),\n ])->values()->all();\n\n $shareUsers = $this->recipientsService->getRecipientsFieldData(team: $team)['options'] ?? [];\n\n $sharedTeamsValue = [];\n $sharedUsersValue = [];\n if ($report) {\n $sharedTeamsValue = $this->transformGroups($team, $report->getGroups());\n\n $recipientUserIds = $report->getRecipients()['users'] ?? [];\n $creatorId = $report->getAttribute('created_by');\n $sharedUserIds = array_values(array_filter(\n $recipientUserIds,\n static fn ($id) => $id !== $creatorId\n ));\n $sharedUsersValue = collect($sharedUserIds)\n ->map(fn ($id) => $this->userRepository->find((int) $id))\n ->filter()\n ->map(fn (User $u) => [\n 'id' => $u->getUuid(),\n 'name' => $u->getName(),\n ])\n ->values()\n ->all();\n }\n\n return [\n 'fields' => [\n [\n 'id' => 'enabled',\n 'inputType' => InputTypeEnum::TOGGLE,\n 'label' => '',\n 'value' => $report?->getStatus() ?? false,\n ],\n [\n 'id' => 'report_name',\n 'inputType' => InputTypeEnum::TEXT,\n 'label' => 'Name',\n 'placeholder' => 'Enter name',\n 'required' => true,\n 'validation' => ['maxLength' => 50],\n 'value' => $report?->getCustomName() ?? '',\n ],\n [\n 'id' => 'frequency',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'label' => 'Frequency',\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => self::ASK_JIMINNY_FREQUENCIES,\n 'value' => $report ? $this->transformFrequency($report->getFrequency()) : null,\n ],\n [\n 'id' => 'expires_on',\n 'inputType' => InputTypeEnum::DATE,\n 'label' => 'Expires on',\n 'required' => true,\n 'placeholder' => 'Select',\n 'validation' => [\n 'minDate' => now($userTimezone)->toDateString(),\n 'maxDate' => now($userTimezone)->addYear()->toDateString(),\n ],\n 'value' => $report?->getExpiresAt()?->toDateString(),\n ],\n [\n 'id' => 'share_teams',\n 'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,\n 'label' => 'Team',\n 'required' => false,\n 'placeholder' => 'Select',\n 'options' => $teamGroups,\n 'value' => $sharedTeamsValue,\n ],\n [\n 'id' => 'share_users',\n 'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,\n 'label' => 'Team member',\n 'required' => false,\n 'placeholder' => 'Select',\n 'groupLabelKey' => 'label',\n 'groupValuesKey' => 'users',\n 'optionLabelKey' => 'name',\n 'optionValueKey' => 'id',\n 'options' => $shareUsers,\n 'value' => $sharedUsersValue,\n ],\n [\n 'id' => 'saved_search',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'label' => 'Saved search',\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => $savedSearches,\n 'value' => $report && $report->getSavedSearch() ? [\n 'id' => $report->getSavedSearch()->getUuid(),\n 'name' => $report->getSavedSearch()->getName(),\n ] : null,\n ],\n [\n 'id' => 'ask_jiminny_prompt',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'label' => 'Ask Jiminny prompt',\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => $prompts,\n 'value' => $report && $report->getAskAnythingPrompt() ? [\n 'id' => $report->getAskAnythingPrompt()->getUuid(),\n 'name' => $report->getAskAnythingPrompt()->getTitle(),\n ] : null,\n ],\n ],\n ];\n }\n\n private function updateResultNames(AutomatedReport $automatedReport): void\n {\n $results = $this->automatedReportsRepository->getResultsByReport($automatedReport);\n\n foreach ($results as $result) {\n $result->update(['name' => $this->getReportFileName($result)]);\n }\n }\n\n public function updateStatus(string $uuid, array $data): array\n {\n $automatedReport = $this->automatedReportsRepository->findByUuid($uuid);\n\n if (! $automatedReport) {\n throw new ModelNotFoundException('Report not found');\n }\n\n $status = $this->validateReportStatus($data['report_enabled'] ?? null);\n $automatedReport->update([\n 'status' => $status,\n ]);\n\n $this->generateOneOffReport($automatedReport);\n\n return $this->transformReportFullView($automatedReport);\n }\n\n private function generateOneOffReport(AutomatedReport $automatedReport): void\n {\n // the scheduler handles all the other frequency types\n if ($automatedReport->getStatus() === false || $automatedReport->getFrequency() !== self::FREQUENCY_ONE_OFF) {\n return;\n }\n\n $this->dispatcher->dispatch(new RequestGenerateReportJob($automatedReport->getUuid()));\n }\n\n public function getReport(string $uuid): AutomatedReport\n {\n $automatedReport = $this->automatedReportsRepository->findByUuid($uuid);\n\n if (! $automatedReport) {\n throw new ModelNotFoundException('Report not found');\n }\n\n return $automatedReport;\n }\n\n public function get(string $uuid): array\n {\n $automatedReport = $this->getReport($uuid);\n\n return $this->transformReportFullView($automatedReport);\n }\n\n public function list(string $sortColumn = 'created_at', string $sortDirection = 'desc'): array\n {\n $results = [];\n $collection = $this->automatedReportsRepository->getAllStandardReports($sortColumn, $sortDirection);\n\n /** @var AutomatedReport $report */\n foreach ($collection as $report) {\n $results[] = $this->transformReportFullView($report);\n }\n\n return ['data' => $results];\n }\n\n public function listAskJiminnyReports(\n User $user,\n string $sortColumn = 'created_at',\n string $sortDirection = 'desc'\n ): array {\n $results = [];\n $collection = $this->automatedReportsRepository->getAskJiminnyReportsByUser($user, $sortColumn, $sortDirection);\n\n /** @var AutomatedReport $report */\n foreach ($collection as $report) {\n $results[] = $this->transformReportFullView($report);\n }\n\n return ['data' => $results];\n }\n\n public function delete(string $uuid): void\n {\n $automatedReport = $this->automatedReportsRepository->findByUuid($uuid);\n\n if (! $automatedReport) {\n throw new ModelNotFoundException('Report not found');\n }\n\n $automatedReport->delete();\n }\n\n public function createReportResult(AutomatedReport $automatedReport, array $data = []): AutomatedReportResult\n {\n return $this->automatedReportsRepository->createResult(\n array_merge(\n [\n 'report_id' => $automatedReport->getId(),\n 'status' => AutomatedReportResult::STATUS_DEFAULT,\n ],\n $data\n )\n );\n }\n\n public function getOrCreateReportResult(AutomatedReport $automatedReport, array $data = []): AutomatedReportResult\n {\n $existing = $this->automatedReportsRepository->findLatestDefaultOrFailedResult($automatedReport);\n\n if ($existing !== null) {\n $existing->update(['status' => AutomatedReportResult::STATUS_DEFAULT]);\n\n return $existing;\n }\n\n return $this->createReportResult($automatedReport, $data);\n }\n\n public function getReportResult(string $resultUuid): AutomatedReportResult\n {\n $report = $this->automatedReportsRepository->findResultByUuid($resultUuid);\n\n if (! $report) {\n throw new ModelNotFoundException('Report Result not found');\n }\n\n return $report;\n }\n\n public function findChildResult(AutomatedReportResult $result, string $type): ?AutomatedReportResult\n {\n return $this->automatedReportsRepository->findChildResult($result, $type);\n }\n\n // prophet API calls\n /**\n * @throws ApplicationException\n */\n public function getGenerateReportPayload(AutomatedReport $automatedReport, string $reportResultUuid): array\n {\n $period = $this->calculateFromAndToDate($automatedReport);\n $fromDate = $period['fromDate'];\n $toDate = $period['toDate'];\n\n return [\n 'team_id' => $automatedReport->getTeamId(),\n 'request_id' => $reportResultUuid,\n 'report_type' => $automatedReport->getType(),\n 'media_types' => $automatedReport->getMediaTypes(),\n 'from_date' => $fromDate->startOfDay()->format(DateTimeInterface::RFC3339),\n 'to_date' => $toDate->endOfDay()->format(DateTimeInterface::RFC3339),\n 'group_ids' => $automatedReport->getGroups(),\n 'call_deal_stage' => $automatedReport->getDealAtCallStages(),\n 'current_deal_stage' => $automatedReport->getCurrentDealStages(),\n 'deal_min_value' => $automatedReport->getDealValueMin(),\n 'deal_max_value' => $automatedReport->getDealValueMax(),\n 'call_types' => $automatedReport->getCallTypes(),\n 'call_duration_min_seconds' => $automatedReport->getCallDurationMin(),\n 'call_duration_max_seconds' => $automatedReport->getCallDurationMax(),\n 'special_requirements' => $automatedReport->getAdditionalPromptInput(),\n 'callback_url' => $this->getCallbackUrl(),\n 'report_period' => $this->formatReportPeriodName(\n $automatedReport->getFrequency(),\n $fromDate,\n $toDate,\n ),\n 'playbook_categories' => $automatedReport->getPlaybookCategories(),\n 'custom_name' => $automatedReport->getCustomName(),\n ];\n }\n\n // $inputPayload - FE payload structure\n public function getActivitiesCountPayload(array $inputPayload): array\n {\n // Use validateAndTransformData to validate and normalize input\n $validatedData = $this->validateAndTransformData($inputPayload);\n $period = $this->calculateFromAndToDatePeriod(\n $validatedData['frequency'],\n Carbon::parse($validatedData['from']),\n Carbon::parse($validatedData['to']),\n );\n $fromDate = $period['fromDate'];\n $toDate = $period['toDate'];\n\n // Create payload similar to getGenerateReportPayload\n return [\n 'team_id' => $validatedData['team_id'],\n 'group_ids' => $validatedData['groups'] ?? [],\n 'report_type' => $validatedData['type'],\n 'from_date' => $fromDate->format(DateTimeInterface::RFC3339),\n 'to_date' => $toDate->format(DateTimeInterface::RFC3339),\n 'call_deal_stage' => $validatedData['deal_at_call_stages'] ?? [],\n 'current_deal_stage' => $validatedData['current_deal_stages'] ?? [],\n 'deal_min_value' => $validatedData['deal_value_min'] ?? null,\n 'deal_max_value' => $validatedData['deal_value_max'] ?? null,\n 'call_types' => $validatedData['call_types'],\n 'call_duration_min_seconds' => $validatedData['call_duration_min'] ?? null,\n 'call_duration_max_seconds' => $validatedData['call_duration_max'] ?? null,\n 'special_requirements' => $validatedData['additional_prompt_input'] ?? null,\n 'playbook_categories' => $validatedData['playbook_categories'] ?? [],\n 'request_id' => null,\n 'callback_url' => null,\n ];\n }\n\n public function shouldSendReport(array $users, ?CarbonInterface $generatedAt = null): bool\n {\n if (empty($users)) {\n return false;\n }\n\n $earliestTz = collect($users)\n ->mapWithKeys(function (array $user) {\n $tz = new DateTimeZone($user['timezone']);\n $nowUtc = new DateTime('now', new DateTimeZone('UTC'));\n $offset = $tz->getOffset($nowUtc);\n\n return [$user['timezone'] => $offset];\n })\n ->sortDesc()\n ->keys()\n ->first();\n\n $now = Carbon::now($earliestTz);\n $isScheduledTime = (int) $now->format('H') === self::SENT_REPORT_AT_HOURS;\n\n if ($isScheduledTime) {\n return true;\n }\n\n return $this->hasPassedScheduledTime($generatedAt, $earliestTz);\n }\n\n public function hasPassedScheduledTime(?CarbonInterface $generatedAt, string $timezone): bool\n {\n if ($generatedAt === null) {\n return false;\n }\n\n $now = Carbon::now($timezone);\n $scheduledTime = $now->copy()->setTime(self::SENT_REPORT_AT_HOURS, 0, 0);\n\n if ($now->hour < self::SENT_REPORT_AT_HOURS) {\n $scheduledTime = $scheduledTime->subDay();\n }\n\n $scheduledTimeUtc = $scheduledTime->copy()->utc();\n $generatedAtUtc = $generatedAt->copy()->utc();\n $nowUtc = $now->copy()->utc();\n\n return $generatedAtUtc->lt($scheduledTimeUtc) && $nowUtc->gt($scheduledTimeUtc);\n }\n\n public function calculateFromAndToDatePeriod(\n string $frequency,\n ?Carbon $fromDate = null,\n ?Carbon $toDate = null\n ): array {\n if ($frequency === self::FREQUENCY_ONE_OFF) {\n return [\n 'fromDate' => $fromDate,\n 'toDate' => $toDate,\n ];\n }\n\n $now = Carbon::now();\n\n return match ($frequency) {\n self::FREQUENCY_DAILY => [\n 'fromDate' => $now->copy()->subDay()->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_WEEKLY => [\n 'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_MONTHLY => [\n 'fromDate' => $now->copy()->subMonths(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_QUARTERLY => [\n 'fromDate' => $now->copy()->subMonths(3)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n default => throw new InvalidArgumentException(\"Unsupported frequency: {$frequency}\"),\n };\n }\n\n private function calculateFromAndToDate(AutomatedReport $automatedReport): array\n {\n return $this->calculateFromAndToDatePeriod(\n $automatedReport->getFrequency(),\n $automatedReport->getFrom(),\n $automatedReport->getTo()\n );\n }\n\n public function getAskJiminnyGenerateReportPayload(\n AutomatedReport $automatedReport,\n AutomatedReportResult $reportResult,\n array $activityIds,\n ): array {\n return [\n 'user_question' => $automatedReport->getAskAnythingPrompt()?->getContent(),\n 'call_ids' => array_map('strval', $activityIds),\n 'team_id' => $automatedReport->getTeamId(),\n 'request_id' => $reportResult->getUuid(),\n 'callback_url' => $this->getCallbackUrl(),\n 'report_period' => $this->getReportPeriodName($reportResult),\n 'report_name' => $automatedReport->getCustomName(),\n ];\n }\n\n private function getCallbackUrl(): string\n {\n return $this->webhookService->route('jiminny.webhook.reports.ready');\n }\n\n /**\n * Validate and transform payload data for automated reports\n *\n * @param array $data\n *\n * @throws InvalidArgumentException\n *\n * @return array\n */\n private function validateAndTransformData(array $data): array\n {\n // Validate organization (team) and check feature\n $team = $this->validateOrganization($data['organization'] ?? null);\n\n $status = $this->validateReportStatus($data['report_enabled'] ?? null);\n $type = $this->validateReportType($data['report_type'] ?? null);\n $frequency = $this->validateFrequency($data['frequency'] ?? null);\n $additionalPromptInput = $this->validateAdditionalPromptInput(\n $data['additional_prompt_input'] ?? null\n );\n $customReportName = $this->validateCustomReportName($data['custom_name'] ?? null);\n\n // Prepare data for the database\n $reportData = [\n 'team_id' => $team->getId(),\n 'type' => $type,\n 'status' => $status,\n 'frequency' => $frequency,\n 'additional_prompt_input' => $additionalPromptInput,\n 'custom_name' => $customReportName,\n ];\n\n // Validate deal values\n $reportData = $this->validateDealValues($data, $reportData);\n\n // Validate date range\n $reportData = $this->validateDateRange($data, $reportData, $frequency);\n\n // Validate call durations\n $reportData = $this->validateCallDurations($data, $reportData);\n\n // Validate call types\n $reportData = $this->validateCallTypes($data, $reportData);\n\n // Validate media types\n $reportData = $this->validateMediaTypes($data, $reportData);\n\n // Validate groups\n if (isset($data['teams'])) {\n $reportData['groups'] = $this->validateAndGetGroupIds($team, $data['teams']);\n }\n\n // Validate deal stages\n $reportData = $this->validateDealStages($data, $reportData, $team, $type);\n\n // Validate playbook categories\n $reportData = $this->validatePlaybookCategories($data, $reportData, $team);\n\n // Validate recipients\n $reportData['recipients'] = [\n 'users' => $this->validateAndGetUserIdsByTeam($team, $data['recipients'] ?? []),\n ];\n\n if (isset($data['jiminny_recipients'])) {\n // Validate Jiminny recipients\n $reportData['jiminny_recipients'] = [\n 'users' => $this->validateAndGetJiminnyUserIds((array) $data['jiminny_recipients']),\n ];\n }\n\n return $reportData;\n }\n\n private function validateDealValues(array $data, array $reportData): array\n {\n if (isset($data['min_deal_value'])) {\n $reportData['deal_value_min'] = (int) $data['min_deal_value'];\n\n if ($reportData['deal_value_min'] > 4294967295 || $reportData['deal_value_min'] < 0) {\n throw new InvalidArgumentException('Min deal value should be between 0 and 4294967295');\n }\n }\n\n if (isset($data['max_deal_value'])) {\n $reportData['deal_value_max'] = (int) $data['max_deal_value'];\n\n if ($reportData['deal_value_max'] > 4294967295 || $reportData['deal_value_max'] < 0) {\n throw new InvalidArgumentException('Max deal value should be between 0 and 4294967295');\n }\n }\n\n if (isset($data['min_deal_value'], $data['max_deal_value'])\n && $data['min_deal_value'] > $data['max_deal_value']\n ) {\n throw new InvalidArgumentException('Min deal value cannot be greater than max deal value');\n }\n\n return $reportData;\n }\n\n private function validateDateRange(array $data, array $reportData, string $frequency): array\n {\n // Set date range only for one_off frequency\n if ($frequency === 'one_off') {\n if (isset($data['start_date_period'])) {\n $reportData['from'] = $this->parseDate($data['start_date_period']);\n }\n\n if (isset($data['end_date_period'])) {\n $reportData['to'] = $this->parseDate($data['end_date_period']);\n }\n\n if (empty($reportData['from']) || empty($reportData['to'])) {\n throw new InvalidArgumentException(\n 'Start date and end date are required for one_off frequency'\n );\n }\n } else {\n $reportData['from'] = null;\n $reportData['to'] = null;\n }\n\n return $reportData;\n }\n\n private function validateCallDurations(array $data, array $reportData): array\n {\n // Convert call durations from minutes to seconds\n if (isset($data['min_call_duration'])) {\n $reportData['call_duration_min'] = (int) $data['min_call_duration'] * 60;\n\n if ($reportData['call_duration_min'] > 4294967295 || $reportData['call_duration_min'] < 0) {\n throw new InvalidArgumentException('Min call duration should be between 0 and 4294967295');\n }\n }\n\n if (isset($data['max_call_duration'])) {\n $reportData['call_duration_max'] = (int) $data['max_call_duration'] * 60;\n\n if ($reportData['call_duration_max'] > 4294967295 || $reportData['call_duration_max'] < 0) {\n throw new InvalidArgumentException('Max call duration should be between 0 and 4294967295');\n }\n }\n\n return $reportData;\n }\n\n private function validateCallTypes(array $data, array $reportData): array\n {\n // Set call types\n $reportData['call_types'] = $data['call_type'] ?? [];\n if (empty($reportData['call_types'])) {\n $reportData['call_types'] = self::getCallTypes();\n }\n\n foreach ($reportData['call_types'] as $callType) {\n if (! in_array($callType, self::getCallTypes(), true)) {\n throw new InvalidArgumentException(sprintf('Call type %s is invalid', $callType));\n }\n }\n\n return $reportData;\n }\n\n private function validateMediaTypes(array $data, array $reportData): array\n {\n // Set media types from input data\n $reportData['media_types'] = $data['media_types'] ?? [];\n\n if (empty($reportData['media_types'])) {\n throw new InvalidArgumentException('Media types are required');\n }\n\n foreach ($reportData['media_types'] as $mediaType) {\n if (! in_array($mediaType, self::MEDIA_TYPES, true)) {\n throw new InvalidArgumentException(sprintf('Media type %s is invalid', $mediaType));\n }\n }\n\n return $reportData;\n }\n\n private function validateDealStages(array $data, array $reportData, Team $team, string $reportType): array\n {\n // Validate and set deal stages\n if (isset($data['deal_stage_at_call'])) {\n $reportData['deal_at_call_stages'] =\n $this->validateAndGetDealStageIds($team, $data['deal_stage_at_call'], 'Deal stage at call');\n }\n\n if (isset($data['current_deal_stage'])) {\n $reportData['current_deal_stages'] =\n $this->validateAndGetDealStageIds($team, $data['current_deal_stage'], 'Current deal stage');\n }\n\n // Ensure current_deal_stage is not provided for loss_analysis report type\n if ($reportType === self::TYPE_LOSS_ANALYSIS && ! empty($data['current_deal_stage'])) {\n throw new InvalidArgumentException('Current deal stage is not applicable for Loss Analysis reports');\n }\n\n return $reportData;\n }\n\n // transform uuid to id\n private function validatePlaybookCategories(array $data, array $reportData, Team $team): array\n {\n $key = 'playbook_categories';\n\n if (isset($data[$key])) {\n $payloadIds = $data[$key];\n $ids = [];\n\n foreach ($payloadIds as $uuid) {\n $uuid = (string) $uuid;\n\n try {\n $playbookCategory = $this->playbookCategoryRepository->findByUuid($uuid);\n } catch (Throwable $throwable) {\n Log::error(__METHOD__ . ' ' . $throwable->getMessage());\n\n throw new InvalidArgumentException(sprintf('Playbook category %s not found', $uuid));\n }\n\n if (! $playbookCategory) {\n throw new InvalidArgumentException(sprintf('Playbook category %s not found', $uuid));\n }\n\n if (! $playbookCategory->hasPlaybook()) {\n throw new InvalidArgumentException(sprintf('Playbook category %s has no playbook', $uuid));\n }\n\n if ($playbookCategory->getPlaybook()->getTeamId() !== $team->getId()) {\n throw new InvalidArgumentException(\n sprintf('Playbook category %s not found for team %s', $uuid, $team->getUuid())\n );\n }\n\n $ids[] = $playbookCategory->getId();\n }\n\n $reportData[$key] = $ids;\n }\n\n return $reportData;\n }\n\n private function validateReportStatus($status): bool\n {\n if (! in_array($status, [true, false], true)) {\n throw new InvalidArgumentException('Report status is invalid');\n }\n\n return $status;\n }\n\n private function validateReportType($type): string\n {\n if (! in_array($type, self::getTypes(), true)) {\n throw new InvalidArgumentException(sprintf('Report type is invalid: %s', $type));\n }\n\n return $type;\n }\n\n private function validateFrequency($frequency): string\n {\n if (! in_array($frequency, self::getFrequencies(), true)) {\n throw new InvalidArgumentException('Frequency is invalid');\n }\n\n return $frequency;\n }\n\n private function validateAdditionalPromptInput(?string $additionalPromptInput): ?string\n {\n if ($additionalPromptInput && strlen($additionalPromptInput) > 5000) {\n throw new InvalidArgumentException('Additional Prompt Input should be less than 5000 characters');\n }\n\n return $additionalPromptInput;\n }\n\n private function validateCustomReportName(?string $customReportName): ?string\n {\n if ($customReportName === null || $customReportName === '') {\n return null;\n }\n\n if (strlen($customReportName) > 70) {\n throw new InvalidArgumentException('Custom report name should be less than 70 characters');\n }\n\n return $customReportName;\n }\n\n private function validateOrganization(?string $organizationUuid): Team\n {\n if (! $organizationUuid) {\n throw new InvalidArgumentException('Organization is required');\n }\n\n $team = $this->teamRepository->idOrUuid($organizationUuid);\n\n if (! $team) {\n throw new InvalidArgumentException('Organization not found');\n }\n\n if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {\n throw new InvalidArgumentException('Organization does not have the Automated Reports feature');\n }\n\n return $team;\n }\n\n private function validateAndGetGroupIds(Team $team, array $teamUuids): array\n {\n $groupIds = [];\n\n foreach ($teamUuids as $uuid) {\n $group = $this->groupRepository->findByUuid($uuid);\n\n if ($group === null || $group->getTeamId() !== $team->getId()) {\n throw new InvalidArgumentException(\n sprintf('Group %s not found for team %s', $uuid, $team->getUuid())\n );\n }\n\n $groupIds[] = $group->getId();\n\n }\n\n return $groupIds;\n }\n\n private function validateAndGetDealStageIds(Team $team, array $stageUuids, string $propertyLabel): array\n {\n $stageIds = [];\n\n foreach ($stageUuids as $uuid) {\n $stage = $this->stageRepository->findByUuid($uuid);\n\n if ($stage === null || $stage->getTeamId() !== $team->getId()) {\n throw new InvalidArgumentException(\n sprintf('Stage %s not found for team %s for %s', $uuid, $team->getUuid(), $propertyLabel)\n );\n }\n\n $stageIds[] = $stage->getId();\n }\n\n return $stageIds;\n }\n\n private function validateAndGetUserIds(array $userUuids, callable $teamCheck): array\n {\n if (empty($userUuids)) {\n return [];\n }\n\n $userIds = [];\n\n foreach ($userUuids as $uuid) {\n $user = $this->userRepository->findByUuid($uuid);\n\n if (! $user || ! $user->isStatusActive()) {\n throw new InvalidArgumentException(\n sprintf('User %s not found or is not active', $uuid)\n );\n }\n\n if (! $teamCheck($user)) {\n throw new InvalidArgumentException(\n sprintf('User %s does not belong to the allowed team(s)', $uuid)\n );\n }\n\n $userIds[] = $user->getId();\n }\n\n return $userIds;\n }\n\n private function validateAndGetUserIdsByTeam(Team $team, array $userUuids): array\n {\n return $this->validateAndGetUserIds($userUuids, fn ($user) => $user->getTeamId() === $team->getId());\n }\n\n private function validateAndGetJiminnyUserIds(array $userUuids): array\n {\n $allowedTeamIds = config('kiosk.teamIds', []);\n\n return $this->validateAndGetUserIds($userUuids, fn ($user) => in_array($user->getTeamId(), $allowedTeamIds, true));\n }\n\n private function parseDate(string $dateString): string\n {\n return date('Y-m-d H:i:s', strtotime($dateString));\n }\n\n private function generateReportResultViewUrl(AutomatedReportResult $result): string\n {\n $mediaResource = $this->getReportMediaRouteResource($result);\n\n return route('ai-reports.' . $mediaResource . '.view', ['uuid' => $result->getUuid()]);\n }\n\n private function generateReportResultDownloadUrl(AutomatedReportResult $result): string\n {\n $mediaResource = $this->getReportMediaRouteResource($result);\n\n return route('ai-reports.' . $mediaResource . '.download', ['uuid' => $result->getUuid()]);\n }\n\n private function getReportMediaRouteResource(AutomatedReportResult $result): string\n {\n if ($result->getMediaType() === self::MEDIA_TYPE_PDF) {\n return self::PDF_KEY;\n } elseif ($result->getMediaType() === self::MEDIA_TYPE_PODCAST) {\n return self::AUDIO_KEY;\n }\n\n throw new \\InvalidArgumentException('Unknown media type.');\n }\n\n public function getMediaPath(AutomatedReportResult $result): ?string\n {\n $url = match ($result->getMediaType()) {\n self::MEDIA_TYPE_PDF => $result->getPdfUrl(),\n self::MEDIA_TYPE_PODCAST => $result->getPodcastAudioUrl(),\n default => null,\n };\n\n if ($url === null) {\n return null;\n }\n\n $path = parse_url(trim($url, '\"\\''), PHP_URL_PATH);\n\n return $path ?: null;\n }\n\n public function getFilenameSuffix(AutomatedReportResult $result): ?string\n {\n return match ($result->getMediaType()) {\n self::MEDIA_TYPE_PODCAST => 'Podcast',\n default => null,\n };\n }\n\n public function getMailSubjectSuffix(AutomatedReportResult $result): string\n {\n return match ($result->getMediaType()) {\n self::MEDIA_TYPE_PDF => 'report',\n self::MEDIA_TYPE_PODCAST => 'podcast',\n default => '',\n };\n }\n\n public function getMediaTypeMetadata(AutomatedReportResult $result): array\n {\n return match ($result->getMediaType()) {\n self::MEDIA_TYPE_PODCAST => ['extension' => 'mp3', 'mime' => 'audio/mpeg'],\n self::MEDIA_TYPE_PDF => ['extension' => 'pdf', 'mime' => 'application/pdf'],\n default => ['extension' => null, 'mime' => null],\n };\n }\n\n public function deleteS3Files(AutomatedReportResult $result): void\n {\n $teamUuid = $result->getReport()->getTeam()->getUuid();\n $reportUuid = $result->getUuid();\n\n // delete all files for a report uuid no mather of pdf, podcast, or both\n // in case of both - the podcast files are linked to the pdf (parent) uuid\n // pdf and podcast date times should be close\n $path = sprintf('%s/%s/%s', $teamUuid, self::S3_DIR, $reportUuid);\n\n foreach (self::FILE_EXTENSIONS_VARIANTS as $extension) {\n $file = $path . '.' . $extension;\n\n if (Storage::exists($file)) {\n Storage::delete($file);\n Log::info('[Reports] Deleted S3 file', [\n 'path' => $file,\n ]);\n }\n }\n\n foreach (self::FILE_PODCAST_EXTENSIONS_VARIANTS as $extension) {\n $file = $path . '_podcast.' . $extension;\n\n if (Storage::exists($file)) {\n Storage::delete($file);\n Log::info('[Reports] Deleted Podcast S3 file', [\n 'path' => $file,\n ]);\n }\n }\n }\n\n /**\n *\n * @param int|null $teamId Optional team ID to filter results\n *\n * @return Collection<int, int> Collection of team IDs\n */\n public function getTeamIdsWithReportsResults(?int $teamId = null): Collection\n {\n return $this->automatedReportsRepository->getTeamIdsWithReportsResults($teamId);\n }\n\n /**\n * Core delete logic for report results using a query\n *\n * @param Builder $query\n * @param array $logContext\n *\n * @return int\n */\n private function deleteReportResultsByQuery(Builder $query, array $logContext = []): int\n {\n $deletedCount = 0;\n\n if ($query->exists()) {\n Log::info(\n 'Run delete report results',\n array_merge(\n $logContext,\n [\n 'service' => 'AutomatedReportsService',\n ]\n )\n );\n\n $query->chunkById(50, function ($results) use (&$deletedCount, $logContext) {\n foreach ($results as $result) {\n $this->deleteReportResult($result);\n $deletedCount++;\n\n Log::info(\n 'Deleted a report result',\n array_merge(\n $logContext,\n [\n 'result_id' => $result->getId(),\n 'report_id' => $result->getReportId(),\n ]\n )\n );\n }\n });\n }\n\n return $deletedCount;\n }\n\n /**\n * Delete report results for a team by retention period\n *\n * @param Team $team\n * @param CarbonImmutable $retentionDate\n *\n * @return int Number of deleted report results\n */\n public function deleteReportsResultsInRetentionPeriod(Team $team, CarbonImmutable $retentionDate): int\n {\n $reportIds = $this->automatedReportsRepository->getReportIdsByTeam($team);\n\n if ($reportIds->isEmpty()) {\n return 0;\n }\n\n $query = $this->automatedReportsRepository\n ->getReportResultsQueryForRetention($team, $retentionDate);\n\n return $this->deleteReportResultsByQuery($query, [\n 'team_id' => $team->getId(),\n 'retention_date' => $retentionDate->toDateTimeString(),\n ]);\n }\n\n /**\n * Delete ALL report results for a specific automated report\n *\n * @param string $uuid\n *\n * @return int\n */\n public function deleteReportResults(string $uuid): int\n {\n $report = $this->getReport($uuid);\n\n $query = $this->automatedReportsRepository->getResultsByReportQuery($report);\n\n return $this->deleteReportResultsByQuery($query, [\n 'report_uuid' => $uuid,\n 'report_id' => $report->getId(),\n ]);\n }\n\n public function deleteReportResult(AutomatedReportResult $result): void\n {\n $this->deleteS3Files($result);\n\n $result->delete();\n }\n\n /**\n * Get all reports for a specific team\n *\n * @param Team $team\n *\n * @return \\Illuminate\\Database\\Eloquent\\Collection\n */\n public function getTeamReports(Team $team): \\Illuminate\\Database\\Eloquent\\Collection\n {\n return $this->automatedReportsRepository->getReportsByTeam($team);\n }\n\n /**\n * Get all report results for a specific report\n *\n * @param AutomatedReport $report\n *\n * @return \\Illuminate\\Database\\Eloquent\\Collection\n */\n public function getReportResults(AutomatedReport $report): \\Illuminate\\Database\\Eloquent\\Collection\n {\n return $this->automatedReportsRepository->getResultsByReport($report);\n }\n\n public function deleteAllReportResults(AutomatedReport $report): void\n {\n $results = $this->getReportResults($report);\n\n /** @var AutomatedReportResult $result */\n foreach ($results as $result) {\n Log::info('Deleting result', [\n 'report' => $report->getId(),\n 'result' => $result->getId(),\n ]);\n\n $this->deleteReportResult($result);\n }\n }\n\n public function deleteAllData(Team $team): void\n {\n Log::info('Deleting automated report and results for team', [\n 'team' => $team->getId(),\n ]);\n\n $reports = $this->getTeamReports($team);\n\n /** @var AutomatedReport $report */\n foreach ($reports as $report) {\n Log::info('Deleting report', [\n 'team' => $team->getId(),\n 'report' => $report->getId(),\n ]);\n\n $this->deleteAllReportResults($report);\n\n $report->delete();\n }\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Kiosk\\AutomatedReports;\n\nuse Carbon\\CarbonImmutable;\nuse Carbon\\CarbonInterface;\nuse Carbon\\Exceptions\\InvalidFormatException;\nuse DateTime;\nuse DateTimeInterface;\nuse DateTimeZone;\nuse Illuminate\\Contracts\\Bus\\Dispatcher as BusDispatcher;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Support\\Carbon;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\Log;\nuse Illuminate\\Support\\Facades\\Storage;\nuse Jiminny\\Component\\ActivitySearch\\FilterDefinition\\InputTypeEnum;\nuse Jiminny\\Component\\AskAnything\\AskAnythingPromptService;\nuse Jiminny\\Component\\AskAnything\\Dtos\\AskAnythingPromptDto;\nuse Jiminny\\Component\\UrlGenerator\\Webhook;\nuse Jiminny\\Contracts\\Repositories\\PlaybookCategoryRepository;\nuse Jiminny\\Contracts\\Repositories\\TeamRepository;\nuse Jiminny\\Contracts\\Repositories\\UserRepository;\nuse Jiminny\\Exceptions\\ApplicationException;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Exceptions\\ModelNotFoundException;\nuse Jiminny\\Jobs\\AutomatedReports\\RequestGenerateReportJob;\nuse Jiminny\\Models\\Activity\\Search;\nuse Jiminny\\Models\\AskAnything\\AskAnythingPrompt;\nuse Jiminny\\Models\\AskAnything\\AskAnythingPromptTarget;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AskAnythingRepository;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\GroupRepository;\nuse Jiminny\\Repositories\\SearchRepository;\nuse Jiminny\\Repositories\\StageRepository;\nuse Throwable;\n\nclass AutomatedReportsService\n{\n public const string TYPE_LOSS_ANALYSIS = 'loss_analysis';\n public const string TYPE_ASK_JIMINNY = 'ask_jiminny';\n\n /**\n * Standard report types (used by kiosk for existing automated reports).\n */\n // @TODO this will add filter, however if we need to control feature by FF we need conditional logic\n public const array TYPES = [\n ['id' => 'exec_summary', 'name' => 'Exec Summary'],\n ['id' => 'coaching_profiles', 'name' => 'Coaching Profiles'],\n ['id' => 'product_feedback', 'name' => 'Product Feedback'],\n ['id' => self::TYPE_LOSS_ANALYSIS, 'name' => 'Loss Analysis'],\n// ['id' => 'questions', 'name' => 'Questions'],\n// ['id' => 'statistical_quant', 'name' => 'Statistical Quantitative'],\n ];\n\n public const array ALL_TYPES = [\n ...self::TYPES,\n ['id' => self::TYPE_ASK_JIMINNY, 'name' => 'Ask Jiminny'],\n ];\n\n public const string FREQUENCY_DAILY = 'daily';\n public const string FREQUENCY_WEEKLY = 'weekly';\n public const string FREQUENCY_MONTHLY = 'monthly';\n public const string FREQUENCY_QUARTERLY = 'quarterly';\n public const string FREQUENCY_ONE_OFF = 'one_off';\n\n /**\n * Frequencies for standard (non-Ask Jiminny) reports.\n */\n public const array FREQUENCIES = [\n ['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],\n ['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],\n ['id' => self::FREQUENCY_QUARTERLY, 'name' => 'Quarterly'],\n ['id' => self::FREQUENCY_ONE_OFF, 'name' => 'One-off'],\n ];\n\n /**\n * Frequencies for Ask Jiminny reports.\n */\n public const array ASK_JIMINNY_FREQUENCIES = [\n ['id' => self::FREQUENCY_DAILY, 'name' => 'Daily'],\n ['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],\n ['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],\n ];\n\n public const string MEDIA_TYPE_PDF = 'pdf';\n public const string MEDIA_TYPE_PODCAST = 'podcast';\n public const array MEDIA_TYPES = [self::MEDIA_TYPE_PDF, self::MEDIA_TYPE_PODCAST];\n public const array MEDIA_TYPE_OBJECT_PDF = ['id' => self::MEDIA_TYPE_PDF, 'name' => 'PDF'];\n public const array MEDIA_TYPE_OBJECT_PODCAST = ['id' => self::MEDIA_TYPE_PODCAST, 'name' => 'Podcast'];\n public const array MEDIA_TYPE_OBJECTS = [self::MEDIA_TYPE_OBJECT_PDF, self::MEDIA_TYPE_OBJECT_PODCAST];\n\n public const array CALL_TYPE_CONFERENCE = ['id' => 'conference', 'name' => 'Conference'];\n public const array CALL_TYPE_DIALER = ['id' => 'dialer', 'name' => 'Dialer'];\n public const int SENT_REPORT_AT_HOURS = 5;\n public const string PDF_KEY = 'pdf';\n public const string AUDIO_KEY = 'audio';\n\n private const array ALL_FREQUENCIES = [\n ['id' => self::FREQUENCY_DAILY, 'name' => 'Daily'],\n ['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],\n ['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],\n ['id' => self::FREQUENCY_QUARTERLY, 'name' => 'Quarterly'],\n ['id' => self::FREQUENCY_ONE_OFF, 'name' => 'One-off'],\n ];\n private const string S3_DIR = 'reports';\n private const array FILE_EXTENSIONS_VARIANTS = ['html', 'MD', 'pdf'];\n private const array FILE_PODCAST_EXTENSIONS_VARIANTS = ['json', 'mp3', 'ssml'];\n\n public function __construct(\n private readonly TeamRepository $teamRepository,\n private readonly GroupRepository $groupRepository,\n private readonly UserRepository $userRepository,\n private readonly StageRepository $stageRepository,\n private readonly DealStagesService $dealStagesService,\n private readonly RecipientsService $recipientsService,\n private readonly AutomatedReportsRepository $automatedReportsRepository,\n private readonly Webhook $webhookService,\n private readonly BusDispatcher $dispatcher,\n private readonly ActivityTypeService $activityTypeService,\n private readonly PlaybookCategoryRepository $playbookCategoryRepository,\n private readonly AskAnythingPromptService $askAnythingPromptService,\n private readonly SearchRepository $activitySearchRepository,\n private readonly AskAnythingRepository $askAnythingRepository,\n ) {\n }\n\n public static function getTypes(): array\n {\n $types = self::TYPES;\n\n return array_map(static function ($type) {\n return $type['id'];\n }, $types);\n }\n\n public static function getCallTypes(): array\n {\n return array_map(static function ($callType) {\n return $callType['id'];\n }, [self::CALL_TYPE_CONFERENCE, self::CALL_TYPE_DIALER]);\n }\n\n public static function getFrequencies(): array\n {\n return array_map(static function ($frequency) {\n return $frequency['id'];\n }, self::FREQUENCIES);\n }\n\n // front-facing structure\n public function getReportEnabledFieldData(bool $value = false): array\n {\n return [\n 'id' => 'report_enabled',\n 'label' => '',\n 'inputType' => InputTypeEnum::TOGGLE,\n 'value' => $value,\n ];\n }\n\n // Organizations = Teams\n public function getOrganizationFieldData(?string $value = null, bool $shortVersion = false): array\n {\n $options = $this->getTeams();\n\n if ($shortVersion) {\n return [\n 'id' => 'organization',\n 'label' => 'Organization',\n 'options' => $options,\n ];\n }\n\n return [\n 'id' => 'organization',\n 'label' => 'Organization',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => $options,\n 'value' => $value,\n 'dependencies' => [\n 'teams',\n 'deal_stage_at_call',\n 'current_deal_stage',\n 'recipients',\n ActivityTypeService::PLAYBOOK_CATEGORIES_KEY,\n ],\n 'dependsOn' => [],\n ];\n }\n\n // Teams = Groups\n public function getTeamFieldData(array $options = [], array $value = [], bool $shortVersion = false): array\n {\n if ($shortVersion) {\n return [\n 'id' => 'teams',\n 'label' => 'Team',\n 'options' => $options,\n ];\n }\n\n return [\n 'id' => 'teams',\n 'label' => 'Team',\n 'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,\n 'required' => false,\n 'placeholder' => 'Select',\n 'options' => $options,\n 'value' => $value, // value should be an array of objects {id, name}\n 'dependencies' => [ActivityTypeService::PLAYBOOK_CATEGORIES_KEY],\n 'dependsOn' => [],\n ];\n }\n\n public function getReportTypeFieldData(?string $value = null, bool $shortVersion = false, ?Team $team = null): array\n {\n $types = [];\n if ($team instanceof Team) {\n if ($team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {\n $types = self::TYPES;\n }\n if ($team->hasFeature(FeatureEnum::ASK_JIMINNY_REPORTS)) {\n $types[] = ['id' => self::TYPE_ASK_JIMINNY, 'name' => 'Ask Jiminny'];\n }\n } else {\n $types = self::TYPES;\n }\n\n if ($shortVersion) {\n return [\n 'id' => 'report_type',\n 'label' => 'Report Type',\n 'options' => $types,\n ];\n }\n\n return [\n 'id' => 'report_type',\n 'label' => 'Report Type',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => $types,\n 'value' => $value,\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getFrequencyFieldData(?string $value = null): array\n {\n return [\n 'id' => 'frequency',\n 'label' => 'Frequency',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => self::FREQUENCIES,\n 'value' => $value,\n 'dependencies' => ['period'],\n 'dependsOn' => [],\n ];\n }\n\n public function getPeriodFieldData(?string $valueStartDate = null, ?string $valueEndDate = null): array\n {\n return [\n 'id' => 'period',\n 'label' => 'Select one-off period',\n 'inputType' => InputTypeEnum::DATE_RANGE,\n 'required' => true,\n 'placeholder' => 'Select',\n 'value' => ['startDate' => $valueStartDate, 'endDate' => $valueEndDate],\n 'queryParams' => [\n 'startDate' => 'start_date_period',\n 'endDate' => 'end_date_period',\n ],\n 'dependencies' => [],\n 'dependsOn' => ['frequency'],\n ];\n }\n\n public function getActivityTypesFieldData(?Team $team = null, array $value = [], array $teamsFilter = []): array\n {\n return $this->activityTypeService->getActivityTypeFieldData(team: $team, value: $value, groupIds: $teamsFilter);\n }\n\n public function getDealStageAtCallFieldData(?Team $team = null, array $value = []): array\n {\n return $this->dealStagesService->getDealStageAtCallFieldData(team: $team, value: $value);\n }\n\n public function getCurrentDealStageFieldData(?Team $team = null, array $value = []): array\n {\n return $this->dealStagesService->getCurrentDealStageFieldData(team: $team, value: $value);\n }\n\n public function getDealValueFieldData(?int $valueMin = null, ?int $valueMax = null): array\n {\n return [\n 'id' => 'deal_value',\n 'label' => 'Deal Value',\n 'inputType' => InputTypeEnum::INTEGER_RANGE,\n 'required' => false,\n 'value' => ['min' => $valueMin, 'max' => $valueMax],\n 'queryParams' => [\n 'min' => 'min_deal_value',\n 'max' => 'max_deal_value',\n ],\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getCallTypeFieldData(bool $conferenceOn = false, bool $dialerOn = false): array\n {\n $value = [];\n $conferenceOn && $value[] = self::CALL_TYPE_CONFERENCE;\n $dialerOn && $value[] = self::CALL_TYPE_DIALER;\n\n return [\n 'id' => 'call_type',\n 'label' => 'Call Type',\n 'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,\n 'required' => true,\n 'options' => [\n self::CALL_TYPE_CONFERENCE,\n self::CALL_TYPE_DIALER,\n ],\n 'value' => $value,\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getMediaTypeFieldData(?AutomatedReport $report = null): array\n {\n $value = [];\n\n if ($report) {\n $value = $this->transformMediaTypes($report);\n }\n\n return [\n 'id' => 'media_types',\n 'label' => 'Export as',\n 'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,\n 'required' => true,\n 'options' => self::MEDIA_TYPE_OBJECTS,\n 'value' => $value,\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getCallDurationFieldData(?int $valueMin = null, ?int $valueMax = null): array\n {\n return [\n 'id' => 'call_duration',\n 'label' => 'Call Duration',\n 'inputType' => InputTypeEnum::INTEGER_RANGE,\n 'required' => false,\n 'value' => ['min' => $valueMin, 'max' => $valueMax],\n 'queryParams' => [\n 'min' => 'min_call_duration',\n 'max' => 'max_call_duration',\n ],\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getRecipientsFieldData(?Team $team = null, array $value = []): array\n {\n return $this->recipientsService->getRecipientsFieldData(team: $team, value: $value);\n }\n\n public function getJiminnyRecipientsFieldData(array $value = []): array\n {\n return $this->recipientsService->getJiminnyRecipientsFieldData($value);\n }\n\n public function getAdditionalPromptInputFieldData(?string $value = null): array\n {\n return [\n 'id' => 'additional_prompt_input',\n 'label' => 'Special requirements',\n 'inputType' => InputTypeEnum::TEXTAREA,\n 'required' => false,\n 'placeholder' => 'What should be the focus of the report?',\n 'value' => $value,\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getCustomReportNameFieldData(?string $value = null): array\n {\n return [\n 'id' => 'custom_name',\n 'label' => 'Custom report name',\n 'inputType' => InputTypeEnum::TEXT,\n 'required' => false,\n 'placeholder' => 'Enter custom name',\n 'value' => $value,\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n // data providers\n public function getTeams(): array\n {\n $teams = $this->teamRepository->getTeamsForKiosk(status: Team::STATUS_ACTIVE);\n\n $teamData = [];\n foreach ($teams as $team) {\n if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {\n continue;\n }\n\n $teamData[] = $this->transformTeam($team);\n }\n\n return $teamData;\n }\n\n public function getTeamGroups(string $teamUuid): array\n {\n $data = [];\n $team = $this->getTeam($teamUuid);\n\n if ($team !== null) {\n $groups = $team->groups()->get();\n\n foreach ($groups as $group) {\n $data[] = [\n 'id' => $group->getUuid(),\n 'name' => $group->getName(),\n ];\n }\n }\n\n return $data;\n }\n\n public function getTeamsGroupsOptions(array $filterTeamUuids = []): array\n {\n $data = [];\n $teams = $this->getTeams();\n\n foreach ($teams as $team) {\n if (! empty($filterTeamUuids) && ! in_array($team['id'], $filterTeamUuids, true)) {\n continue;\n }\n\n $data[] = [\n 'label' => $team['name'],\n 'groups' => $this->getTeamGroups($team['id']),\n ];\n }\n\n return $data;\n }\n\n public function getTeam(string $teamUuid): ?Team\n {\n return $this->teamRepository->idOrUuid($teamUuid);\n }\n\n public function getTeamById(int $teamId): ?Team\n {\n return $this->teamRepository->find($teamId);\n }\n\n public function getGroupsUuids(AutomatedReport $report): array\n {\n $uuids = [];\n $reportGroups = $report->getGroups();\n foreach ($reportGroups as $groupId) {\n if ($group = $this->groupRepository->find($groupId)) {\n $uuids[] = $group->getUuid();\n }\n }\n\n return $uuids;\n }\n\n public function getPlaybookCategoriesUuids(AutomatedReport $report): array\n {\n $uuids = [];\n $playbookCategories = $report->getPlaybookCategories();\n foreach ($playbookCategories as $id) {\n if ($category = $this->playbookCategoryRepository->find($id)) {\n $uuids[] = $category->getUuid();\n }\n }\n\n return $uuids;\n }\n\n public function getDealAtCallStagesUuids(AutomatedReport $report): array\n {\n $uuids = [];\n $reportStages = $report->getDealAtCallStages();\n foreach ($reportStages as $id) {\n if ($stage = $this->stageRepository->find($id)) {\n $uuids[] = $stage->getUuid();\n }\n }\n\n return $uuids;\n }\n\n public function getCurrentDealStagesUuids(AutomatedReport $report): array\n {\n $uuids = [];\n $reportStages = $report->getCurrentDealStages();\n foreach ($reportStages as $id) {\n if ($stage = $this->stageRepository->find($id)) {\n $uuids[] = $stage->getUuid();\n }\n }\n\n return $uuids;\n }\n\n public function getUsersUuids(AutomatedReport $report): array\n {\n return $this->extractUserUuids($report->getRecipients());\n }\n\n public function getJiminnyUsersUuids(AutomatedReport $report): array\n {\n return $this->extractUserUuids($report->getJiminnyRecipients());\n }\n\n /**\n * @param array<string, mixed> $recipients\n */\n private function extractUserUuids(array $recipients): array\n {\n $userIds = $recipients['users'] ?? [];\n\n return collect($userIds)\n ->map(fn ($id) => $this->userRepository->find((int) $id))\n ->filter()\n ->map(fn (UserContract $user) => $user->getUuid())\n ->values()\n ->all();\n }\n\n // get mail data\n public function getRecipientUsers(AutomatedReport $report): array\n {\n return $this->buildRecipientUsers($report->getRecipients());\n }\n\n /**\n * @return array<UserContract>\n */\n public function getRecipientUserObjects(AutomatedReport $report): array\n {\n $userIds = $report->getRecipients()['users'] ?? [];\n\n return collect($userIds)\n ->map(fn ($id) => $this->userRepository->find((int) $id))\n ->filter()\n ->values()\n ->all();\n }\n\n private function getJiminnyRecipientUsers(AutomatedReport $report): array\n {\n return $this->buildRecipientUsers($report->getJiminnyRecipients());\n }\n\n /**\n * @param array<string, mixed> $recipients\n */\n private function buildRecipientUsers(array $recipients): array\n {\n $userIds = $recipients['users'] ?? [];\n\n return collect($userIds)\n ->map(fn ($id) => $this->userRepository->find((int) $id))\n ->filter()\n ->map(fn (UserContract $user) => [\n 'email' => $user->getEmailAddress(),\n 'name' => $user->getName(),\n 'timezone' => $user->getTimezone()->getName(),\n ])\n ->values()\n ->all();\n }\n\n public function getValidRecipientUsers(AutomatedReport $report, bool $includeJiminny = false): array\n {\n if ($report->isAskJiminnyReport()) {\n $recipients = $this->resolveAskJiminnyRecipients($report);\n } else {\n $recipients = $this->getRecipientUsers($report);\n if ($includeJiminny) {\n $recipients = array_merge($recipients, $this->getJiminnyRecipientUsers($report));\n }\n }\n\n $emails = [];\n\n return array_values(array_filter(\n $recipients,\n static function ($recipient) use (&$emails) {\n if (empty($recipient['email']) || in_array($recipient['email'], $emails, true)) {\n return false;\n }\n $emails[] = $recipient['email'];\n\n return true;\n }\n ));\n }\n\n private function resolveAskJiminnyRecipients(AutomatedReport $report): array\n {\n $recipients = [];\n\n $creator = $report->getCreator();\n if ($creator !== null) {\n $recipients[] = [\n 'email' => $creator->getEmailAddress(),\n 'name' => $creator->getName(),\n 'timezone' => $creator->getTimezone()->getName(),\n ];\n }\n\n return array_merge(\n $recipients,\n $this->buildRecipientUsers($report->getRecipients()),\n $this->getGroupRecipientUsers($report),\n );\n }\n\n private function getGroupRecipientUsers(AutomatedReport $report): array\n {\n $users = [];\n foreach ($report->getGroups() as $groupId) {\n $group = $this->groupRepository->find($groupId);\n if ($group === null) {\n continue;\n }\n foreach ($group->getMembers() as $member) {\n $users[] = [\n 'email' => $member->getEmailAddress(),\n 'name' => $member->getName(),\n 'timezone' => $member->getTimezone()->getName(),\n ];\n }\n }\n\n return $users;\n }\n\n public function getReportTypeName(AutomatedReportResult $report): string\n {\n $type = $report->getReport()->getType();\n\n $getType = $this->transformReportType($type);\n\n return $getType['name'];\n }\n\n public function getReportPeriodName(AutomatedReportResult $report): string\n {\n $from = $report->getFromDate();\n $to = $report->getToDate();\n $frequency = $report->getReport()->getFrequency();\n\n if ($from === null || $to === null) {\n if (! $report->getReport()->isAskJiminnyReport()) {\n $invalidPeriod = $from === null ? 'from' : 'to';\n\n throw new ApplicationException('Report period is invalid: ' . $invalidPeriod);\n }\n\n $period = $this->calculateFromAndToDatePeriod($frequency);\n $from = $period['fromDate'];\n $to = $period['toDate'];\n }\n\n return $this->formatReportPeriodName($frequency, $from, $to);\n }\n\n private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string\n {\n $fromYear = $from->format('Y');\n $toYear = $to->format('Y');\n $differentYears = $fromYear !== $toYear;\n\n switch ($frequency) {\n case self::FREQUENCY_DAILY:\n return $from->format('j M Y');\n\n case self::FREQUENCY_QUARTERLY:\n // 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ\n $startMonth = $from->format('M');\n $endMonth = $to->copy()->subMonth();\n $endMonthName = $endMonth->format('M');\n $endMonthYear = $endMonth->format('Y');\n\n if ($differentYears) {\n return \"{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}\";\n }\n\n return \"{$startMonth} - {$endMonthName} {$toYear}\";\n\n case self::FREQUENCY_MONTHLY:\n // 'May 2025' - monthly reports are always within the same year\n return $from->format('M Y');\n\n case self::FREQUENCY_WEEKLY:\n // '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ\n $startDay = $from->format('j');\n $endDay = $to->format('j');\n $startMonth = $from->format('M');\n $endMonth = $to->format('M');\n\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n if ($startMonth !== $endMonth) {\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n return \"{$startDay} - {$endDay} {$endMonth} {$toYear}\";\n\n case self::FREQUENCY_ONE_OFF:\n // '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ\n $startDay = $from->format('j');\n $startMonth = $from->format('M');\n $endDay = $to->format('j');\n $endMonth = $to->format('M');\n\n // If same month and year, use a format like '2-31 May 2025'\n if ($startMonth === $endMonth && ! $differentYears) {\n return \"{$startDay} - {$endDay} {$startMonth} {$toYear}\";\n }\n\n // If different years, include both years\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n // Same year but different months\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n\n default:\n // Default format for unknown frequencies\n return $from->format('j M Y') . ' - ' . $to->format('j M Y');\n }\n }\n\n public function getReportTeamsName(AutomatedReportResult $report): string\n {\n $groups = $report->getGroups();\n\n if (empty($groups)) {\n return 'All';\n }\n\n // Get group names from repository\n $groupNames = [];\n foreach ($groups as $groupId) {\n $group = $this->groupRepository->find($groupId);\n if ($group) {\n $groupNames[] = $group->getName();\n }\n }\n\n if (count($groupNames) === 1) {\n // Single team format\n $teamsName = $groupNames[0];\n } else {\n // Multiple teams format\n $teamsName = implode(', ', $groupNames);\n }\n\n return $teamsName;\n }\n\n public function getReportFileName(AutomatedReportResult $report): string\n {\n $customName = $report->getReport()->getCustomName();\n $periodName = $this->getReportPeriodName($report);\n $filenameSuffix = $this->getFilenameSuffix($report);\n\n if ($customName) {\n if ($filenameSuffix) {\n $customName .= \" {$filenameSuffix}\";\n }\n\n return $this->sanitizeFileName(\"{$customName} - {$periodName}\");\n }\n\n $baseName = $this->getReportTypeName($report);\n\n if ($filenameSuffix) {\n $baseName .= \" {$filenameSuffix}\";\n }\n\n return $this->sanitizeFileName(\"{$baseName} - {$periodName} - {$this->getReportTeamsName($report)}\");\n }\n\n public function getReportFileNameWithExtension(AutomatedReportResult $result): string\n {\n $extension = $this->getMediaTypeMetadata($result)['extension'];\n\n return $this->getReportFileName($result) . '.' . $extension;\n }\n\n public function sanitizeFileName(string $fileName): string\n {\n return str_replace(['/', '\\\\'], '-', $fileName);\n }\n\n public function isUserRecipientOfReport(User $user, AutomatedReport $report): bool\n {\n $recipients = array_map('intval', $report->getRecipients()['users'] ?? []);\n\n return in_array($user->getId(), $recipients);\n }\n\n public function transformReportResults(Collection $automatedReportResults): array\n {\n $data = [];\n foreach ($automatedReportResults as $automatedReportResult) {\n /** @var AutomatedReportResult $automatedReportResult */\n\n $report = $automatedReportResult->getReport();\n\n $createdBy = $report->getCreator();\n $creator = [\n 'id' => $createdBy?->getUuid(),\n 'name' => $createdBy?->getName(),\n 'email' => $createdBy?->getEmailAddress(),\n 'photoUrl' => $createdBy?->getPhotoUrl(),\n ];\n\n $recipients = $this->buildRecipients($report);\n\n $data[] = [\n 'id' => $automatedReportResult->getUuid(),\n 'name' => $automatedReportResult->getName(),\n 'frequency' => $this->transformFrequency($report->getFrequency()),\n 'recipients' => [\n ...array_values($this->transformGroups(team: $report->getTeam(), groupsIds: $report->getGroups())),\n ...array_values($this->transformRecipients($report->getRecipients())),\n ],\n 'report_type' => $this->transformReportType($report->getType()),\n 'media_type' => $automatedReportResult->getMediaType(),\n 'downloadUrl' => $this->generateReportResultDownloadUrl($automatedReportResult),\n 'viewUrl' => $this->generateReportResultViewUrl($automatedReportResult),\n 'generated_at' => $automatedReportResult->getGeneratedAt()?->toIso8601String(),\n 'creator' => $creator,\n ];\n }\n\n return $data;\n }\n\n public function hasCallTypeConference(AutomatedReport $report): bool\n {\n return in_array(self::CALL_TYPE_CONFERENCE['id'], $report->getCallTypes(), true);\n }\n\n public function hasCallTypeDialer(AutomatedReport $report): bool\n {\n return in_array(self::CALL_TYPE_DIALER['id'], $report->getCallTypes(), true);\n }\n\n // transformers\n private function transformTeam(Team $team): array\n {\n if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {\n return [];\n }\n\n return [\n 'id' => $team->getUuid(),\n 'name' => $team->getName(),\n ];\n }\n\n private function transformReportFullView(AutomatedReport $report): array\n {\n $base = $this->transformReportBase($report);\n\n return $report->getType() === self::TYPE_ASK_JIMINNY\n ? $base + $this->transformAskJiminnyFields($report)\n : $base + $this->transformStandardReportFields($report);\n }\n\n private function transformReportBase(AutomatedReport $report): array\n {\n return [\n 'id' => $report->getUuid(),\n 'organization' => $this->transformOrganization(team: $report->getTeam()),\n 'report_type' => $this->transformReportType($report->getType()),\n 'frequency' => $this->transformFrequency($report->getFrequency()),\n ];\n }\n\n private function transformStandardReportFields(AutomatedReport $report): array\n {\n $team = $report->getTeam();\n\n return [\n 'report_enabled' => $report->getStatus(),\n 'start_date_period' => $report->getFrom()?->format('Y-m-d H:i:s'),\n 'end_date_period' => $report->getTo()?->format('Y-m-d H:i:s'),\n 'deal_value_min' => $report->getDealValueMin(),\n 'deal_value_max' => $report->getDealValueMax(),\n 'call_types' => $this->transformCallType($report->getCallTypes()),\n 'media_types' => $this->transformMediaTypes($report),\n 'call_duration_min' => $this->transformDurationToMinutes($report->getCallDurationMin()),\n 'call_duration_max' => $this->transformDurationToMinutes($report->getCallDurationMax()),\n 'teams' => $this->transformGroups(team: $team, groupsIds: $report->getGroups()),\n 'deal_at_call_stages' => $this->transformStages(team: $team, stagesIds: $report->getDealAtCallStages()),\n 'current_deal_stages' => $this->transformStages(team: $team, stagesIds: $report->getCurrentDealStages()),\n 'recipients' => $this->transformRecipients($report->getRecipients()),\n 'created_by' => $this->transformCreator($report->getCreator()),\n 'additional_prompt_input' => $report->getAdditionalPromptInput(),\n 'custom_name' => $report->getCustomName(),\n 'created_at' => $report->getCreatedAt()->format('Y-m-d H:i:s'),\n 'updated_at' => $report->getUpdatedAt()->format('Y-m-d H:i:s'),\n 'deleted_at' => $report->getDeletedAt()?->format('Y-m-d H:i:s'),\n ];\n }\n\n private function transformAskJiminnyFields(AutomatedReport $report): array\n {\n $team = $report->getTeam();\n $creatorId = $report->getAttribute('created_by');\n $explicitUserIds = array_values(array_filter(\n $report->getRecipients()['users'] ?? [],\n static fn ($id) => $id !== $creatorId\n ));\n\n return [\n 'report_name' => $report->getCustomName(),\n 'enabled' => $report->getStatus(),\n 'share_teams' => $this->transformGroups(team: $team, groupsIds: $report->getGroups()),\n 'share_users' => $this->transformRecipients(['users' => $explicitUserIds]),\n 'saved_search' => $this->transformSafeSearch($report->getSavedSearch()),\n 'ask_jiminny_prompt' => $this->transformAskJiminnyPrompt($report->getAskAnythingPrompt()),\n 'expires_on' => $report->getExpiresAt()?->format('Y-m-d'),\n ];\n }\n\n private function transformOrganization(?Team $team): array\n {\n return [\n 'id' => $team?->getUuid(),\n 'name' => $team?->getName(),\n ];\n }\n\n private function transformReportType(string $type): array\n {\n foreach (self::ALL_TYPES as $typeItem) {\n if ($typeItem['id'] === $type) {\n return $typeItem;\n }\n }\n\n return [];\n }\n\n private function transformCallType(array $types): array\n {\n $result = [];\n $callTypes = [self::CALL_TYPE_CONFERENCE, self::CALL_TYPE_DIALER];\n\n foreach ($types as $type) {\n foreach ($callTypes as $callTypeItem) {\n if ($callTypeItem['id'] === $type) {\n $result[] = $callTypeItem;\n\n break;\n }\n }\n }\n\n return $result;\n }\n\n private function transformMediaTypes(AutomatedReport $report): array\n {\n $values = [];\n\n foreach ($report->getMediaTypes() as $mediaType) {\n if (! in_array($mediaType, self::MEDIA_TYPES, true)) {\n continue;\n }\n\n $values[] = match ($mediaType) {\n self::MEDIA_TYPE_PDF => self::MEDIA_TYPE_OBJECT_PDF,\n self::MEDIA_TYPE_PODCAST => self::MEDIA_TYPE_OBJECT_PODCAST,\n };\n }\n\n return $values;\n }\n\n private function transformFrequency(string $frequency): array\n {\n foreach (self::ALL_FREQUENCIES as $frequencyItem) {\n if ($frequencyItem['id'] === $frequency) {\n return $frequencyItem;\n }\n }\n\n return [];\n }\n\n public function transformDurationToMinutes(?int $duration): ?int\n {\n if (! $duration) {\n return null;\n }\n\n return (int) ($duration / 60);\n }\n\n private function transformGroups(?Team $team, array $groupsIds): array\n {\n if (empty($groupsIds) || ! $team) {\n return [];\n }\n\n $data = [];\n foreach ($groupsIds as $groupId) {\n $group = $team->groups()->where('id', $groupId)->first();\n\n if ($group) {\n $data[] = [\n 'id' => $group->getUuid(),\n 'name' => $group->getName(),\n 'photoUrl' => $group->getPhotoUrl(),\n ];\n }\n }\n\n return $data;\n }\n\n private function transformStages(?Team $team, array $stagesIds): array\n {\n if (empty($stagesIds) || ! $team) {\n return [];\n }\n\n $data = [];\n foreach ($stagesIds as $stageId) {\n $stage = $team->stages()->where('id', $stageId)->first();\n\n if ($stage) {\n $data[] = [\n 'id' => $stage->getUuid(),\n 'name' => $stage->getName(),\n ];\n }\n }\n\n return $data;\n }\n\n private function transformRecipients(array $recipients): array\n {\n $users = [];\n foreach ($recipients['users'] ?? [] as $userId) {\n $users[] = $this->transformUser($userId);\n }\n\n return $users;\n }\n\n private function transformCreator(?User $user): ?array\n {\n if ($user === null) {\n return null;\n }\n\n return $this->transformUser($user->getId());\n }\n\n private function transformAskJiminnyPrompt(?AskAnythingPrompt $prompt): ?array\n {\n if ($prompt === null) {\n return null;\n }\n\n return [\n 'id' => $prompt->getUuid(),\n 'name' => $prompt->getTitle(),\n ];\n }\n\n private function transformSafeSearch(?Search $search): ?array\n {\n if ($search === null) {\n return null;\n }\n\n return [\n 'id' => $search->getUuid(),\n 'name' => $search->getName(),\n ];\n }\n\n private function transformUser(int $userId): array\n {\n /* @var ?User $user */\n $user = $this->userRepository->find($userId);\n\n return [\n 'id' => $user?->getUuid(),\n 'name' => $user?->getName(),\n 'email' => $user?->getEmailAddress(),\n 'photoUrl' => $user?->getPhotoUrl(),\n ];\n }\n\n public function create(array $data): array\n {\n $validatedData = $this->validateAndTransformData($data);\n $validatedData['created_by'] = auth()->id();\n\n $automatedReport = $this->automatedReportsRepository->create($validatedData);\n\n $this->generateOneOffReport($automatedReport);\n\n return $this->transformReportFullView($automatedReport);\n }\n\n public function update(string $uuid, array $data): array\n {\n $validatedData = $this->validateAndTransformData($data);\n $report = $this->automatedReportsRepository->findByUuid($uuid);\n\n if (! $report) {\n throw new InvalidArgumentException('Report not found');\n }\n\n $oldCustomName = $report->getCustomName();\n\n $automatedReport = $this->automatedReportsRepository->update($report, $validatedData);\n\n if ($oldCustomName !== $automatedReport->getCustomName()) {\n $this->updateResultNames($automatedReport);\n }\n\n $this->generateOneOffReport($automatedReport);\n\n return $this->transformReportFullView($automatedReport);\n }\n\n /**\n * Create an Ask Jiminny report.\n */\n public function createAskJiminnyReport(array $data, User $creator): array\n {\n $validatedData = $this->validateAskJiminnyReportData($data, $creator);\n $validatedData['created_by'] = $creator->getId();\n\n $automatedReport = $this->automatedReportsRepository->create($validatedData);\n\n return $this->transformReportFullView($automatedReport);\n }\n\n /**\n * Update an Ask Jiminny report.\n */\n public function updateAskJiminnyReport(AutomatedReport $report, array $data, User $user): array\n {\n if (! $report->isAskJiminnyReport()) {\n throw new InvalidArgumentException('Report is not an Ask Jiminny report');\n }\n\n $validatedData = $this->validateAskJiminnyReportData($data, $user);\n\n $oldCustomName = $report->getCustomName();\n\n $automatedReport = $this->automatedReportsRepository->update($report, $validatedData);\n\n if ($oldCustomName !== $automatedReport->getCustomName()) {\n $this->updateResultNames($automatedReport);\n }\n\n return $this->transformReportFullView($automatedReport);\n }\n\n public function updateAskJiminnyReportStatus(AutomatedReport $report, bool $status): array\n {\n $this->automatedReportsRepository->update($report, ['status' => $status]);\n\n return $this->transformReportFullView($report->fresh());\n }\n\n /**\n * Validate and transform data for Ask Jiminny reports.\n */\n private function validateAskJiminnyReportData(array $data, User $user): array\n {\n // Validate name\n $name = trim($data['report_name'] ?? '');\n if (empty($name)) {\n throw new InvalidArgumentException('Report name is required');\n }\n if (mb_strlen($name) > 50) {\n throw new InvalidArgumentException('Report name must be 50 characters or less');\n }\n\n // Validate frequency (only daily, weekly, monthly for Ask Jiminny)\n $frequency = $data['frequency'] ?? null;\n $askJiminnyFrequencies = [self::FREQUENCY_DAILY, self::FREQUENCY_WEEKLY, self::FREQUENCY_MONTHLY];\n if (! in_array($frequency, $askJiminnyFrequencies, true)) {\n throw new InvalidArgumentException('Frequency must be daily, weekly, or monthly');\n }\n\n // Validate expiration date\n $expiresAt = $data['expires_on'] ?? null;\n if (empty($expiresAt)) {\n throw new InvalidArgumentException('Expiration date is required');\n }\n\n try {\n $expiresAtDate = Carbon::parse($expiresAt);\n } catch (InvalidFormatException $e) {\n throw new InvalidArgumentException('Expiration date format is invalid');\n }\n $maxExpiration = Carbon::now()->addYear()->endOfDay();\n if ($expiresAtDate->gt($maxExpiration)) {\n throw new InvalidArgumentException('Expiration date cannot be more than 1 year from now');\n }\n if ($expiresAtDate->isPast()) {\n throw new InvalidArgumentException('Expiration date cannot be in the past');\n }\n\n // Validate saved search\n $activitySearchId = $data['saved_search'] ?? null;\n if (empty($activitySearchId)) {\n throw new InvalidArgumentException('Saved search is required');\n }\n $savedSearch = $this->activitySearchRepository->findByUuidAndUser($activitySearchId, $user);\n if (! $savedSearch) {\n throw new InvalidArgumentException('Saved search not found or does not belong to you');\n }\n\n // Validate saved prompt\n $askAnythingPromptId = $data['ask_jiminny_prompt'] ?? null;\n if (empty($askAnythingPromptId)) {\n throw new InvalidArgumentException('Ask Jiminny prompt is required');\n }\n $prompt = $this->askAnythingRepository->getPromptByUuid($askAnythingPromptId);\n if (! $prompt) {\n throw new InvalidArgumentException('Ask Jiminny prompt not found');\n }\n\n // Validate status\n $status = $data['enabled'] ?? false;\n\n $recipientUserIds = [$user->getId()];\n\n if (! empty($data['share_users'])) {\n $sharedUserIds = $this->validateAndGetUserIdsByTeam(\n $user->team,\n (array) $data['share_users']\n );\n $recipientUserIds = array_merge($recipientUserIds, $sharedUserIds);\n }\n\n $sharedGroupIds = [];\n if (! empty($data['share_teams'])) {\n $sharedGroupIds = $this->validateAndGetGroupIds($user->team, (array) $data['share_teams']);\n }\n\n $recipientUserIds = array_values(array_unique($recipientUserIds));\n\n return [\n 'team_id' => $user->getTeamId(),\n 'type' => self::TYPE_ASK_JIMINNY,\n 'status' => (bool) $status,\n 'frequency' => $frequency,\n 'custom_name' => $name,\n 'activity_search_id' => $savedSearch->getId(),\n 'ask_anything_prompt_id' => $prompt->getId(),\n 'expires_at' => $expiresAtDate->toDateString(),\n 'media_types' => [self::MEDIA_TYPE_PDF],\n 'call_types' => [],\n 'recipients' => ['users' => $recipientUserIds],\n 'groups' => $sharedGroupIds,\n ];\n }\n\n public static function getAskJiminnyFrequencies(): array\n {\n return array_map(static function ($frequency) {\n return $frequency['id'];\n }, self::ASK_JIMINNY_FREQUENCIES);\n }\n\n public function getAskJiminnyReportFilters(User $user): array\n {\n $savedSearches = $this->activitySearchRepository->findByUserOrderedByName($user)\n ->map(fn (Search $search) => [\n 'id' => $search->getUuid(),\n 'name' => $search->getName(),\n ])\n ->values()->all();\n\n $prompts = collect(\n $this->askAnythingPromptService->get($user, AskAnythingPromptTarget::on_demand)\n )->map(fn (AskAnythingPromptDto $prompt) => [\n 'id' => $prompt->id,\n 'name' => $prompt->title,\n ])->values()->all();\n\n return [\n [\n 'id' => 'prompt',\n 'label' => 'Prompt',\n 'options' => $prompts,\n ],\n [\n 'id' => 'saved_search',\n 'label' => 'Saved Search',\n 'options' => $savedSearches,\n ],\n ];\n }\n\n public function getAskJiminnyReportFormData(User $user, ?AutomatedReport $report = null): array\n {\n $team = $user->getTeam();\n $userTimezone = $user->getTimezone();\n\n $savedSearches = $this->activitySearchRepository->findByUserOrderedByName($user)\n ->map(fn (Search $search) => [\n 'id' => $search->getUuid(),\n 'name' => $search->getName(),\n ])\n ->values()->all();\n\n $prompts = collect(\n $this->askAnythingPromptService->get($user, AskAnythingPromptTarget::on_demand)\n )->map(fn (AskAnythingPromptDto $prompt) => [\n 'id' => $prompt->id,\n 'name' => $prompt->title,\n ])->values()->all();\n\n $teamGroups = $this->groupRepository->getAllByTeam($team)->map(fn ($group) => [\n 'id' => $group->getUuid(),\n 'name' => $group->getName(),\n ])->values()->all();\n\n $shareUsers = $this->recipientsService->getRecipientsFieldData(team: $team)['options'] ?? [];\n\n $sharedTeamsValue = [];\n $sharedUsersValue = [];\n if ($report) {\n $sharedTeamsValue = $this->transformGroups($team, $report->getGroups());\n\n $recipientUserIds = $report->getRecipients()['users'] ?? [];\n $creatorId = $report->getAttribute('created_by');\n $sharedUserIds = array_values(array_filter(\n $recipientUserIds,\n static fn ($id) => $id !== $creatorId\n ));\n $sharedUsersValue = collect($sharedUserIds)\n ->map(fn ($id) => $this->userRepository->find((int) $id))\n ->filter()\n ->map(fn (User $u) => [\n 'id' => $u->getUuid(),\n 'name' => $u->getName(),\n ])\n ->values()\n ->all();\n }\n\n return [\n 'fields' => [\n [\n 'id' => 'enabled',\n 'inputType' => InputTypeEnum::TOGGLE,\n 'label' => '',\n 'value' => $report?->getStatus() ?? false,\n ],\n [\n 'id' => 'report_name',\n 'inputType' => InputTypeEnum::TEXT,\n 'label' => 'Name',\n 'placeholder' => 'Enter name',\n 'required' => true,\n 'validation' => ['maxLength' => 50],\n 'value' => $report?->getCustomName() ?? '',\n ],\n [\n 'id' => 'frequency',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'label' => 'Frequency',\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => self::ASK_JIMINNY_FREQUENCIES,\n 'value' => $report ? $this->transformFrequency($report->getFrequency()) : null,\n ],\n [\n 'id' => 'expires_on',\n 'inputType' => InputTypeEnum::DATE,\n 'label' => 'Expires on',\n 'required' => true,\n 'placeholder' => 'Select',\n 'validation' => [\n 'minDate' => now($userTimezone)->toDateString(),\n 'maxDate' => now($userTimezone)->addYear()->toDateString(),\n ],\n 'value' => $report?->getExpiresAt()?->toDateString(),\n ],\n [\n 'id' => 'share_teams',\n 'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,\n 'label' => 'Team',\n 'required' => false,\n 'placeholder' => 'Select',\n 'options' => $teamGroups,\n 'value' => $sharedTeamsValue,\n ],\n [\n 'id' => 'share_users',\n 'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,\n 'label' => 'Team member',\n 'required' => false,\n 'placeholder' => 'Select',\n 'groupLabelKey' => 'label',\n 'groupValuesKey' => 'users',\n 'optionLabelKey' => 'name',\n 'optionValueKey' => 'id',\n 'options' => $shareUsers,\n 'value' => $sharedUsersValue,\n ],\n [\n 'id' => 'saved_search',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'label' => 'Saved search',\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => $savedSearches,\n 'value' => $report && $report->getSavedSearch() ? [\n 'id' => $report->getSavedSearch()->getUuid(),\n 'name' => $report->getSavedSearch()->getName(),\n ] : null,\n ],\n [\n 'id' => 'ask_jiminny_prompt',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'label' => 'Ask Jiminny prompt',\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => $prompts,\n 'value' => $report && $report->getAskAnythingPrompt() ? [\n 'id' => $report->getAskAnythingPrompt()->getUuid(),\n 'name' => $report->getAskAnythingPrompt()->getTitle(),\n ] : null,\n ],\n ],\n ];\n }\n\n private function updateResultNames(AutomatedReport $automatedReport): void\n {\n $results = $this->automatedReportsRepository->getResultsByReport($automatedReport);\n\n foreach ($results as $result) {\n $result->update(['name' => $this->getReportFileName($result)]);\n }\n }\n\n public function updateStatus(string $uuid, array $data): array\n {\n $automatedReport = $this->automatedReportsRepository->findByUuid($uuid);\n\n if (! $automatedReport) {\n throw new ModelNotFoundException('Report not found');\n }\n\n $status = $this->validateReportStatus($data['report_enabled'] ?? null);\n $automatedReport->update([\n 'status' => $status,\n ]);\n\n $this->generateOneOffReport($automatedReport);\n\n return $this->transformReportFullView($automatedReport);\n }\n\n private function generateOneOffReport(AutomatedReport $automatedReport): void\n {\n // the scheduler handles all the other frequency types\n if ($automatedReport->getStatus() === false || $automatedReport->getFrequency() !== self::FREQUENCY_ONE_OFF) {\n return;\n }\n\n $this->dispatcher->dispatch(new RequestGenerateReportJob($automatedReport->getUuid()));\n }\n\n public function getReport(string $uuid): AutomatedReport\n {\n $automatedReport = $this->automatedReportsRepository->findByUuid($uuid);\n\n if (! $automatedReport) {\n throw new ModelNotFoundException('Report not found');\n }\n\n return $automatedReport;\n }\n\n public function get(string $uuid): array\n {\n $automatedReport = $this->getReport($uuid);\n\n return $this->transformReportFullView($automatedReport);\n }\n\n public function list(string $sortColumn = 'created_at', string $sortDirection = 'desc'): array\n {\n $results = [];\n $collection = $this->automatedReportsRepository->getAllStandardReports($sortColumn, $sortDirection);\n\n /** @var AutomatedReport $report */\n foreach ($collection as $report) {\n $results[] = $this->transformReportFullView($report);\n }\n\n return ['data' => $results];\n }\n\n public function listAskJiminnyReports(\n User $user,\n string $sortColumn = 'created_at',\n string $sortDirection = 'desc'\n ): array {\n $results = [];\n $collection = $this->automatedReportsRepository->getAskJiminnyReportsByUser($user, $sortColumn, $sortDirection);\n\n /** @var AutomatedReport $report */\n foreach ($collection as $report) {\n $results[] = $this->transformReportFullView($report);\n }\n\n return ['data' => $results];\n }\n\n public function delete(string $uuid): void\n {\n $automatedReport = $this->automatedReportsRepository->findByUuid($uuid);\n\n if (! $automatedReport) {\n throw new ModelNotFoundException('Report not found');\n }\n\n $automatedReport->delete();\n }\n\n public function createReportResult(AutomatedReport $automatedReport, array $data = []): AutomatedReportResult\n {\n return $this->automatedReportsRepository->createResult(\n array_merge(\n [\n 'report_id' => $automatedReport->getId(),\n 'status' => AutomatedReportResult::STATUS_DEFAULT,\n ],\n $data\n )\n );\n }\n\n public function getOrCreateReportResult(AutomatedReport $automatedReport, array $data = []): AutomatedReportResult\n {\n $existing = $this->automatedReportsRepository->findLatestDefaultOrFailedResult($automatedReport);\n\n if ($existing !== null) {\n $existing->update(['status' => AutomatedReportResult::STATUS_DEFAULT]);\n\n return $existing;\n }\n\n return $this->createReportResult($automatedReport, $data);\n }\n\n public function getReportResult(string $resultUuid): AutomatedReportResult\n {\n $report = $this->automatedReportsRepository->findResultByUuid($resultUuid);\n\n if (! $report) {\n throw new ModelNotFoundException('Report Result not found');\n }\n\n return $report;\n }\n\n public function findChildResult(AutomatedReportResult $result, string $type): ?AutomatedReportResult\n {\n return $this->automatedReportsRepository->findChildResult($result, $type);\n }\n\n // prophet API calls\n /**\n * @throws ApplicationException\n */\n public function getGenerateReportPayload(AutomatedReport $automatedReport, string $reportResultUuid): array\n {\n $period = $this->calculateFromAndToDate($automatedReport);\n $fromDate = $period['fromDate'];\n $toDate = $period['toDate'];\n\n return [\n 'team_id' => $automatedReport->getTeamId(),\n 'request_id' => $reportResultUuid,\n 'report_type' => $automatedReport->getType(),\n 'media_types' => $automatedReport->getMediaTypes(),\n 'from_date' => $fromDate->startOfDay()->format(DateTimeInterface::RFC3339),\n 'to_date' => $toDate->endOfDay()->format(DateTimeInterface::RFC3339),\n 'group_ids' => $automatedReport->getGroups(),\n 'call_deal_stage' => $automatedReport->getDealAtCallStages(),\n 'current_deal_stage' => $automatedReport->getCurrentDealStages(),\n 'deal_min_value' => $automatedReport->getDealValueMin(),\n 'deal_max_value' => $automatedReport->getDealValueMax(),\n 'call_types' => $automatedReport->getCallTypes(),\n 'call_duration_min_seconds' => $automatedReport->getCallDurationMin(),\n 'call_duration_max_seconds' => $automatedReport->getCallDurationMax(),\n 'special_requirements' => $automatedReport->getAdditionalPromptInput(),\n 'callback_url' => $this->getCallbackUrl(),\n 'report_period' => $this->formatReportPeriodName(\n $automatedReport->getFrequency(),\n $fromDate,\n $toDate,\n ),\n 'playbook_categories' => $automatedReport->getPlaybookCategories(),\n 'custom_name' => $automatedReport->getCustomName(),\n ];\n }\n\n // $inputPayload - FE payload structure\n public function getActivitiesCountPayload(array $inputPayload): array\n {\n // Use validateAndTransformData to validate and normalize input\n $validatedData = $this->validateAndTransformData($inputPayload);\n $period = $this->calculateFromAndToDatePeriod(\n $validatedData['frequency'],\n Carbon::parse($validatedData['from']),\n Carbon::parse($validatedData['to']),\n );\n $fromDate = $period['fromDate'];\n $toDate = $period['toDate'];\n\n // Create payload similar to getGenerateReportPayload\n return [\n 'team_id' => $validatedData['team_id'],\n 'group_ids' => $validatedData['groups'] ?? [],\n 'report_type' => $validatedData['type'],\n 'from_date' => $fromDate->format(DateTimeInterface::RFC3339),\n 'to_date' => $toDate->format(DateTimeInterface::RFC3339),\n 'call_deal_stage' => $validatedData['deal_at_call_stages'] ?? [],\n 'current_deal_stage' => $validatedData['current_deal_stages'] ?? [],\n 'deal_min_value' => $validatedData['deal_value_min'] ?? null,\n 'deal_max_value' => $validatedData['deal_value_max'] ?? null,\n 'call_types' => $validatedData['call_types'],\n 'call_duration_min_seconds' => $validatedData['call_duration_min'] ?? null,\n 'call_duration_max_seconds' => $validatedData['call_duration_max'] ?? null,\n 'special_requirements' => $validatedData['additional_prompt_input'] ?? null,\n 'playbook_categories' => $validatedData['playbook_categories'] ?? [],\n 'request_id' => null,\n 'callback_url' => null,\n ];\n }\n\n public function shouldSendReport(array $users, ?CarbonInterface $generatedAt = null): bool\n {\n if (empty($users)) {\n return false;\n }\n\n $earliestTz = collect($users)\n ->mapWithKeys(function (array $user) {\n $tz = new DateTimeZone($user['timezone']);\n $nowUtc = new DateTime('now', new DateTimeZone('UTC'));\n $offset = $tz->getOffset($nowUtc);\n\n return [$user['timezone'] => $offset];\n })\n ->sortDesc()\n ->keys()\n ->first();\n\n $now = Carbon::now($earliestTz);\n $isScheduledTime = (int) $now->format('H') === self::SENT_REPORT_AT_HOURS;\n\n if ($isScheduledTime) {\n return true;\n }\n\n return $this->hasPassedScheduledTime($generatedAt, $earliestTz);\n }\n\n public function hasPassedScheduledTime(?CarbonInterface $generatedAt, string $timezone): bool\n {\n if ($generatedAt === null) {\n return false;\n }\n\n $now = Carbon::now($timezone);\n $scheduledTime = $now->copy()->setTime(self::SENT_REPORT_AT_HOURS, 0, 0);\n\n if ($now->hour < self::SENT_REPORT_AT_HOURS) {\n $scheduledTime = $scheduledTime->subDay();\n }\n\n $scheduledTimeUtc = $scheduledTime->copy()->utc();\n $generatedAtUtc = $generatedAt->copy()->utc();\n $nowUtc = $now->copy()->utc();\n\n return $generatedAtUtc->lt($scheduledTimeUtc) && $nowUtc->gt($scheduledTimeUtc);\n }\n\n public function calculateFromAndToDatePeriod(\n string $frequency,\n ?Carbon $fromDate = null,\n ?Carbon $toDate = null\n ): array {\n if ($frequency === self::FREQUENCY_ONE_OFF) {\n return [\n 'fromDate' => $fromDate,\n 'toDate' => $toDate,\n ];\n }\n\n $now = Carbon::now();\n\n return match ($frequency) {\n self::FREQUENCY_DAILY => [\n 'fromDate' => $now->copy()->subDay()->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_WEEKLY => [\n 'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_MONTHLY => [\n 'fromDate' => $now->copy()->subMonths(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_QUARTERLY => [\n 'fromDate' => $now->copy()->subMonths(3)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n default => throw new InvalidArgumentException(\"Unsupported frequency: {$frequency}\"),\n };\n }\n\n private function calculateFromAndToDate(AutomatedReport $automatedReport): array\n {\n return $this->calculateFromAndToDatePeriod(\n $automatedReport->getFrequency(),\n $automatedReport->getFrom(),\n $automatedReport->getTo()\n );\n }\n\n public function getAskJiminnyGenerateReportPayload(\n AutomatedReport $automatedReport,\n AutomatedReportResult $reportResult,\n array $activityIds,\n ): array {\n return [\n 'user_question' => $automatedReport->getAskAnythingPrompt()?->getContent(),\n 'call_ids' => array_map('strval', $activityIds),\n 'team_id' => $automatedReport->getTeamId(),\n 'request_id' => $reportResult->getUuid(),\n 'callback_url' => $this->getCallbackUrl(),\n 'report_period' => $this->getReportPeriodName($reportResult),\n 'report_name' => $automatedReport->getCustomName(),\n ];\n }\n\n private function getCallbackUrl(): string\n {\n return $this->webhookService->route('jiminny.webhook.reports.ready');\n }\n\n /**\n * Validate and transform payload data for automated reports\n *\n * @param array $data\n *\n * @throws InvalidArgumentException\n *\n * @return array\n */\n private function validateAndTransformData(array $data): array\n {\n // Validate organization (team) and check feature\n $team = $this->validateOrganization($data['organization'] ?? null);\n\n $status = $this->validateReportStatus($data['report_enabled'] ?? null);\n $type = $this->validateReportType($data['report_type'] ?? null);\n $frequency = $this->validateFrequency($data['frequency'] ?? null);\n $additionalPromptInput = $this->validateAdditionalPromptInput(\n $data['additional_prompt_input'] ?? null\n );\n $customReportName = $this->validateCustomReportName($data['custom_name'] ?? null);\n\n // Prepare data for the database\n $reportData = [\n 'team_id' => $team->getId(),\n 'type' => $type,\n 'status' => $status,\n 'frequency' => $frequency,\n 'additional_prompt_input' => $additionalPromptInput,\n 'custom_name' => $customReportName,\n ];\n\n // Validate deal values\n $reportData = $this->validateDealValues($data, $reportData);\n\n // Validate date range\n $reportData = $this->validateDateRange($data, $reportData, $frequency);\n\n // Validate call durations\n $reportData = $this->validateCallDurations($data, $reportData);\n\n // Validate call types\n $reportData = $this->validateCallTypes($data, $reportData);\n\n // Validate media types\n $reportData = $this->validateMediaTypes($data, $reportData);\n\n // Validate groups\n if (isset($data['teams'])) {\n $reportData['groups'] = $this->validateAndGetGroupIds($team, $data['teams']);\n }\n\n // Validate deal stages\n $reportData = $this->validateDealStages($data, $reportData, $team, $type);\n\n // Validate playbook categories\n $reportData = $this->validatePlaybookCategories($data, $reportData, $team);\n\n // Validate recipients\n $reportData['recipients'] = [\n 'users' => $this->validateAndGetUserIdsByTeam($team, $data['recipients'] ?? []),\n ];\n\n if (isset($data['jiminny_recipients'])) {\n // Validate Jiminny recipients\n $reportData['jiminny_recipients'] = [\n 'users' => $this->validateAndGetJiminnyUserIds((array) $data['jiminny_recipients']),\n ];\n }\n\n return $reportData;\n }\n\n private function validateDealValues(array $data, array $reportData): array\n {\n if (isset($data['min_deal_value'])) {\n $reportData['deal_value_min'] = (int) $data['min_deal_value'];\n\n if ($reportData['deal_value_min'] > 4294967295 || $reportData['deal_value_min'] < 0) {\n throw new InvalidArgumentException('Min deal value should be between 0 and 4294967295');\n }\n }\n\n if (isset($data['max_deal_value'])) {\n $reportData['deal_value_max'] = (int) $data['max_deal_value'];\n\n if ($reportData['deal_value_max'] > 4294967295 || $reportData['deal_value_max'] < 0) {\n throw new InvalidArgumentException('Max deal value should be between 0 and 4294967295');\n }\n }\n\n if (isset($data['min_deal_value'], $data['max_deal_value'])\n && $data['min_deal_value'] > $data['max_deal_value']\n ) {\n throw new InvalidArgumentException('Min deal value cannot be greater than max deal value');\n }\n\n return $reportData;\n }\n\n private function validateDateRange(array $data, array $reportData, string $frequency): array\n {\n // Set date range only for one_off frequency\n if ($frequency === 'one_off') {\n if (isset($data['start_date_period'])) {\n $reportData['from'] = $this->parseDate($data['start_date_period']);\n }\n\n if (isset($data['end_date_period'])) {\n $reportData['to'] = $this->parseDate($data['end_date_period']);\n }\n\n if (empty($reportData['from']) || empty($reportData['to'])) {\n throw new InvalidArgumentException(\n 'Start date and end date are required for one_off frequency'\n );\n }\n } else {\n $reportData['from'] = null;\n $reportData['to'] = null;\n }\n\n return $reportData;\n }\n\n private function validateCallDurations(array $data, array $reportData): array\n {\n // Convert call durations from minutes to seconds\n if (isset($data['min_call_duration'])) {\n $reportData['call_duration_min'] = (int) $data['min_call_duration'] * 60;\n\n if ($reportData['call_duration_min'] > 4294967295 || $reportData['call_duration_min'] < 0) {\n throw new InvalidArgumentException('Min call duration should be between 0 and 4294967295');\n }\n }\n\n if (isset($data['max_call_duration'])) {\n $reportData['call_duration_max'] = (int) $data['max_call_duration'] * 60;\n\n if ($reportData['call_duration_max'] > 4294967295 || $reportData['call_duration_max'] < 0) {\n throw new InvalidArgumentException('Max call duration should be between 0 and 4294967295');\n }\n }\n\n return $reportData;\n }\n\n private function validateCallTypes(array $data, array $reportData): array\n {\n // Set call types\n $reportData['call_types'] = $data['call_type'] ?? [];\n if (empty($reportData['call_types'])) {\n $reportData['call_types'] = self::getCallTypes();\n }\n\n foreach ($reportData['call_types'] as $callType) {\n if (! in_array($callType, self::getCallTypes(), true)) {\n throw new InvalidArgumentException(sprintf('Call type %s is invalid', $callType));\n }\n }\n\n return $reportData;\n }\n\n private function validateMediaTypes(array $data, array $reportData): array\n {\n // Set media types from input data\n $reportData['media_types'] = $data['media_types'] ?? [];\n\n if (empty($reportData['media_types'])) {\n throw new InvalidArgumentException('Media types are required');\n }\n\n foreach ($reportData['media_types'] as $mediaType) {\n if (! in_array($mediaType, self::MEDIA_TYPES, true)) {\n throw new InvalidArgumentException(sprintf('Media type %s is invalid', $mediaType));\n }\n }\n\n return $reportData;\n }\n\n private function validateDealStages(array $data, array $reportData, Team $team, string $reportType): array\n {\n // Validate and set deal stages\n if (isset($data['deal_stage_at_call'])) {\n $reportData['deal_at_call_stages'] =\n $this->validateAndGetDealStageIds($team, $data['deal_stage_at_call'], 'Deal stage at call');\n }\n\n if (isset($data['current_deal_stage'])) {\n $reportData['current_deal_stages'] =\n $this->validateAndGetDealStageIds($team, $data['current_deal_stage'], 'Current deal stage');\n }\n\n // Ensure current_deal_stage is not provided for loss_analysis report type\n if ($reportType === self::TYPE_LOSS_ANALYSIS && ! empty($data['current_deal_stage'])) {\n throw new InvalidArgumentException('Current deal stage is not applicable for Loss Analysis reports');\n }\n\n return $reportData;\n }\n\n // transform uuid to id\n private function validatePlaybookCategories(array $data, array $reportData, Team $team): array\n {\n $key = 'playbook_categories';\n\n if (isset($data[$key])) {\n $payloadIds = $data[$key];\n $ids = [];\n\n foreach ($payloadIds as $uuid) {\n $uuid = (string) $uuid;\n\n try {\n $playbookCategory = $this->playbookCategoryRepository->findByUuid($uuid);\n } catch (Throwable $throwable) {\n Log::error(__METHOD__ . ' ' . $throwable->getMessage());\n\n throw new InvalidArgumentException(sprintf('Playbook category %s not found', $uuid));\n }\n\n if (! $playbookCategory) {\n throw new InvalidArgumentException(sprintf('Playbook category %s not found', $uuid));\n }\n\n if (! $playbookCategory->hasPlaybook()) {\n throw new InvalidArgumentException(sprintf('Playbook category %s has no playbook', $uuid));\n }\n\n if ($playbookCategory->getPlaybook()->getTeamId() !== $team->getId()) {\n throw new InvalidArgumentException(\n sprintf('Playbook category %s not found for team %s', $uuid, $team->getUuid())\n );\n }\n\n $ids[] = $playbookCategory->getId();\n }\n\n $reportData[$key] = $ids;\n }\n\n return $reportData;\n }\n\n private function validateReportStatus($status): bool\n {\n if (! in_array($status, [true, false], true)) {\n throw new InvalidArgumentException('Report status is invalid');\n }\n\n return $status;\n }\n\n private function validateReportType($type): string\n {\n if (! in_array($type, self::getTypes(), true)) {\n throw new InvalidArgumentException(sprintf('Report type is invalid: %s', $type));\n }\n\n return $type;\n }\n\n private function validateFrequency($frequency): string\n {\n if (! in_array($frequency, self::getFrequencies(), true)) {\n throw new InvalidArgumentException('Frequency is invalid');\n }\n\n return $frequency;\n }\n\n private function validateAdditionalPromptInput(?string $additionalPromptInput): ?string\n {\n if ($additionalPromptInput && strlen($additionalPromptInput) > 5000) {\n throw new InvalidArgumentException('Additional Prompt Input should be less than 5000 characters');\n }\n\n return $additionalPromptInput;\n }\n\n private function validateCustomReportName(?string $customReportName): ?string\n {\n if ($customReportName === null || $customReportName === '') {\n return null;\n }\n\n if (strlen($customReportName) > 70) {\n throw new InvalidArgumentException('Custom report name should be less than 70 characters');\n }\n\n return $customReportName;\n }\n\n private function validateOrganization(?string $organizationUuid): Team\n {\n if (! $organizationUuid) {\n throw new InvalidArgumentException('Organization is required');\n }\n\n $team = $this->teamRepository->idOrUuid($organizationUuid);\n\n if (! $team) {\n throw new InvalidArgumentException('Organization not found');\n }\n\n if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {\n throw new InvalidArgumentException('Organization does not have the Automated Reports feature');\n }\n\n return $team;\n }\n\n private function validateAndGetGroupIds(Team $team, array $teamUuids): array\n {\n $groupIds = [];\n\n foreach ($teamUuids as $uuid) {\n $group = $this->groupRepository->findByUuid($uuid);\n\n if ($group === null || $group->getTeamId() !== $team->getId()) {\n throw new InvalidArgumentException(\n sprintf('Group %s not found for team %s', $uuid, $team->getUuid())\n );\n }\n\n $groupIds[] = $group->getId();\n\n }\n\n return $groupIds;\n }\n\n private function validateAndGetDealStageIds(Team $team, array $stageUuids, string $propertyLabel): array\n {\n $stageIds = [];\n\n foreach ($stageUuids as $uuid) {\n $stage = $this->stageRepository->findByUuid($uuid);\n\n if ($stage === null || $stage->getTeamId() !== $team->getId()) {\n throw new InvalidArgumentException(\n sprintf('Stage %s not found for team %s for %s', $uuid, $team->getUuid(), $propertyLabel)\n );\n }\n\n $stageIds[] = $stage->getId();\n }\n\n return $stageIds;\n }\n\n private function validateAndGetUserIds(array $userUuids, callable $teamCheck): array\n {\n if (empty($userUuids)) {\n return [];\n }\n\n $userIds = [];\n\n foreach ($userUuids as $uuid) {\n $user = $this->userRepository->findByUuid($uuid);\n\n if (! $user || ! $user->isStatusActive()) {\n throw new InvalidArgumentException(\n sprintf('User %s not found or is not active', $uuid)\n );\n }\n\n if (! $teamCheck($user)) {\n throw new InvalidArgumentException(\n sprintf('User %s does not belong to the allowed team(s)', $uuid)\n );\n }\n\n $userIds[] = $user->getId();\n }\n\n return $userIds;\n }\n\n private function validateAndGetUserIdsByTeam(Team $team, array $userUuids): array\n {\n return $this->validateAndGetUserIds($userUuids, fn ($user) => $user->getTeamId() === $team->getId());\n }\n\n private function validateAndGetJiminnyUserIds(array $userUuids): array\n {\n $allowedTeamIds = config('kiosk.teamIds', []);\n\n return $this->validateAndGetUserIds($userUuids, fn ($user) => in_array($user->getTeamId(), $allowedTeamIds, true));\n }\n\n private function parseDate(string $dateString): string\n {\n return date('Y-m-d H:i:s', strtotime($dateString));\n }\n\n private function generateReportResultViewUrl(AutomatedReportResult $result): string\n {\n $mediaResource = $this->getReportMediaRouteResource($result);\n\n return route('ai-reports.' . $mediaResource . '.view', ['uuid' => $result->getUuid()]);\n }\n\n private function generateReportResultDownloadUrl(AutomatedReportResult $result): string\n {\n $mediaResource = $this->getReportMediaRouteResource($result);\n\n return route('ai-reports.' . $mediaResource . '.download', ['uuid' => $result->getUuid()]);\n }\n\n private function getReportMediaRouteResource(AutomatedReportResult $result): string\n {\n if ($result->getMediaType() === self::MEDIA_TYPE_PDF) {\n return self::PDF_KEY;\n } elseif ($result->getMediaType() === self::MEDIA_TYPE_PODCAST) {\n return self::AUDIO_KEY;\n }\n\n throw new \\InvalidArgumentException('Unknown media type.');\n }\n\n public function getMediaPath(AutomatedReportResult $result): ?string\n {\n $url = match ($result->getMediaType()) {\n self::MEDIA_TYPE_PDF => $result->getPdfUrl(),\n self::MEDIA_TYPE_PODCAST => $result->getPodcastAudioUrl(),\n default => null,\n };\n\n if ($url === null) {\n return null;\n }\n\n $path = parse_url(trim($url, '\"\\''), PHP_URL_PATH);\n\n return $path ?: null;\n }\n\n public function getFilenameSuffix(AutomatedReportResult $result): ?string\n {\n return match ($result->getMediaType()) {\n self::MEDIA_TYPE_PODCAST => 'Podcast',\n default => null,\n };\n }\n\n public function getMailSubjectSuffix(AutomatedReportResult $result): string\n {\n return match ($result->getMediaType()) {\n self::MEDIA_TYPE_PDF => 'report',\n self::MEDIA_TYPE_PODCAST => 'podcast',\n default => '',\n };\n }\n\n public function getMediaTypeMetadata(AutomatedReportResult $result): array\n {\n return match ($result->getMediaType()) {\n self::MEDIA_TYPE_PODCAST => ['extension' => 'mp3', 'mime' => 'audio/mpeg'],\n self::MEDIA_TYPE_PDF => ['extension' => 'pdf', 'mime' => 'application/pdf'],\n default => ['extension' => null, 'mime' => null],\n };\n }\n\n public function deleteS3Files(AutomatedReportResult $result): void\n {\n $teamUuid = $result->getReport()->getTeam()->getUuid();\n $reportUuid = $result->getUuid();\n\n // delete all files for a report uuid no mather of pdf, podcast, or both\n // in case of both - the podcast files are linked to the pdf (parent) uuid\n // pdf and podcast date times should be close\n $path = sprintf('%s/%s/%s', $teamUuid, self::S3_DIR, $reportUuid);\n\n foreach (self::FILE_EXTENSIONS_VARIANTS as $extension) {\n $file = $path . '.' . $extension;\n\n if (Storage::exists($file)) {\n Storage::delete($file);\n Log::info('[Reports] Deleted S3 file', [\n 'path' => $file,\n ]);\n }\n }\n\n foreach (self::FILE_PODCAST_EXTENSIONS_VARIANTS as $extension) {\n $file = $path . '_podcast.' . $extension;\n\n if (Storage::exists($file)) {\n Storage::delete($file);\n Log::info('[Reports] Deleted Podcast S3 file', [\n 'path' => $file,\n ]);\n }\n }\n }\n\n /**\n *\n * @param int|null $teamId Optional team ID to filter results\n *\n * @return Collection<int, int> Collection of team IDs\n */\n public function getTeamIdsWithReportsResults(?int $teamId = null): Collection\n {\n return $this->automatedReportsRepository->getTeamIdsWithReportsResults($teamId);\n }\n\n /**\n * Core delete logic for report results using a query\n *\n * @param Builder $query\n * @param array $logContext\n *\n * @return int\n */\n private function deleteReportResultsByQuery(Builder $query, array $logContext = []): int\n {\n $deletedCount = 0;\n\n if ($query->exists()) {\n Log::info(\n 'Run delete report results',\n array_merge(\n $logContext,\n [\n 'service' => 'AutomatedReportsService',\n ]\n )\n );\n\n $query->chunkById(50, function ($results) use (&$deletedCount, $logContext) {\n foreach ($results as $result) {\n $this->deleteReportResult($result);\n $deletedCount++;\n\n Log::info(\n 'Deleted a report result',\n array_merge(\n $logContext,\n [\n 'result_id' => $result->getId(),\n 'report_id' => $result->getReportId(),\n ]\n )\n );\n }\n });\n }\n\n return $deletedCount;\n }\n\n /**\n * Delete report results for a team by retention period\n *\n * @param Team $team\n * @param CarbonImmutable $retentionDate\n *\n * @return int Number of deleted report results\n */\n public function deleteReportsResultsInRetentionPeriod(Team $team, CarbonImmutable $retentionDate): int\n {\n $reportIds = $this->automatedReportsRepository->getReportIdsByTeam($team);\n\n if ($reportIds->isEmpty()) {\n return 0;\n }\n\n $query = $this->automatedReportsRepository\n ->getReportResultsQueryForRetention($team, $retentionDate);\n\n return $this->deleteReportResultsByQuery($query, [\n 'team_id' => $team->getId(),\n 'retention_date' => $retentionDate->toDateTimeString(),\n ]);\n }\n\n /**\n * Delete ALL report results for a specific automated report\n *\n * @param string $uuid\n *\n * @return int\n */\n public function deleteReportResults(string $uuid): int\n {\n $report = $this->getReport($uuid);\n\n $query = $this->automatedReportsRepository->getResultsByReportQuery($report);\n\n return $this->deleteReportResultsByQuery($query, [\n 'report_uuid' => $uuid,\n 'report_id' => $report->getId(),\n ]);\n }\n\n public function deleteReportResult(AutomatedReportResult $result): void\n {\n $this->deleteS3Files($result);\n\n $result->delete();\n }\n\n /**\n * Get all reports for a specific team\n *\n * @param Team $team\n *\n * @return \\Illuminate\\Database\\Eloquent\\Collection\n */\n public function getTeamReports(Team $team): \\Illuminate\\Database\\Eloquent\\Collection\n {\n return $this->automatedReportsRepository->getReportsByTeam($team);\n }\n\n /**\n * Get all report results for a specific report\n *\n * @param AutomatedReport $report\n *\n * @return \\Illuminate\\Database\\Eloquent\\Collection\n */\n public function getReportResults(AutomatedReport $report): \\Illuminate\\Database\\Eloquent\\Collection\n {\n return $this->automatedReportsRepository->getResultsByReport($report);\n }\n\n public function deleteAllReportResults(AutomatedReport $report): void\n {\n $results = $this->getReportResults($report);\n\n /** @var AutomatedReportResult $result */\n foreach ($results as $result) {\n Log::info('Deleting result', [\n 'report' => $report->getId(),\n 'result' => $result->getId(),\n ]);\n\n $this->deleteReportResult($result);\n }\n }\n\n public function deleteAllData(Team $team): void\n {\n Log::info('Deleting automated report and results for team', [\n 'team' => $team->getId(),\n ]);\n\n $reports = $this->getTeamReports($team);\n\n /** @var AutomatedReport $report */\n foreach ($reports as $report) {\n Log::info('Deleting report', [\n 'team' => $team->getId(),\n 'report' => $report->getId(),\n ]);\n\n $this->deleteAllReportResults($report);\n\n $report->delete();\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Cancel Running Statements","depth":4,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Playground","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"jiminny","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"18","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"14","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"2","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"4","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"SELECT a.id, a.uuid, a.actual_start_time, o.id, o.uuid FROM opportunities o\nJOIN activities a ON o.id = a.opportunity_id\nWHERE a.crm_configuration_id = 39\nAND a.actual_start_time > '2025-10-13'\nAND a.type IN ('conference', 'softphone-inbound', 'softphone-outbound')\n;\n\nSELECT * FROM activities\nWHERE crm_configuration_id = 39 and user_id = 143\nand actual_start_time >= '2025-10-13'\nAND type IN ('conference', 'softphone-inbound', 'softphone-outbound')\n;\n\nSELECT * FROM opportunities WHERE account_id IN (178);\nselect * from activities where id IN (620137, 620187, 620188, 620189, 620230);\n\n# HS\nSELECT * FROM opportunities WHERE id IN (238);\nselect * from activities where id IN (477,2076);\n\nselect * from users;\n\nSELECT COUNT(*) FROM users;\nSELECT COUNT(*) FROM activities;\nSELECT COUNT(*) FROM opportunities;\n\nUPDATE activities\nSET\n actual_start_time = '2025-12-19 09:00:00',\n actual_end_time = '2025-12-19 10:30:00',\n scheduled_start_time = '2025-12-19 09:00:00',\n scheduled_end_time = '2025-12-19 10:30:00'\nWHERE id IN (407509,407375);\n\nselect * from partners;\n\nSELECT id, uuid, type, actual_start_time, user_id, crm_configuration_id\nFROM activities\nWHERE user_id = 143\nAND actual_start_time >= '2025-10-13 00:00:00'\nAND actual_start_time <= '2026-01-13 23:59:59'\nORDER BY actual_start_time DESC;\n\nSELECT * FROM activities WHERE uuid_to_bin('78eda160-3086-435f-88a5-bb0c71b6008d') = uuid;\nSELECT * FROM crm_layouts where crm_configuration_id = 39;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 282;\n# lead_id\n# account_id 177\n# contact_id 3969\n# opportunity_id\n# stage_id 203\n\nSELECT * FROM opportunities WHERE opportunities.crm_configuration_id = id = 282;\n\nSELECT * FROM activities where crm_configuration_id = 39 AND type = 'conference'\nAND user_id = 143 and actual_start_time >= '2025-10-13';\n\nSELECT * FROM activities a\n# JOIN opportunities o ON a.opportunity_id = o.id\nWHERE a.crm_configuration_id = 39 AND a.type = 'conference'\nand status = 'completed' and recording_state = 'recorded'\nand a.actual_start_time >= '2025-10-13'\nAND a.user_id = 143\n;\n\nselect * from leads\nwhere crm_configuration_id = 39; # 112 -> ac. 178, 109 => op. 1707\n\nSELECT * FROM activities WHERE id IN (356013,616188,616202,616310,407509,407375,356001,356008);\nSELECT * FROM activities WHERE id IN (356013,616188,616202,616310);\nSELECT * FROM activities WHERE id IN (407509,407375); # leads: 112, 109 | status - 198\nSELECT * FROM activities WHERE id IN (356001, 356008); # contacts:\n\nSELECT * FROM opportunities WHERE id IN (1707);\nSELECT * FROM stages where id IN (204, 198);\nSELECT * FROM opportunities WHERE account_id IN (178);\nSELECT * FROM opportunities WHERE crm_configuration_id = 39 AND created_at > '2025-01-01';\nSELECT * FROM contacts WHERE account_id IN (178); # 4118 Musaibe, 4448 Ceco Personal\n\nSELECT * FROM activities where crm_configuration_id = 39\nAND opportunity_id IS NULL\nAND is_internal = false\nand status = 'completed' and recording_state = 'recorded'\nAND actual_start_time >= '2025-10-13'\nAND (lead_id IS NOT NULL OR contact_id IS NOT NULL OR account_id IS NOT NULL)\n# AND lead_id IN (112, 109)\n;\n\nSELECT * FROM crm_profiles WHERE user_id = 143;\n\nselect * from inboxes; # 212\nselect * from users where id = 143; # 143\nselect * from inbox_email_batches where inbox_id = 212\nand updated_at >= '2026-01-28 00:00:00' order by id desc;\nselect * from inbox_emails where inbox_id = 212\nand batch_id = 95885 order by id desc;\nselect * from email_messages where origin_user_id = 143;\nselect * from activities where user_id = 143 and updated_at >= '2026-01-28 00:00:00';\nselect * from participants where activity_id = 620247;\n\nselect * from crm_profiles where user_id = 143;\n\nSELECT * FROM activities WHERE uuid_to_bin('458cf915-b914-4000-b083-5687b32b2956') = uuid; # 356001\nselect * from transcription where activity_id = 356001; # 6943\nselect * from ai_prompts where transcription_id = 6943;\nSELECT * FROM activity_summary_logs where activity_id = 356001;\n\nSELECT * FROM social_accounts WHERE sociable_id = 143;\n\n# ************************************************************************************\nSELECT * FROM activities WHERE uuid_to_bin('0164a4fb-cb95-454e-9edd-4d804e4999bd') = uuid;\n# 422515 softphone tr. 8100\n\nSELECT * FROM activities WHERE uuid_to_bin('7520add8-8d87-41a5-98e5-fc4edf96f21e') = uuid;\n# 407509 conference tr. 7670 crmId: 00UD1000002J9aTMAS\n\nselect * from ai_prompts where transcription_id IN (8100, 7670);\nselect * from activity_summary_logs where activity_id = 407509;\n\nselect * from sidekick_settings;\nselect * from default_activity_types;\n\nSELECT * FROM contacts WHERE crm_configuration_id = 39 and email = 'm.kogoj@gmx.at';\nSELECT * FROM leads WHERE crm_configuration_id = 39 and email = 'm.kogoj@gmx.at';\n\nSELECT * FROM activity_searches where user_id = 143;\nSELECT * FROM groups where team_id = 1;\n\nselect * from teams where id = 1;\nselect * from groups where team_id = 1; # 1150 - 7e75f8025c22\nselect id, name, group_id, status, deleted_at, email\nfrom users where team_id = 1 order by group_id desc ;\n\nselect * from activity_searches where id in (1977, 1978, 1979);\nselect * from activity_search_filters where activity_search_id IN (1977, 1978, 1979);\nselect * from activity_search_filters where filter = 'group_id' and value = '443f26b8-8512-437e-a9f9-7e75f8025c22'; # 10268, 10272, 10277\nselect * from nudges where activity_search_id IN (1977, 1978, 1979); # 877, 878, 879\n\nINSERT INTO `activity_search_filters`\n(`activity_search_id`, `filter`, `value`) VALUES\n(1977, 'group_id', '443f26b8-8512-437e-a9f9-7e75f8025c22'),\n(1978, 'group_id', '443f26b8-8512-437e-a9f9-7e75f8025c22'),\n(1979, 'group_id', '443f26b8-8512-437e-a9f9-7e75f8025c22')\n;\n\nselect * from crm_configurations where id = 39;\n\n\nselect sa.* from users u JOIN social_accounts sa on u.id = sa.sociable_id\nwhere u.team_id = 1;\nSELECT * FROM social_accounts WHERE sociable_id = 1635;\nSELECT * FROM users WHERE id = 1635;\n\nselect * from teams where id = 1;\nselect * from users where team_id = 1;\nselect * from team_features where team_id = 1;\nselect * from features;\n\nSELECT * FROM activity_searches where id = 1982; # 1981\nSELECT * FROM activity_search_filters WHERE activity_search_id = 1982;\n\nSELECT * FROM automated_reports where id = 68;\nUPDATE automated_reports set playbook_categories = NULL where id = 68;\nSELECT * FROM automated_report_results where id = 275;\n\nSELECT * FROM automated_reports order by id desc;\nSELECT * FROM automated_report_results order by id desc;\nselect * from activity_searches where user_id = 143;\nselect * from ask_anything_prompts;\n\nSELECT * FROM groups WHERE id = 1439;\nSELECT * FROM users WHERE group_id = 1439;\n\nselect * from permissions; # 158\nselect * from roles;\nselect * from permission_role\n\nselect * from teams where id = 1;\nselect * from groups g JOIN playbooks p on g.playbook_id = p.id where g.team_id = 1;\nselect * from groups where id = 28;\nselect * from playbooks where team_id = 1;\nselect * from playbooks where id = 179;\nselect * from playbook_categories where id = 1391;\nselect * from users where id = 143;\nselect * from crm_profiles where user_id = 143;\nselect * from activities where crm_configuration_id = 39 and type = 'conference'\nand crm_provider_id IS NOT NULL ORDER by id desc;\nselect * from activities where id = 422003; # 00UO400000pB6fpMAC\n\nSELECT ar.id, ar.uuid, ar.media_type, ar.status, a.type\nFROM automated_report_results ar\nJOIN automated_reports a ON a.id = ar.report_id\nWHERE a.type = 'ask_jiminny'\nLIMIT 10;\n\nSELECT `automated_report_results`.* FROM `automated_report_results`\nINNER JOIN `automated_reports`\n ON `automated_report_results`.`report_id` = `automated_reports`.`id`\nWHERE `automated_report_results`.`generated_at` IS NOT NULL\n AND `automated_reports`.`team_id` = 1\n AND JSON_CONTAINS(`automated_reports`.`recipients`, 1635, '$.\"users\"')\n;\n\n\nselect * from teams where id = 3143;\nselect * from crm_configurations where id = 500;\nselect * from users where name = 'Integration Account'; # 1695\nSELECT * FROM social_accounts WHERE sociable_id = 1695;\n\nselect * from activities where crm_configuration_id = 39\nand recording_state = 'recorded' and duration > 60\nand status = 'completed' and actual_start_time >= '2025-12-01';\n\nSELECT * FROM activities WHERE uuid_to_bin('458cf915-b914-4000-b083-5687b32b2956') = uuid;\n\nselect * from leads;","depth":4,"value":"SELECT a.id, a.uuid, a.actual_start_time, o.id, o.uuid FROM opportunities o\nJOIN activities a ON o.id = a.opportunity_id\nWHERE a.crm_configuration_id = 39\nAND a.actual_start_time > '2025-10-13'\nAND a.type IN ('conference', 'softphone-inbound', 'softphone-outbound')\n;\n\nSELECT * FROM activities\nWHERE crm_configuration_id = 39 and user_id = 143\nand actual_start_time >= '2025-10-13'\nAND type IN ('conference', 'softphone-inbound', 'softphone-outbound')\n;\n\nSELECT * FROM opportunities WHERE account_id IN (178);\nselect * from activities where id IN (620137, 620187, 620188, 620189, 620230);\n\n# HS\nSELECT * FROM opportunities WHERE id IN (238);\nselect * from activities where id IN (477,2076);\n\nselect * from users;\n\nSELECT COUNT(*) FROM users;\nSELECT COUNT(*) FROM activities;\nSELECT COUNT(*) FROM opportunities;\n\nUPDATE activities\nSET\n actual_start_time = '2025-12-19 09:00:00',\n actual_end_time = '2025-12-19 10:30:00',\n scheduled_start_time = '2025-12-19 09:00:00',\n scheduled_end_time = '2025-12-19 10:30:00'\nWHERE id IN (407509,407375);\n\nselect * from partners;\n\nSELECT id, uuid, type, actual_start_time, user_id, crm_configuration_id\nFROM activities\nWHERE user_id = 143\nAND actual_start_time >= '2025-10-13 00:00:00'\nAND actual_start_time <= '2026-01-13 23:59:59'\nORDER BY actual_start_time DESC;\n\nSELECT * FROM activities WHERE uuid_to_bin('78eda160-3086-435f-88a5-bb0c71b6008d') = uuid;\nSELECT * FROM crm_layouts where crm_configuration_id = 39;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 282;\n# lead_id\n# account_id 177\n# contact_id 3969\n# opportunity_id\n# stage_id 203\n\nSELECT * FROM opportunities WHERE opportunities.crm_configuration_id = id = 282;\n\nSELECT * FROM activities where crm_configuration_id = 39 AND type = 'conference'\nAND user_id = 143 and actual_start_time >= '2025-10-13';\n\nSELECT * FROM activities a\n# JOIN opportunities o ON a.opportunity_id = o.id\nWHERE a.crm_configuration_id = 39 AND a.type = 'conference'\nand status = 'completed' and recording_state = 'recorded'\nand a.actual_start_time >= '2025-10-13'\nAND a.user_id = 143\n;\n\nselect * from leads\nwhere crm_configuration_id = 39; # 112 -> ac. 178, 109 => op. 1707\n\nSELECT * FROM activities WHERE id IN (356013,616188,616202,616310,407509,407375,356001,356008);\nSELECT * FROM activities WHERE id IN (356013,616188,616202,616310);\nSELECT * FROM activities WHERE id IN (407509,407375); # leads: 112, 109 | status - 198\nSELECT * FROM activities WHERE id IN (356001, 356008); # contacts:\n\nSELECT * FROM opportunities WHERE id IN (1707);\nSELECT * FROM stages where id IN (204, 198);\nSELECT * FROM opportunities WHERE account_id IN (178);\nSELECT * FROM opportunities WHERE crm_configuration_id = 39 AND created_at > '2025-01-01';\nSELECT * FROM contacts WHERE account_id IN (178); # 4118 Musaibe, 4448 Ceco Personal\n\nSELECT * FROM activities where crm_configuration_id = 39\nAND opportunity_id IS NULL\nAND is_internal = false\nand status = 'completed' and recording_state = 'recorded'\nAND actual_start_time >= '2025-10-13'\nAND (lead_id IS NOT NULL OR contact_id IS NOT NULL OR account_id IS NOT NULL)\n# AND lead_id IN (112, 109)\n;\n\nSELECT * FROM crm_profiles WHERE user_id = 143;\n\nselect * from inboxes; # 212\nselect * from users where id = 143; # 143\nselect * from inbox_email_batches where inbox_id = 212\nand updated_at >= '2026-01-28 00:00:00' order by id desc;\nselect * from inbox_emails where inbox_id = 212\nand batch_id = 95885 order by id desc;\nselect * from email_messages where origin_user_id = 143;\nselect * from activities where user_id = 143 and updated_at >= '2026-01-28 00:00:00';\nselect * from participants where activity_id = 620247;\n\nselect * from crm_profiles where user_id = 143;\n\nSELECT * FROM activities WHERE uuid_to_bin('458cf915-b914-4000-b083-5687b32b2956') = uuid; # 356001\nselect * from transcription where activity_id = 356001; # 6943\nselect * from ai_prompts where transcription_id = 6943;\nSELECT * FROM activity_summary_logs where activity_id = 356001;\n\nSELECT * FROM social_accounts WHERE sociable_id = 143;\n\n# ************************************************************************************\nSELECT * FROM activities WHERE uuid_to_bin('0164a4fb-cb95-454e-9edd-4d804e4999bd') = uuid;\n# 422515 softphone tr. 8100\n\nSELECT * FROM activities WHERE uuid_to_bin('7520add8-8d87-41a5-98e5-fc4edf96f21e') = uuid;\n# 407509 conference tr. 7670 crmId: 00UD1000002J9aTMAS\n\nselect * from ai_prompts where transcription_id IN (8100, 7670);\nselect * from activity_summary_logs where activity_id = 407509;\n\nselect * from sidekick_settings;\nselect * from default_activity_types;\n\nSELECT * FROM contacts WHERE crm_configuration_id = 39 and email = 'm.kogoj@gmx.at';\nSELECT * FROM leads WHERE crm_configuration_id = 39 and email = 'm.kogoj@gmx.at';\n\nSELECT * FROM activity_searches where user_id = 143;\nSELECT * FROM groups where team_id = 1;\n\nselect * from teams where id = 1;\nselect * from groups where team_id = 1; # 1150 - 7e75f8025c22\nselect id, name, group_id, status, deleted_at, email\nfrom users where team_id = 1 order by group_id desc ;\n\nselect * from activity_searches where id in (1977, 1978, 1979);\nselect * from activity_search_filters where activity_search_id IN (1977, 1978, 1979);\nselect * from activity_search_filters where filter = 'group_id' and value = '443f26b8-8512-437e-a9f9-7e75f8025c22'; # 10268, 10272, 10277\nselect * from nudges where activity_search_id IN (1977, 1978, 1979); # 877, 878, 879\n\nINSERT INTO `activity_search_filters`\n(`activity_search_id`, `filter`, `value`) VALUES\n(1977, 'group_id', '443f26b8-8512-437e-a9f9-7e75f8025c22'),\n(1978, 'group_id', '443f26b8-8512-437e-a9f9-7e75f8025c22'),\n(1979, 'group_id', '443f26b8-8512-437e-a9f9-7e75f8025c22')\n;\n\nselect * from crm_configurations where id = 39;\n\n\nselect sa.* from users u JOIN social_accounts sa on u.id = sa.sociable_id\nwhere u.team_id = 1;\nSELECT * FROM social_accounts WHERE sociable_id = 1635;\nSELECT * FROM users WHERE id = 1635;\n\nselect * from teams where id = 1;\nselect * from users where team_id = 1;\nselect * from team_features where team_id = 1;\nselect * from features;\n\nSELECT * FROM activity_searches where id = 1982; # 1981\nSELECT * FROM activity_search_filters WHERE activity_search_id = 1982;\n\nSELECT * FROM automated_reports where id = 68;\nUPDATE automated_reports set playbook_categories = NULL where id = 68;\nSELECT * FROM automated_report_results where id = 275;\n\nSELECT * FROM automated_reports order by id desc;\nSELECT * FROM automated_report_results order by id desc;\nselect * from activity_searches where user_id = 143;\nselect * from ask_anything_prompts;\n\nSELECT * FROM groups WHERE id = 1439;\nSELECT * FROM users WHERE group_id = 1439;\n\nselect * from permissions; # 158\nselect * from roles;\nselect * from permission_role\n\nselect * from teams where id = 1;\nselect * from groups g JOIN playbooks p on g.playbook_id = p.id where g.team_id = 1;\nselect * from groups where id = 28;\nselect * from playbooks where team_id = 1;\nselect * from playbooks where id = 179;\nselect * from playbook_categories where id = 1391;\nselect * from users where id = 143;\nselect * from crm_profiles where user_id = 143;\nselect * from activities where crm_configuration_id = 39 and type = 'conference'\nand crm_provider_id IS NOT NULL ORDER by id desc;\nselect * from activities where id = 422003; # 00UO400000pB6fpMAC\n\nSELECT ar.id, ar.uuid, ar.media_type, ar.status, a.type\nFROM automated_report_results ar\nJOIN automated_reports a ON a.id = ar.report_id\nWHERE a.type = 'ask_jiminny'\nLIMIT 10;\n\nSELECT `automated_report_results`.* FROM `automated_report_results`\nINNER JOIN `automated_reports`\n ON `automated_report_results`.`report_id` = `automated_reports`.`id`\nWHERE `automated_report_results`.`generated_at` IS NOT NULL\n AND `automated_reports`.`team_id` = 1\n AND JSON_CONTAINS(`automated_reports`.`recipients`, 1635, '$.\"users\"')\n;\n\n\nselect * from teams where id = 3143;\nselect * from crm_configurations where id = 500;\nselect * from users where name = 'Integration Account'; # 1695\nSELECT * FROM social_accounts WHERE sociable_id = 1695;\n\nselect * from activities where crm_configuration_id = 39\nand recording_state = 'recorded' and duration > 60\nand status = 'completed' and actual_start_time >= '2025-12-01';\n\nSELECT * FROM activities WHERE uuid_to_bin('458cf915-b914-4000-b083-5687b32b2956') = uuid;\n\nselect * from leads;","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-8553716549540953071
|
1126710648141684156
|
visual_change
|
accessibility
|
NULL
|
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
AutomatedReportsRepositoryTest
Run 'AutomatedReportsRepositoryTest'
Debug 'AutomatedReportsRepositoryTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
1
103
3
34
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Kiosk\AutomatedReports;
use Carbon\CarbonImmutable;
use Carbon\CarbonInterface;
use Carbon\Exceptions\InvalidFormatException;
use DateTime;
use DateTimeInterface;
use DateTimeZone;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Jiminny\Component\ActivitySearch\FilterDefinition\InputTypeEnum;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Component\AskAnything\Dtos\AskAnythingPromptDto;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Repositories\UserRepository;
use Jiminny\Exceptions\ApplicationException;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\ModelNotFoundException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateReportJob;
use Jiminny\Models\Activity\Search;
use Jiminny\Models\AskAnything\AskAnythingPrompt;
use Jiminny\Models\AskAnything\AskAnythingPromptTarget;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AskAnythingRepository;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\StageRepository;
use Throwable;
class AutomatedReportsService
{
public const string TYPE_LOSS_ANALYSIS = 'loss_analysis';
public const string TYPE_ASK_JIMINNY = 'ask_jiminny';
/**
* Standard report types (used by kiosk for existing automated reports).
*/
// @TODO this will add filter, however if we need to control feature by FF we need conditional logic
public const array TYPES = [
['id' => 'exec_summary', 'name' => 'Exec Summary'],
['id' => 'coaching_profiles', 'name' => 'Coaching Profiles'],
['id' => 'product_feedback', 'name' => 'Product Feedback'],
['id' => self::TYPE_LOSS_ANALYSIS, 'name' => 'Loss Analysis'],
// ['id' => 'questions', 'name' => 'Questions'],
// ['id' => 'statistical_quant', 'name' => 'Statistical Quantitative'],
];
public const array ALL_TYPES = [
...self::TYPES,
['id' => self::TYPE_ASK_JIMINNY, 'name' => 'Ask Jiminny'],
];
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
/**
* Frequencies for standard (non-Ask Jiminny) reports.
*/
public const array FREQUENCIES = [
['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],
['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],
['id' => self::FREQUENCY_QUARTERLY, 'name' => 'Quarterly'],
['id' => self::FREQUENCY_ONE_OFF, 'name' => 'One-off'],
];
/**
* Frequencies for Ask Jiminny reports.
*/
public const array ASK_JIMINNY_FREQUENCIES = [
['id' => self::FREQUENCY_DAILY, 'name' => 'Daily'],
['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],
['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],
];
public const string MEDIA_TYPE_PDF = 'pdf';
public const string MEDIA_TYPE_PODCAST = 'podcast';
public const array MEDIA_TYPES = [self::MEDIA_TYPE_PDF, self::MEDIA_TYPE_PODCAST];
public const array MEDIA_TYPE_OBJECT_PDF = ['id' => self::MEDIA_TYPE_PDF, 'name' => 'PDF'];
public const array MEDIA_TYPE_OBJECT_PODCAST = ['id' => self::MEDIA_TYPE_PODCAST, 'name' => 'Podcast'];
public const array MEDIA_TYPE_OBJECTS = [self::MEDIA_TYPE_OBJECT_PDF, self::MEDIA_TYPE_OBJECT_PODCAST];
public const array CALL_TYPE_CONFERENCE = ['id' => 'conference', 'name' => 'Conference'];
public const array CALL_TYPE_DIALER = ['id' => 'dialer', 'name' => 'Dialer'];
public const int SENT_REPORT_AT_HOURS = 5;
public const string PDF_KEY = 'pdf';
public const string AUDIO_KEY = 'audio';
private const array ALL_FREQUENCIES = [
['id' => self::FREQUENCY_DAILY, 'name' => 'Daily'],
['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],
['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],
['id' => self::FREQUENCY_QUARTERLY, 'name' => 'Quarterly'],
['id' => self::FREQUENCY_ONE_OFF, 'name' => 'One-off'],
];
private const string S3_DIR = 'reports';
private const array FILE_EXTENSIONS_VARIANTS = ['html', 'MD', 'pdf'];
private const array FILE_PODCAST_EXTENSIONS_VARIANTS = ['json', 'mp3', 'ssml'];
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly GroupRepository $groupRepository,
private readonly UserRepository $userRepository,
private readonly StageRepository $stageRepository,
private readonly DealStagesService $dealStagesService,
private readonly RecipientsService $recipientsService,
private readonly AutomatedReportsRepository $automatedReportsRepository,
private readonly Webhook $webhookService,
private readonly BusDispatcher $dispatcher,
private readonly ActivityTypeService $activityTypeService,
private readonly PlaybookCategoryRepository $playbookCategoryRepository,
private readonly AskAnythingPromptService $askAnythingPromptService,
private readonly SearchRepository $activitySearchRepository,
private readonly AskAnythingRepository $askAnythingRepository,
) {
}
public static function getTypes(): array
{
$types = self::TYPES;
return array_map(static function ($type) {
return $type['id'];
}, $types);
}
public static function getCallTypes(): array
{
return array_map(static function ($callType) {
return $callType['id'];
}, [self::CALL_TYPE_CONFERENCE, self::CALL_TYPE_DIALER]);
}
public static function getFrequencies(): array
{
return array_map(static function ($frequency) {
return $frequency['id'];
}, self::FREQUENCIES);
}
// front-facing structure
public function getReportEnabledFieldData(bool $value = false): array
{
return [
'id' => 'report_enabled',
'label' => '',
'inputType' => InputTypeEnum::TOGGLE,
'value' => $value,
];
}
// Organizations = Teams
public function getOrganizationFieldData(?string $value = null, bool $shortVersion = false): array
{
$options = $this->getTeams();
if ($shortVersion) {
return [
'id' => 'organization',
'label' => 'Organization',
'options' => $options,
];
}
return [
'id' => 'organization',
'label' => 'Organization',
'inputType' => InputTypeEnum::DROPDOWN,
'required' => true,
'placeholder' => 'Select',
'options' => $options,
'value' => $value,
'dependencies' => [
'teams',
'deal_stage_at_call',
'current_deal_stage',
'recipients',
ActivityTypeService::PLAYBOOK_CATEGORIES_KEY,
],
'dependsOn' => [],
];
}
// Teams = Groups
public function getTeamFieldData(array $options = [], array $value = [], bool $shortVersion = false): array
{
if ($shortVersion) {
return [
'id' => 'teams',
'label' => 'Team',
'options' => $options,
];
}
return [
'id' => 'teams',
'label' => 'Team',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'required' => false,
'placeholder' => 'Select',
'options' => $options,
'value' => $value, // value should be an array of objects {id, name}
'dependencies' => [ActivityTypeService::PLAYBOOK_CATEGORIES_KEY],
'dependsOn' => [],
];
}
public function getReportTypeFieldData(?string $value = null, bool $shortVersion = false, ?Team $team = null): array
{
$types = [];
if ($team instanceof Team) {
if ($team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {
$types = self::TYPES;
}
if ($team->hasFeature(FeatureEnum::ASK_JIMINNY_REPORTS)) {
$types[] = ['id' => self::TYPE_ASK_JIMINNY, 'name' => 'Ask Jiminny'];
}
} else {
$types = self::TYPES;
}
if ($shortVersion) {
return [
'id' => 'report_type',
'label' => 'Report Type',
'options' => $types,
];
}
return [
'id' => 'report_type',
'label' => 'Report Type',
'inputType' => InputTypeEnum::DROPDOWN,
'required' => true,
'placeholder' => 'Select',
'options' => $types,
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getFrequencyFieldData(?string $value = null): array
{
return [
'id' => 'frequency',
'label' => 'Frequency',
'inputType' => InputTypeEnum::DROPDOWN,
'required' => true,
'placeholder' => 'Select',
'options' => self::FREQUENCIES,
'value' => $value,
'dependencies' => ['period'],
'dependsOn' => [],
];
}
public function getPeriodFieldData(?string $valueStartDate = null, ?string $valueEndDate = null): array
{
return [
'id' => 'period',
'label' => 'Select one-off period',
'inputType' => InputTypeEnum::DATE_RANGE,
'required' => true,
'placeholder' => 'Select',
'value' => ['startDate' => $valueStartDate, 'endDate' => $valueEndDate],
'queryParams' => [
'startDate' => 'start_date_period',
'endDate' => 'end_date_period',
],
'dependencies' => [],
'dependsOn' => ['frequency'],
];
}
public function getActivityTypesFieldData(?Team $team = null, array $value = [], array $teamsFilter = []): array
{
return $this->activityTypeService->getActivityTypeFieldData(team: $team, value: $value, groupIds: $teamsFilter);
}
public function getDealStageAtCallFieldData(?Team $team = null, array $value = []): array
{
return $this->dealStagesService->getDealStageAtCallFieldData(team: $team, value: $value);
}
public function getCurrentDealStageFieldData(?Team $team = null, array $value = []): array
{
return $this->dealStagesService->getCurrentDealStageFieldData(team: $team, value: $value);
}
public function getDealValueFieldData(?int $valueMin = null, ?int $valueMax = null): array
{
return [
'id' => 'deal_value',
'label' => 'Deal Value',
'inputType' => InputTypeEnum::INTEGER_RANGE,
'required' => false,
'value' => ['min' => $valueMin, 'max' => $valueMax],
'queryParams' => [
'min' => 'min_deal_value',
'max' => 'max_deal_value',
],
'dependencies' => [],
'dependsOn' => [],
];
}
public function getCallTypeFieldData(bool $conferenceOn = false, bool $dialerOn = false): array
{
$value = [];
$conferenceOn && $value[] = self::CALL_TYPE_CONFERENCE;
$dialerOn && $value[] = self::CALL_TYPE_DIALER;
return [
'id' => 'call_type',
'label' => 'Call Type',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'required' => true,
'options' => [
self::CALL_TYPE_CONFERENCE,
self::CALL_TYPE_DIALER,
],
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getMediaTypeFieldData(?AutomatedReport $report = null): array
{
$value = [];
if ($report) {
$value = $this->transformMediaTypes($report);
}
return [
'id' => 'media_types',
'label' => 'Export as',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'required' => true,
'options' => self::MEDIA_TYPE_OBJECTS,
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getCallDurationFieldData(?int $valueMin = null, ?int $valueMax = null): array
{
return [
'id' => 'call_duration',
'label' => 'Call Duration',
'inputType' => InputTypeEnum::INTEGER_RANGE,
'required' => false,
'value' => ['min' => $valueMin, 'max' => $valueMax],
'queryParams' => [
'min' => 'min_call_duration',
'max' => 'max_call_duration',
],
'dependencies' => [],
'dependsOn' => [],
];
}
public function getRecipientsFieldData(?Team $team = null, array $value = []): array
{
return $this->recipientsService->getRecipientsFieldData(team: $team, value: $value);
}
public function getJiminnyRecipientsFieldData(array $value = []): array
{
return $this->recipientsService->getJiminnyRecipientsFieldData($value);
}
public function getAdditionalPromptInputFieldData(?string $value = null): array
{
return [
'id' => 'additional_prompt_input',
'label' => 'Special requirements',
'inputType' => InputTypeEnum::TEXTAREA,
'required' => false,
'placeholder' => 'What should be the focus of the report?',
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getCustomReportNameFieldData(?string $value = null): array
{
return [
'id' => 'custom_name',
'label' => 'Custom report name',
'inputType' => InputTypeEnum::TEXT,
'required' => false,
'placeholder' => 'Enter custom name',
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
// data providers
public function getTeams(): array
{
$teams = $this->teamRepository->getTeamsForKiosk(status: Team::STATUS_ACTIVE);
$teamData = [];
foreach ($teams as $team) {
if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {
continue;
}
$teamData[] = $this->transformTeam($team);
}
return $teamData;
}
public function getTeamGroups(string $teamUuid): array
{
$data = [];
$team = $this->getTeam($teamUuid);
if ($team !== null) {
$groups = $team->groups()->get();
foreach ($groups as $group) {
$data[] = [
'id' => $group->getUuid(),
'name' => $group->getName(),
];
}
}
return $data;
}
public function getTeamsGroupsOptions(array $filterTeamUuids = []): array
{
$data = [];
$teams = $this->getTeams();
foreach ($teams as $team) {
if (! empty($filterTeamUuids) && ! in_array($team['id'], $filterTeamUuids, true)) {
continue;
}
$data[] = [
'label' => $team['name'],
'groups' => $this->getTeamGroups($team['id']),
];
}
return $data;
}
public function getTeam(string $teamUuid): ?Team
{
return $this->teamRepository->idOrUuid($teamUuid);
}
public function getTeamById(int $teamId): ?Team
{
return $this->teamRepository->find($teamId);
}
public function getGroupsUuids(AutomatedReport $report): array
{
$uuids = [];
$reportGroups = $report->getGroups();
foreach ($reportGroups as $groupId) {
if ($group = $this->groupRepository->find($groupId)) {
$uuids[] = $group->getUuid();
}
}
return $uuids;
}
public function getPlaybookCategoriesUuids(AutomatedReport $report): array
{
$uuids = [];
$playbookCategories = $report->getPlaybookCategories();
foreach ($playbookCategories as $id) {
if ($category = $this->playbookCategoryRepository->find($id)) {
$uuids[] = $category->getUuid();
}
}
return $uuids;
}
public function getDealAtCallStagesUuids(AutomatedReport $report): array
{
$uuids = [];
$reportStages = $report->getDealAtCallStages();
foreach ($reportStages as $id) {
if ($stage = $this->stageRepository->find($id)) {
$uuids[] = $stage->getUuid();
}
}
return $uuids;
}
public function getCurrentDealStagesUuids(AutomatedReport $report): array
{
$uuids = [];
$reportStages = $report->getCurrentDealStages();
foreach ($reportStages as $id) {
if ($stage = $this->stageRepository->find($id)) {
$uuids[] = $stage->getUuid();
}
}
return $uuids;
}
public function getUsersUuids(AutomatedReport $report): array
{
return $this->extractUserUuids($report->getRecipients());
}
public function getJiminnyUsersUuids(AutomatedReport $report): array
{
return $this->extractUserUuids($report->getJiminnyRecipients());
}
/**
* @param array<string, mixed> $recipients
*/
private function extractUserUuids(array $recipients): array
{
$userIds = $recipients['users'] ?? [];
return collect($userIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->map(fn (UserContract $user) => $user->getUuid())
->values()
->all();
}
// get mail data
public function getRecipientUsers(AutomatedReport $report): array
{
return $this->buildRecipientUsers($report->getRecipients());
}
/**
* @return array<UserContract>
*/
public function getRecipientUserObjects(AutomatedReport $report): array
{
$userIds = $report->getRecipients()['users'] ?? [];
return collect($userIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->values()
->all();
}
private function getJiminnyRecipientUsers(AutomatedReport $report): array
{
return $this->buildRecipientUsers($report->getJiminnyRecipients());
}
/**
* @param array<string, mixed> $recipients
*/
private function buildRecipientUsers(array $recipients): array
{
$userIds = $recipients['users'] ?? [];
return collect($userIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->map(fn (UserContract $user) => [
'email' => $user->getEmailAddress(),
'name' => $user->getName(),
'timezone' => $user->getTimezone()->getName(),
])
->values()
->all();
}
public function getValidRecipientUsers(AutomatedReport $report, bool $includeJiminny = false): array
{
if ($report->isAskJiminnyReport()) {
$recipients = $this->resolveAskJiminnyRecipients($report);
} else {
$recipients = $this->getRecipientUsers($report);
if ($includeJiminny) {
$recipients = array_merge($recipients, $this->getJiminnyRecipientUsers($report));
}
}
$emails = [];
return array_values(array_filter(
$recipients,
static function ($recipient) use (&$emails) {
if (empty($recipient['email']) || in_array($recipient['email'], $emails, true)) {
return false;
}
$emails[] = $recipient['email'];
return true;
}
));
}
private function resolveAskJiminnyRecipients(AutomatedReport $report): array
{
$recipients = [];
$creator = $report->getCreator();
if ($creator !== null) {
$recipients[] = [
'email' => $creator->getEmailAddress(),
'name' => $creator->getName(),
'timezone' => $creator->getTimezone()->getName(),
];
}
return array_merge(
$recipients,
$this->buildRecipientUsers($report->getRecipients()),
$this->getGroupRecipientUsers($report),
);
}
private function getGroupRecipientUsers(AutomatedReport $report): array
{
$users = [];
foreach ($report->getGroups() as $groupId) {
$group = $this->groupRepository->find($groupId);
if ($group === null) {
continue;
}
foreach ($group->getMembers() as $member) {
$users[] = [
'email' => $member->getEmailAddress(),
'name' => $member->getName(),
'timezone' => $member->getTimezone()->getName(),
];
}
}
return $users;
}
public function getReportTypeName(AutomatedReportResult $report): string
{
$type = $report->getReport()->getType();
$getType = $this->transformReportType($type);
return $getType['name'];
}
public function getReportPeriodName(AutomatedReportResult $report): string
{
$from = $report->getFromDate();
$to = $report->getToDate();
$frequency = $report->getReport()->getFrequency();
if ($from === null || $to === null) {
if (! $report->getReport()->isAskJiminnyReport()) {
$invalidPeriod = $from === null ? 'from' : 'to';
throw new ApplicationException('Report period is invalid: ' . $invalidPeriod);
}
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
}
return $this->formatReportPeriodName($frequency, $from, $to);
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function getReportTeamsName(AutomatedReportResult $report): string
{
$groups = $report->getGroups();
if (empty($groups)) {
return 'All';
}
// Get group names from repository
$groupNames = [];
foreach ($groups as $groupId) {
$group = $this->groupRepository->find($groupId);
if ($group) {
$groupNames[] = $group->getName();
}
}
if (count($groupNames) === 1) {
// Single team format
$teamsName = $groupNames[0];
} else {
// Multiple teams format
$teamsName = implode(', ', $groupNames);
}
return $teamsName;
}
public function getReportFileName(AutomatedReportResult $report): string
{
$customName = $report->getReport()->getCustomName();
$periodName = $this->getReportPeriodName($report);
$filenameSuffix = $this->getFilenameSuffix($report);
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
return $this->sanitizeFileName("{$customName} - {$periodName}");
}
$baseName = $this->getReportTypeName($report);
if ($filenameSuffix) {
$baseName .= " {$filenameSuffix}";
}
return $this->sanitizeFileName("{$baseName} - {$periodName} - {$this->getReportTeamsName($report)}");
}
public function getReportFileNameWithExtension(AutomatedReportResult $result): string
{
$extension = $this->getMediaTypeMetadata($result)['extension'];
return $this->getReportFileName($result) . '.' . $extension;
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
public function isUserRecipientOfReport(User $user, AutomatedReport $report): bool
{
$recipients = array_map('intval', $report->getRecipients()['users'] ?? []);
return in_array($user->getId(), $recipients);
}
public function transformReportResults(Collection $automatedReportResults): array
{
$data = [];
foreach ($automatedReportResults as $automatedReportResult) {
/** @var AutomatedReportResult $automatedReportResult */
$report = $automatedReportResult->getReport();
$createdBy = $report->getCreator();
$creator = [
'id' => $createdBy?->getUuid(),
'name' => $createdBy?->getName(),
'email' => $createdBy?->getEmailAddress(),
'photoUrl' => $createdBy?->getPhotoUrl(),
];
$recipients = $this->buildRecipients($report);
$data[] = [
'id' => $automatedReportResult->getUuid(),
'name' => $automatedReportResult->getName(),
'frequency' => $this->transformFrequency($report->getFrequency()),
'recipients' => [
...array_values($this->transformGroups(team: $report->getTeam(), groupsIds: $report->getGroups())),
...array_values($this->transformRecipients($report->getRecipients())),
],
'report_type' => $this->transformReportType($report->getType()),
'media_type' => $automatedReportResult->getMediaType(),
'downloadUrl' => $this->generateReportResultDownloadUrl($automatedReportResult),
'viewUrl' => $this->generateReportResultViewUrl($automatedReportResult),
'generated_at' => $automatedReportResult->getGeneratedAt()?->toIso8601String(),
'creator' => $creator,
];
}
return $data;
}
public function hasCallTypeConference(AutomatedReport $report): bool
{
return in_array(self::CALL_TYPE_CONFERENCE['id'], $report->getCallTypes(), true);
}
public function hasCallTypeDialer(AutomatedReport $report): bool
{
return in_array(self::CALL_TYPE_DIALER['id'], $report->getCallTypes(), true);
}
// transformers
private function transformTeam(Team $team): array
{
if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {
return [];
}
return [
'id' => $team->getUuid(),
'name' => $team->getName(),
];
}
private function transformReportFullView(AutomatedReport $report): array
{
$base = $this->transformReportBase($report);
return $report->getType() === self::TYPE_ASK_JIMINNY
? $base + $this->transformAskJiminnyFields($report)
: $base + $this->transformStandardReportFields($report);
}
private function transformReportBase(AutomatedReport $report): array
{
return [
'id' => $report->getUuid(),
'organization' => $this->transformOrganization(team: $report->getTeam()),
'report_type' => $this->transformReportType($report->getType()),
'frequency' => $this->transformFrequency($report->getFrequency()),
];
}
private function transformStandardReportFields(AutomatedReport $report): array
{
$team = $report->getTeam();
return [
'report_enabled' => $report->getStatus(),
'start_date_period' => $report->getFrom()?->format('Y-m-d H:i:s'),
'end_date_period' => $report->getTo()?->format('Y-m-d H:i:s'),
'deal_value_min' => $report->getDealValueMin(),
'deal_value_max' => $report->getDealValueMax(),
'call_types' => $this->transformCallType($report->getCallTypes()),
'media_types' => $this->transformMediaTypes($report),
'call_duration_min' => $this->transformDurationToMinutes($report->getCallDurationMin()),
'call_duration_max' => $this->transformDurationToMinutes($report->getCallDurationMax()),
'teams' => $this->transformGroups(team: $team, groupsIds: $report->getGroups()),
'deal_at_call_stages' => $this->transformStages(team: $team, stagesIds: $report->getDealAtCallStages()),
'current_deal_stages' => $this->transformStages(team: $team, stagesIds: $report->getCurrentDealStages()),
'recipients' => $this->transformRecipients($report->getRecipients()),
'created_by' => $this->transformCreator($report->getCreator()),
'additional_prompt_input' => $report->getAdditionalPromptInput(),
'custom_name' => $report->getCustomName(),
'created_at' => $report->getCreatedAt()->format('Y-m-d H:i:s'),
'updated_at' => $report->getUpdatedAt()->format('Y-m-d H:i:s'),
'deleted_at' => $report->getDeletedAt()?->format('Y-m-d H:i:s'),
];
}
private function transformAskJiminnyFields(AutomatedReport $report): array
{
$team = $report->getTeam();
$creatorId = $report->getAttribute('created_by');
$explicitUserIds = array_values(array_filter(
$report->getRecipients()['users'] ?? [],
static fn ($id) => $id !== $creatorId
));
return [
'report_name' => $report->getCustomName(),
'enabled' => $report->getStatus(),
'share_teams' => $this->transformGroups(team: $team, groupsIds: $report->getGroups()),
'share_users' => $this->transformRecipients(['users' => $explicitUserIds]),
'saved_search' => $this->transformSafeSearch($report->getSavedSearch()),
'ask_jiminny_prompt' => $this->transformAskJiminnyPrompt($report->getAskAnythingPrompt()),
'expires_on' => $report->getExpiresAt()?->format('Y-m-d'),
];
}
private function transformOrganization(?Team $team): array
{
return [
'id' => $team?->getUuid(),
'name' => $team?->getName(),
];
}
private function transformReportType(string $type): array
{
foreach (self::ALL_TYPES as $typeItem) {
if ($typeItem['id'] === $type) {
return $typeItem;
}
}
return [];
}
private function transformCallType(array $types): array
{
$result = [];
$callTypes = [self::CALL_TYPE_CONFERENCE, self::CALL_TYPE_DIALER];
foreach ($types as $type) {
foreach ($callTypes as $callTypeItem) {
if ($callTypeItem['id'] === $type) {
$result[] = $callTypeItem;
break;
}
}
}
return $result;
}
private function transformMediaTypes(AutomatedReport $report): array
{
$values = [];
foreach ($report->getMediaTypes() as $mediaType) {
if (! in_array($mediaType, self::MEDIA_TYPES, true)) {
continue;
}
$values[] = match ($mediaType) {
self::MEDIA_TYPE_PDF => self::MEDIA_TYPE_OBJECT_PDF,
self::MEDIA_TYPE_PODCAST => self::MEDIA_TYPE_OBJECT_PODCAST,
};
}
return $values;
}
private function transformFrequency(string $frequency): array
{
foreach (self::ALL_FREQUENCIES as $frequencyItem) {
if ($frequencyItem['id'] === $frequency) {
return $frequencyItem;
}
}
return [];
}
public function transformDurationToMinutes(?int $duration): ?int
{
if (! $duration) {
return null;
}
return (int) ($duration / 60);
}
private function transformGroups(?Team $team, array $groupsIds): array
{
if (empty($groupsIds) || ! $team) {
return [];
}
$data = [];
foreach ($groupsIds as $groupId) {
$group = $team->groups()->where('id', $groupId)->first();
if ($group) {
$data[] = [
'id' => $group->getUuid(),
'name' => $group->getName(),
'photoUrl' => $group->getPhotoUrl(),
];
}
}
return $data;
}
private function transformStages(?Team $team, array $stagesIds): array
{
if (empty($stagesIds) || ! $team) {
return [];
}
$data = [];
foreach ($stagesIds as $stageId) {
$stage = $team->stages()->where('id', $stageId)->first();
if ($stage) {
$data[] = [
'id' => $stage->getUuid(),
'name' => $stage->getName(),
];
}
}
return $data;
}
private function transformRecipients(array $recipients): array
{
$users = [];
foreach ($recipients['users'] ?? [] as $userId) {
$users[] = $this->transformUser($userId);
}
return $users;
}
private function transformCreator(?User $user): ?array
{
if ($user === null) {
return null;
}
return $this->transformUser($user->getId());
}
private function transformAskJiminnyPrompt(?AskAnythingPrompt $prompt): ?array
{
if ($prompt === null) {
return null;
}
return [
'id' => $prompt->getUuid(),
'name' => $prompt->getTitle(),
];
}
private function transformSafeSearch(?Search $search): ?array
{
if ($search === null) {
return null;
}
return [
'id' => $search->getUuid(),
'name' => $search->getName(),
];
}
private function transformUser(int $userId): array
{
/* @var ?User $user */
$user = $this->userRepository->find($userId);
return [
'id' => $user?->getUuid(),
'name' => $user?->getName(),
'email' => $user?->getEmailAddress(),
'photoUrl' => $user?->getPhotoUrl(),
];
}
public function create(array $data): array
{
$validatedData = $this->validateAndTransformData($data);
$validatedData['created_by'] = auth()->id();
$automatedReport = $this->automatedReportsRepository->create($validatedData);
$this->generateOneOffReport($automatedReport);
return $this->transformReportFullView($automatedReport);
}
public function update(string $uuid, array $data): array
{
$validatedData = $this->validateAndTransformData($data);
$report = $this->automatedReportsRepository->findByUuid($uuid);
if (! $report) {
throw new InvalidArgumentException('Report not found');
}
$oldCustomName = $report->getCustomName();
$automatedReport = $this->automatedReportsRepository->update($report, $validatedData);
if ($oldCustomName !== $automatedReport->getCustomName()) {
$this->updateResultNames($automatedReport);
}
$this->generateOneOffReport($automatedReport);
return $this->transformReportFullView($automatedReport);
}
/**
* Create an Ask Jiminny report.
*/
public function createAskJiminnyReport(array $data, User $creator): array
{
$validatedData = $this->validateAskJiminnyReportData($data, $creator);
$validatedData['created_by'] = $creator->getId();
$automatedReport = $this->automatedReportsRepository->create($validatedData);
return $this->transformReportFullView($automatedReport);
}
/**
* Update an Ask Jiminny report.
*/
public function updateAskJiminnyReport(AutomatedReport $report, array $data, User $user): array
{
if (! $report->isAskJiminnyReport()) {
throw new InvalidArgumentException('Report is not an Ask Jiminny report');
}
$validatedData = $this->validateAskJiminnyReportData($data, $user);
$oldCustomName = $report->getCustomName();
$automatedReport = $this->automatedReportsRepository->update($report, $validatedData);
if ($oldCustomName !== $automatedReport->getCustomName()) {
$this->updateResultNames($automatedReport);
}
return $this->transformReportFullView($automatedReport);
}
public function updateAskJiminnyReportStatus(AutomatedReport $report, bool $status): array
{
$this->automatedReportsRepository->update($report, ['status' => $status]);
return $this->transformReportFullView($report->fresh());
}
/**
* Validate and transform data for Ask Jiminny reports.
*/
private function validateAskJiminnyReportData(array $data, User $user): array
{
// Validate name
$name = trim($data['report_name'] ?? '');
if (empty($name)) {
throw new InvalidArgumentException('Report name is required');
}
if (mb_strlen($name) > 50) {
throw new InvalidArgumentException('Report name must be 50 characters or less');
}
// Validate frequency (only daily, weekly, monthly for Ask Jiminny)
$frequency = $data['frequency'] ?? null;
$askJiminnyFrequencies = [self::FREQUENCY_DAILY, self::FREQUENCY_WEEKLY, self::FREQUENCY_MONTHLY];
if (! in_array($frequency, $askJiminnyFrequencies, true)) {
throw new InvalidArgumentException('Frequency must be daily, weekly, or monthly');
}
// Validate expiration date
$expiresAt = $data['expires_on'] ?? null;
if (empty($expiresAt)) {
throw new InvalidArgumentException('Expiration date is required');
}
try {
$expiresAtDate = Carbon::parse($expiresAt);
} catch (InvalidFormatException $e) {
throw new InvalidArgumentException('Expiration date format is invalid');
}
$maxExpiration = Carbon::now()->addYear()->endOfDay();
if ($expiresAtDate->gt($maxExpiration)) {
throw new InvalidArgumentException('Expiration date cannot be more than 1 year from now');
}
if ($expiresAtDate->isPast()) {
throw new InvalidArgumentException('Expiration date cannot be in the past');
}
// Validate saved search
$activitySearchId = $data['saved_search'] ?? null;
if (empty($activitySearchId)) {
throw new InvalidArgumentException('Saved search is required');
}
$savedSearch = $this->activitySearchRepository->findByUuidAndUser($activitySearchId, $user);
if (! $savedSearch) {
throw new InvalidArgumentException('Saved search not found or does not belong to you');
}
// Validate saved prompt
$askAnythingPromptId = $data['ask_jiminny_prompt'] ?? null;
if (empty($askAnythingPromptId)) {
throw new InvalidArgumentException('Ask Jiminny prompt is required');
}
$prompt = $this->askAnythingRepository->getPromptByUuid($askAnythingPromptId);
if (! $prompt) {
throw new InvalidArgumentException('Ask Jiminny prompt not found');
}
// Validate status
$status = $data['enabled'] ?? false;
$recipientUserIds = [$user->getId()];
if (! empty($data['share_users'])) {
$sharedUserIds = $this->validateAndGetUserIdsByTeam(
$user->team,
(array) $data['share_users']
);
$recipientUserIds = array_merge($recipientUserIds, $sharedUserIds);
}
$sharedGroupIds = [];
if (! empty($data['share_teams'])) {
$sharedGroupIds = $this->validateAndGetGroupIds($user->team, (array) $data['share_teams']);
}
$recipientUserIds = array_values(array_unique($recipientUserIds));
return [
'team_id' => $user->getTeamId(),
'type' => self::TYPE_ASK_JIMINNY,
'status' => (bool) $status,
'frequency' => $frequency,
'custom_name' => $name,
'activity_search_id' => $savedSearch->getId(),
'ask_anything_prompt_id' => $prompt->getId(),
'expires_at' => $expiresAtDate->toDateString(),
'media_types' => [self::MEDIA_TYPE_PDF],
'call_types' => [],
'recipients' => ['users' => $recipientUserIds],
'groups' => $sharedGroupIds,
];
}
public static function getAskJiminnyFrequencies(): array
{
return array_map(static function ($frequency) {
return $frequency['id'];
}, self::ASK_JIMINNY_FREQUENCIES);
}
public function getAskJiminnyReportFilters(User $user): array
{
$savedSearches = $this->activitySearchRepository->findByUserOrderedByName($user)
->map(fn (Search $search) => [
'id' => $search->getUuid(),
'name' => $search->getName(),
])
->values()->all();
$prompts = collect(
$this->askAnythingPromptService->get($user, AskAnythingPromptTarget::on_demand)
)->map(fn (AskAnythingPromptDto $prompt) => [
'id' => $prompt->id,
'name' => $prompt->title,
])->values()->all();
return [
[
'id' => 'prompt',
'label' => 'Prompt',
'options' => $prompts,
],
[
'id' => 'saved_search',
'label' => 'Saved Search',
'options' => $savedSearches,
],
];
}
public function getAskJiminnyReportFormData(User $user, ?AutomatedReport $report = null): array
{
$team = $user->getTeam();
$userTimezone = $user->getTimezone();
$savedSearches = $this->activitySearchRepository->findByUserOrderedByName($user)
->map(fn (Search $search) => [
'id' => $search->getUuid(),
'name' => $search->getName(),
])
->values()->all();
$prompts = collect(
$this->askAnythingPromptService->get($user, AskAnythingPromptTarget::on_demand)
)->map(fn (AskAnythingPromptDto $prompt) => [
'id' => $prompt->id,
'name' => $prompt->title,
])->values()->all();
$teamGroups = $this->groupRepository->getAllByTeam($team)->map(fn ($group) => [
'id' => $group->getUuid(),
'name' => $group->getName(),
])->values()->all();
$shareUsers = $this->recipientsService->getRecipientsFieldData(team: $team)['options'] ?? [];
$sharedTeamsValue = [];
$sharedUsersValue = [];
if ($report) {
$sharedTeamsValue = $this->transformGroups($team, $report->getGroups());
$recipientUserIds = $report->getRecipients()['users'] ?? [];
$creatorId = $report->getAttribute('created_by');
$sharedUserIds = array_values(array_filter(
$recipientUserIds,
static fn ($id) => $id !== $creatorId
));
$sharedUsersValue = collect($sharedUserIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->map(fn (User $u) => [
'id' => $u->getUuid(),
'name' => $u->getName(),
])
->values()
->all();
}
return [
'fields' => [
[
'id' => 'enabled',
'inputType' => InputTypeEnum::TOGGLE,
'label' => '',
'value' => $report?->getStatus() ?? false,
],
[
'id' => 'report_name',
'inputType' => InputTypeEnum::TEXT,
'label' => 'Name',
'placeholder' => 'Enter name',
'required' => true,
'validation' => ['maxLength' => 50],
'value' => $report?->getCustomName() ?? '',
],
[
'id' => 'frequency',
'inputType' => InputTypeEnum::DROPDOWN,
'label' => 'Frequency',
'required' => true,
'placeholder' => 'Select',
'options' => self::ASK_JIMINNY_FREQUENCIES,
'value' => $report ? $this->transformFrequency($report->getFrequency()) : null,
],
[
'id' => 'expires_on',
'inputType' => InputTypeEnum::DATE,
'label' => 'Expires on',
'required' => true,
'placeholder' => 'Select',
'validation' => [
'minDate' => now($userTimezone)->toDateString(),
'maxDate' => now($userTimezone)->addYear()->toDateString(),
],
'value' => $report?->getExpiresAt()?->toDateString(),
],
[
'id' => 'share_teams',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'label' => 'Team',
...
|
66556
|
|
66558
|
1496
|
42
|
2026-04-21T14:47:00.568206+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-21/1776 /Users/lukas/.screenpipe/data/data/2026-04-21/1776782820568_m2.jpg...
|
PhpStorm
|
faVsco.js – AutomatedReportsService.php
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
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
AutomatedReportsRepositoryTest
Run 'AutomatedReportsRepositoryTest'
Debug 'AutomatedReportsRepositoryTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
1
103
3
34
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Kiosk\AutomatedReports;
use Carbon\CarbonImmutable;
use Carbon\CarbonInterface;
use Carbon\Exceptions\InvalidFormatException;
use DateTime;
use DateTimeInterface;
use DateTimeZone;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Jiminny\Component\ActivitySearch\FilterDefinition\InputTypeEnum;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Component\AskAnything\Dtos\AskAnythingPromptDto;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Repositories\UserRepository;
use Jiminny\Exceptions\ApplicationException;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\ModelNotFoundException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateReportJob;
use Jiminny\Models\Activity\Search;
use Jiminny\Models\AskAnything\AskAnythingPrompt;
use Jiminny\Models\AskAnything\AskAnythingPromptTarget;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AskAnythingRepository;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\StageRepository;
use Throwable;
class AutomatedReportsService
{
public const string TYPE_LOSS_ANALYSIS = 'loss_analysis';
public const string TYPE_ASK_JIMINNY = 'ask_jiminny';
/**
* Standard report types (used by kiosk for existing automated reports).
*/
// @TODO this will add filter, however if we need to control feature by FF we need conditional logic
public const array TYPES = [
['id' => 'exec_summary', 'name' => 'Exec Summary'],
['id' => 'coaching_profiles', 'name' => 'Coaching Profiles'],
['id' => 'product_feedback', 'name' => 'Product Feedback'],
['id' => self::TYPE_LOSS_ANALYSIS, 'name' => 'Loss Analysis'],
// ['id' => 'questions', 'name' => 'Questions'],
// ['id' => 'statistical_quant', 'name' => 'Statistical Quantitative'],
];
public const array ALL_TYPES = [
...self::TYPES,
['id' => self::TYPE_ASK_JIMINNY, 'name' => 'Ask Jiminny'],
];
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
/**
* Frequencies for standard (non-Ask Jiminny) reports.
*/
public const array FREQUENCIES = [
['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],
['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],
['id' => self::FREQUENCY_QUARTERLY, 'name' => 'Quarterly'],
['id' => self::FREQUENCY_ONE_OFF, 'name' => 'One-off'],
];
/**
* Frequencies for Ask Jiminny reports.
*/
public const array ASK_JIMINNY_FREQUENCIES = [
['id' => self::FREQUENCY_DAILY, 'name' => 'Daily'],
['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],
['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],
];
public const string MEDIA_TYPE_PDF = 'pdf';
public const string MEDIA_TYPE_PODCAST = 'podcast';
public const array MEDIA_TYPES = [self::MEDIA_TYPE_PDF, self::MEDIA_TYPE_PODCAST];
public const array MEDIA_TYPE_OBJECT_PDF = ['id' => self::MEDIA_TYPE_PDF, 'name' => 'PDF'];
public const array MEDIA_TYPE_OBJECT_PODCAST = ['id' => self::MEDIA_TYPE_PODCAST, 'name' => 'Podcast'];
public const array MEDIA_TYPE_OBJECTS = [self::MEDIA_TYPE_OBJECT_PDF, self::MEDIA_TYPE_OBJECT_PODCAST];
public const array CALL_TYPE_CONFERENCE = ['id' => 'conference', 'name' => 'Conference'];
public const array CALL_TYPE_DIALER = ['id' => 'dialer', 'name' => 'Dialer'];
public const int SENT_REPORT_AT_HOURS = 5;
public const string PDF_KEY = 'pdf';
public const string AUDIO_KEY = 'audio';
private const array ALL_FREQUENCIES = [
['id' => self::FREQUENCY_DAILY, 'name' => 'Daily'],
['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],
['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],
['id' => self::FREQUENCY_QUARTERLY, 'name' => 'Quarterly'],
['id' => self::FREQUENCY_ONE_OFF, 'name' => 'One-off'],
];
private const string S3_DIR = 'reports';
private const array FILE_EXTENSIONS_VARIANTS = ['html', 'MD', 'pdf'];
private const array FILE_PODCAST_EXTENSIONS_VARIANTS = ['json', 'mp3', 'ssml'];
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly GroupRepository $groupRepository,
private readonly UserRepository $userRepository,
private readonly StageRepository $stageRepository,
private readonly DealStagesService $dealStagesService,
private readonly RecipientsService $recipientsService,
private readonly AutomatedReportsRepository $automatedReportsRepository,
private readonly Webhook $webhookService,
private readonly BusDispatcher $dispatcher,
private readonly ActivityTypeService $activityTypeService,
private readonly PlaybookCategoryRepository $playbookCategoryRepository,
private readonly AskAnythingPromptService $askAnythingPromptService,
private readonly SearchRepository $activitySearchRepository,
private readonly AskAnythingRepository $askAnythingRepository,
) {
}
public static function getTypes(): array
{
$types = self::TYPES;
return array_map(static function ($type) {
return $type['id'];
}, $types);
}
public static function getCallTypes(): array
{
return array_map(static function ($callType) {
return $callType['id'];
}, [self::CALL_TYPE_CONFERENCE, self::CALL_TYPE_DIALER]);
}
public static function getFrequencies(): array
{
return array_map(static function ($frequency) {
return $frequency['id'];
}, self::FREQUENCIES);
}
// front-facing structure
public function getReportEnabledFieldData(bool $value = false): array
{
return [
'id' => 'report_enabled',
'label' => '',
'inputType' => InputTypeEnum::TOGGLE,
'value' => $value,
];
}
// Organizations = Teams
public function getOrganizationFieldData(?string $value = null, bool $shortVersion = false): array
{
$options = $this->getTeams();
if ($shortVersion) {
return [
'id' => 'organization',
'label' => 'Organization',
'options' => $options,
];
}
return [
'id' => 'organization',
'label' => 'Organization',
'inputType' => InputTypeEnum::DROPDOWN,
'required' => true,
'placeholder' => 'Select',
'options' => $options,
'value' => $value,
'dependencies' => [
'teams',
'deal_stage_at_call',
'current_deal_stage',
'recipients',
ActivityTypeService::PLAYBOOK_CATEGORIES_KEY,
],
'dependsOn' => [],
];
}
// Teams = Groups
public function getTeamFieldData(array $options = [], array $value = [], bool $shortVersion = false): array
{
if ($shortVersion) {
return [
'id' => 'teams',
'label' => 'Team',
'options' => $options,
];
}
return [
'id' => 'teams',
'label' => 'Team',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'required' => false,
'placeholder' => 'Select',
'options' => $options,
'value' => $value, // value should be an array of objects {id, name}
'dependencies' => [ActivityTypeService::PLAYBOOK_CATEGORIES_KEY],
'dependsOn' => [],
];
}
public function getReportTypeFieldData(?string $value = null, bool $shortVersion = false, ?Team $team = null): array
{
$types = [];
if ($team instanceof Team) {
if ($team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {
$types = self::TYPES;
}
if ($team->hasFeature(FeatureEnum::ASK_JIMINNY_REPORTS)) {
$types[] = ['id' => self::TYPE_ASK_JIMINNY, 'name' => 'Ask Jiminny'];
}
} else {
$types = self::TYPES;
}
if ($shortVersion) {
return [
'id' => 'report_type',
'label' => 'Report Type',
'options' => $types,
];
}
return [
'id' => 'report_type',
'label' => 'Report Type',
'inputType' => InputTypeEnum::DROPDOWN,
'required' => true,
'placeholder' => 'Select',
'options' => $types,
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getFrequencyFieldData(?string $value = null): array
{
return [
'id' => 'frequency',
'label' => 'Frequency',
'inputType' => InputTypeEnum::DROPDOWN,
'required' => true,
'placeholder' => 'Select',
'options' => self::FREQUENCIES,
'value' => $value,
'dependencies' => ['period'],
'dependsOn' => [],
];
}
public function getPeriodFieldData(?string $valueStartDate = null, ?string $valueEndDate = null): array
{
return [
'id' => 'period',
'label' => 'Select one-off period',
'inputType' => InputTypeEnum::DATE_RANGE,
'required' => true,
'placeholder' => 'Select',
'value' => ['startDate' => $valueStartDate, 'endDate' => $valueEndDate],
'queryParams' => [
'startDate' => 'start_date_period',
'endDate' => 'end_date_period',
],
'dependencies' => [],
'dependsOn' => ['frequency'],
];
}
public function getActivityTypesFieldData(?Team $team = null, array $value = [], array $teamsFilter = []): array
{
return $this->activityTypeService->getActivityTypeFieldData(team: $team, value: $value, groupIds: $teamsFilter);
}
public function getDealStageAtCallFieldData(?Team $team = null, array $value = []): array
{
return $this->dealStagesService->getDealStageAtCallFieldData(team: $team, value: $value);
}
public function getCurrentDealStageFieldData(?Team $team = null, array $value = []): array
{
return $this->dealStagesService->getCurrentDealStageFieldData(team: $team, value: $value);
}
public function getDealValueFieldData(?int $valueMin = null, ?int $valueMax = null): array
{
return [
'id' => 'deal_value',
'label' => 'Deal Value',
'inputType' => InputTypeEnum::INTEGER_RANGE,
'required' => false,
'value' => ['min' => $valueMin, 'max' => $valueMax],
'queryParams' => [
'min' => 'min_deal_value',
'max' => 'max_deal_value',
],
'dependencies' => [],
'dependsOn' => [],
];
}
public function getCallTypeFieldData(bool $conferenceOn = false, bool $dialerOn = false): array
{
$value = [];
$conferenceOn && $value[] = self::CALL_TYPE_CONFERENCE;
$dialerOn && $value[] = self::CALL_TYPE_DIALER;
return [
'id' => 'call_type',
'label' => 'Call Type',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'required' => true,
'options' => [
self::CALL_TYPE_CONFERENCE,
self::CALL_TYPE_DIALER,
],
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getMediaTypeFieldData(?AutomatedReport $report = null): array
{
$value = [];
if ($report) {
$value = $this->transformMediaTypes($report);
}
return [
'id' => 'media_types',
'label' => 'Export as',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'required' => true,
'options' => self::MEDIA_TYPE_OBJECTS,
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getCallDurationFieldData(?int $valueMin = null, ?int $valueMax = null): array
{
return [
'id' => 'call_duration',
'label' => 'Call Duration',
'inputType' => InputTypeEnum::INTEGER_RANGE,
'required' => false,
'value' => ['min' => $valueMin, 'max' => $valueMax],
'queryParams' => [
'min' => 'min_call_duration',
'max' => 'max_call_duration',
],
'dependencies' => [],
'dependsOn' => [],
];
}
public function getRecipientsFieldData(?Team $team = null, array $value = []): array
{
return $this->recipientsService->getRecipientsFieldData(team: $team, value: $value);
}
public function getJiminnyRecipientsFieldData(array $value = []): array
{
return $this->recipientsService->getJiminnyRecipientsFieldData($value);
}
public function getAdditionalPromptInputFieldData(?string $value = null): array
{
return [
'id' => 'additional_prompt_input',
'label' => 'Special requirements',
'inputType' => InputTypeEnum::TEXTAREA,
'required' => false,
'placeholder' => 'What should be the focus of the report?',
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getCustomReportNameFieldData(?string $value = null): array
{
return [
'id' => 'custom_name',
'label' => 'Custom report name',
'inputType' => InputTypeEnum::TEXT,
'required' => false,
'placeholder' => 'Enter custom name',
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
// data providers
public function getTeams(): array
{
$teams = $this->teamRepository->getTeamsForKiosk(status: Team::STATUS_ACTIVE);
$teamData = [];
foreach ($teams as $team) {
if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {
continue;
}
$teamData[] = $this->transformTeam($team);
}
return $teamData;
}
public function getTeamGroups(string $teamUuid): array
{
$data = [];
$team = $this->getTeam($teamUuid);
if ($team !== null) {
$groups = $team->groups()->get();
foreach ($groups as $group) {
$data[] = [
'id' => $group->getUuid(),
'name' => $group->getName(),
];
}
}
return $data;
}
public function getTeamsGroupsOptions(array $filterTeamUuids = []): array
{
$data = [];
$teams = $this->getTeams();
foreach ($teams as $team) {
if (! empty($filterTeamUuids) && ! in_array($team['id'], $filterTeamUuids, true)) {
continue;
}
$data[] = [
'label' => $team['name'],
'groups' => $this->getTeamGroups($team['id']),
];
}
return $data;
}
public function getTeam(string $teamUuid): ?Team
{
return $this->teamRepository->idOrUuid($teamUuid);
}
public function getTeamById(int $teamId): ?Team
{
return $this->teamRepository->find($teamId);
}
public function getGroupsUuids(AutomatedReport $report): array
{
$uuids = [];
$reportGroups = $report->getGroups();
foreach ($reportGroups as $groupId) {
if ($group = $this->groupRepository->find($groupId)) {
$uuids[] = $group->getUuid();
}
}
return $uuids;
}
public function getPlaybookCategoriesUuids(AutomatedReport $report): array
{
$uuids = [];
$playbookCategories = $report->getPlaybookCategories();
foreach ($playbookCategories as $id) {
if ($category = $this->playbookCategoryRepository->find($id)) {
$uuids[] = $category->getUuid();
}
}
return $uuids;
}
public function getDealAtCallStagesUuids(AutomatedReport $report): array
{
$uuids = [];
$reportStages = $report->getDealAtCallStages();
foreach ($reportStages as $id) {
if ($stage = $this->stageRepository->find($id)) {
$uuids[] = $stage->getUuid();
}
}
return $uuids;
}
public function getCurrentDealStagesUuids(AutomatedReport $report): array
{
$uuids = [];
$reportStages = $report->getCurrentDealStages();
foreach ($reportStages as $id) {
if ($stage = $this->stageRepository->find($id)) {
$uuids[] = $stage->getUuid();
}
}
return $uuids;
}
public function getUsersUuids(AutomatedReport $report): array
{
return $this->extractUserUuids($report->getRecipients());
}
public function getJiminnyUsersUuids(AutomatedReport $report): array
{
return $this->extractUserUuids($report->getJiminnyRecipients());
}
/**
* @param array<string, mixed> $recipients
*/
private function extractUserUuids(array $recipients): array
{
$userIds = $recipients['users'] ?? [];
return collect($userIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->map(fn (UserContract $user) => $user->getUuid())
->values()
->all();
}
// get mail data
public function getRecipientUsers(AutomatedReport $report): array
{
return $this->buildRecipientUsers($report->getRecipients());
}
/**
* @return array<UserContract>
*/
public function getRecipientUserObjects(AutomatedReport $report): array
{
$userIds = $report->getRecipients()['users'] ?? [];
return collect($userIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->values()
->all();
}
private function getJiminnyRecipientUsers(AutomatedReport $report): array
{
return $this->buildRecipientUsers($report->getJiminnyRecipients());
}
/**
* @param array<string, mixed> $recipients
*/
private function buildRecipientUsers(array $recipients): array
{
$userIds = $recipients['users'] ?? [];
return collect($userIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->map(fn (UserContract $user) => [
'email' => $user->getEmailAddress(),
'name' => $user->getName(),
'timezone' => $user->getTimezone()->getName(),
])
->values()
->all();
}
public function getValidRecipientUsers(AutomatedReport $report, bool $includeJiminny = false): array
{
if ($report->isAskJiminnyReport()) {
$recipients = $this->resolveAskJiminnyRecipients($report);
} else {
$recipients = $this->getRecipientUsers($report);
if ($includeJiminny) {
$recipients = array_merge($recipients, $this->getJiminnyRecipientUsers($report));
}
}
$emails = [];
return array_values(array_filter(
$recipients,
static function ($recipient) use (&$emails) {
if (empty($recipient['email']) || in_array($recipient['email'], $emails, true)) {
return false;
}
$emails[] = $recipient['email'];
return true;
}
));
}
private function resolveAskJiminnyRecipients(AutomatedReport $report): array
{
$recipients = [];
$creator = $report->getCreator();
if ($creator !== null) {
$recipients[] = [
'email' => $creator->getEmailAddress(),
'name' => $creator->getName(),
'timezone' => $creator->getTimezone()->getName(),
];
}
return array_merge(
$recipients,
$this->buildRecipientUsers($report->getRecipients()),
$this->getGroupRecipientUsers($report),
);
}
private function getGroupRecipientUsers(AutomatedReport $report): array
{
$users = [];
foreach ($report->getGroups() as $groupId) {
$group = $this->groupRepository->find($groupId);
if ($group === null) {
continue;
}
foreach ($group->getMembers() as $member) {
$users[] = [
'email' => $member->getEmailAddress(),
'name' => $member->getName(),
'timezone' => $member->getTimezone()->getName(),
];
}
}
return $users;
}
public function getReportTypeName(AutomatedReportResult $report): string
{
$type = $report->getReport()->getType();
$getType = $this->transformReportType($type);
return $getType['name'];
}
public function getReportPeriodName(AutomatedReportResult $report): string
{
$from = $report->getFromDate();
$to = $report->getToDate();
$frequency = $report->getReport()->getFrequency();
if ($from === null || $to === null) {
if (! $report->getReport()->isAskJiminnyReport()) {
$invalidPeriod = $from === null ? 'from' : 'to';
throw new ApplicationException('Report period is invalid: ' . $invalidPeriod);
}
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
}
return $this->formatReportPeriodName($frequency, $from, $to);
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function getReportTeamsName(AutomatedReportResult $report): string
{
$groups = $report->getGroups();
if (empty($groups)) {
return 'All';
}
// Get group names from repository
$groupNames = [];
foreach ($groups as $groupId) {
$group = $this->groupRepository->find($groupId);
if ($group) {
$groupNames[] = $group->getName();
}
}
if (count($groupNames) === 1) {
// Single team format
$teamsName = $groupNames[0];
} else {
// Multiple teams format
$teamsName = implode(', ', $groupNames);
}
return $teamsName;
}
public function getReportFileName(AutomatedReportResult $report): string
{
$customName = $report->getReport()->getCustomName();
$periodName = $this->getReportPeriodName($report);
$filenameSuffix = $this->getFilenameSuffix($report);
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
return $this->sanitizeFileName("{$customName} - {$periodName}");
}
$baseName = $this->getReportTypeName($report);
if ($filenameSuffix) {
$baseName .= " {$filenameSuffix}";
}
return $this->sanitizeFileName("{$baseName} - {$periodName} - {$this->getReportTeamsName($report)}");
}
public function getReportFileNameWithExtension(AutomatedReportResult $result): string
{
$extension = $this->getMediaTypeMetadata($result)['extension'];
return $this->getReportFileName($result) . '.' . $extension;
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
public function isUserRecipientOfReport(User $user, AutomatedReport $report): bool
{
$recipients = array_map('intval', $report->getRecipients()['users'] ?? []);
return in_array($user->getId(), $recipients);
}
public function transformReportResults(Collection $automatedReportResults): array
{
$data = [];
foreach ($automatedReportResults as $automatedReportResult) {
/** @var AutomatedReportResult $automatedReportResult */
$report = $automatedReportResult->getReport();
$createdBy = $report->getCreator();
$creator = [
'id' => $createdBy?->getUuid(),
'name' => $createdBy?->getName(),
'email' => $createdBy?->getEmailAddress(),
'photoUrl' => $createdBy?->getPhotoUrl(),
];
$recipients = $this->buildRecipients($report);
$data[] = [
'id' => $automatedReportResult->getUuid(),
'name' => $automatedReportResult->getName(),
'frequency' => $this->transformFrequency($report->getFrequency()),
'recipients' => [
...array_values($this->transformGroups(team: $report->getTeam(), groupsIds: $report->getGroups())),
...array_values($this->transformRecipients($report->getRecipients())),
],
'report_type' => $this->transformReportType($report->getType()),
'media_type' => $automatedReportResult->getMediaType(),
'downloadUrl' => $this->generateReportResultDownloadUrl($automatedReportResult),
'viewUrl' => $this->generateReportResultViewUrl($automatedReportResult),
'generated_at' => $automatedReportResult->getGeneratedAt()?->toIso8601String(),
'creator' => $creator,
];
}
return $data;
}
public function hasCallTypeConference(AutomatedReport $report): bool
{
return in_array(self::CALL_TYPE_CONFERENCE['id'], $report->getCallTypes(), true);
}
public function hasCallTypeDialer(AutomatedReport $report): bool
{
return in_array(self::CALL_TYPE_DIALER['id'], $report->getCallTypes(), true);
}
// transformers
private function transformTeam(Team $team): array
{
if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {
return [];
}
return [
'id' => $team->getUuid(),
'name' => $team->getName(),
];
}
private function transformReportFullView(AutomatedReport $report): array
{
$base = $this->transformReportBase($report);
return $report->getType() === self::TYPE_ASK_JIMINNY
? $base + $this->transformAskJiminnyFields($report)
: $base + $this->transformStandardReportFields($report);
}
private function transformReportBase(AutomatedReport $report): array
{
return [
'id' => $report->getUuid(),
'organization' => $this->transformOrganization(team: $report->getTeam()),
'report_type' => $this->transformReportType($report->getType()),
'frequency' => $this->transformFrequency($report->getFrequency()),
];
}
private function transformStandardReportFields(AutomatedReport $report): array
{
$team = $report->getTeam();
return [
'report_enabled' => $report->getStatus(),
'start_date_period' => $report->getFrom()?->format('Y-m-d H:i:s'),
'end_date_period' => $report->getTo()?->format('Y-m-d H:i:s'),
'deal_value_min' => $report->getDealValueMin(),
'deal_value_max' => $report->getDealValueMax(),
'call_types' => $this->transformCallType($report->getCallTypes()),
'media_types' => $this->transformMediaTypes($report),
'call_duration_min' => $this->transformDurationToMinutes($report->getCallDurationMin()),
'call_duration_max' => $this->transformDurationToMinutes($report->getCallDurationMax()),
'teams' => $this->transformGroups(team: $team, groupsIds: $report->getGroups()),
'deal_at_call_stages' => $this->transformStages(team: $team, stagesIds: $report->getDealAtCallStages()),
'current_deal_stages' => $this->transformStages(team: $team, stagesIds: $report->getCurrentDealStages()),
'recipients' => $this->transformRecipients($report->getRecipients()),
'created_by' => $this->transformCreator($report->getCreator()),
'additional_prompt_input' => $report->getAdditionalPromptInput(),
'custom_name' => $report->getCustomName(),
'created_at' => $report->getCreatedAt()->format('Y-m-d H:i:s'),
'updated_at' => $report->getUpdatedAt()->format('Y-m-d H:i:s'),
'deleted_at' => $report->getDeletedAt()?->format('Y-m-d H:i:s'),
];
}
private function transformAskJiminnyFields(AutomatedReport $report): array
{
$team = $report->getTeam();
$creatorId = $report->getAttribute('created_by');
$explicitUserIds = array_values(array_filter(
$report->getRecipients()['users'] ?? [],
static fn ($id) => $id !== $creatorId
));
return [
'report_name' => $report->getCustomName(),
'enabled' => $report->getStatus(),
'share_teams' => $this->transformGroups(team: $team, groupsIds: $report->getGroups()),
'share_users' => $this->transformRecipients(['users' => $explicitUserIds]),
'saved_search' => $this->transformSafeSearch($report->getSavedSearch()),
'ask_jiminny_prompt' => $this->transformAskJiminnyPrompt($report->getAskAnythingPrompt()),
'expires_on' => $report->getExpiresAt()?->format('Y-m-d'),
];
}
private function transformOrganization(?Team $team): array
{
return [
'id' => $team?->getUuid(),
'name' => $team?->getName(),
];
}
private function transformReportType(string $type): array
{
foreach (self::ALL_TYPES as $typeItem) {
if ($typeItem['id'] === $type) {
return $typeItem;
}
}
return [];
}
private function transformCallType(array $types): array
{
$result = [];
$callTypes = [self::CALL_TYPE_CONFERENCE, self::CALL_TYPE_DIALER];
foreach ($types as $type) {
foreach ($callTypes as $callTypeItem) {
if ($callTypeItem['id'] === $type) {
$result[] = $callTypeItem;
break;
}
}
}
return $result;
}
private function transformMediaTypes(AutomatedReport $report): array
{
$values = [];
foreach ($report->getMediaTypes() as $mediaType) {
if (! in_array($mediaType, self::MEDIA_TYPES, true)) {
continue;
}
$values[] = match ($mediaType) {
self::MEDIA_TYPE_PDF => self::MEDIA_TYPE_OBJECT_PDF,
self::MEDIA_TYPE_PODCAST => self::MEDIA_TYPE_OBJECT_PODCAST,
};
}
return $values;
}
private function transformFrequency(string $frequency): array
{
foreach (self::ALL_FREQUENCIES as $frequencyItem) {
if ($frequencyItem['id'] === $frequency) {
return $frequencyItem;
}
}
return [];
}
public function transformDurationToMinutes(?int $duration): ?int
{
if (! $duration) {
return null;
}
return (int) ($duration / 60);
}
private function transformGroups(?Team $team, array $groupsIds): array
{
if (empty($groupsIds) || ! $team) {
return [];
}
$data = [];
foreach ($groupsIds as $groupId) {
$group = $team->groups()->where('id', $groupId)->first();
if ($group) {
$data[] = [
'id' => $group->getUuid(),
'name' => $group->getName(),
'photoUrl' => $group->getPhotoUrl(),
];
}
}
return $data;
}
private function transformStages(?Team $team, array $stagesIds): array
{
if (empty($stagesIds) || ! $team) {
return [];
}
$data = [];
foreach ($stagesIds as $stageId) {
$stage = $team->stages()->where('id', $stageId)->first();
if ($stage) {
$data[] = [
'id' => $stage->getUuid(),
'name' => $stage->getName(),
];
}
}
return $data;
}
private function transformRecipients(array $recipients): array
{
$users = [];
foreach ($recipients['users'] ?? [] as $userId) {
$users[] = $this->transformUser($userId);
}
return $users;
}
private function transformCreator(?User $user): ?array
{
if ($user === null) {
return null;
}
return $this->transformUser($user->getId());
}
private function transformAskJiminnyPrompt(?AskAnythingPrompt $prompt): ?array
{
if ($prompt === null) {
return null;
}
return [
'id' => $prompt->getUuid(),
'name' => $prompt->getTitle(),
];
}
private function transformSafeSearch(?Search $search): ?array
{
if ($search === null) {
return null;
}
return [
'id' => $search->getUuid(),
'name' => $search->getName(),
];
}
private function transformUser(int $userId): array
{
/* @var ?User $user */
$user = $this->userRepository->find($userId);
return [
'id' => $user?->getUuid(),
'name' => $user?->getName(),
'email' => $user?->getEmailAddress(),
'photoUrl' => $user?->getPhotoUrl(),
];
}
public function create(array $data): array
{
$validatedData = $this->validateAndTransformData($data);
$validatedData['created_by'] = auth()->id();
$automatedReport = $this->automatedReportsRepository->create($validatedData);
$this->generateOneOffReport($automatedReport);
return $this->transformReportFullView($automatedReport);
}
public function update(string $uuid, array $data): array
{
$validatedData = $this->validateAndTransformData($data);
$report = $this->automatedReportsRepository->findByUuid($uuid);
if (! $report) {
throw new InvalidArgumentException('Report not found');
}
$oldCustomName = $report->getCustomName();
$automatedReport = $this->automatedReportsRepository->update($report, $validatedData);
if ($oldCustomName !== $automatedReport->getCustomName()) {
$this->updateResultNames($automatedReport);
}
$this->generateOneOffReport($automatedReport);
return $this->transformReportFullView($automatedReport);
}
/**
* Create an Ask Jiminny report.
*/
public function createAskJiminnyReport(array $data, User $creator): array
{
$validatedData = $this->validateAskJiminnyReportData($data, $creator);
$validatedData['created_by'] = $creator->getId();
$automatedReport = $this->automatedReportsRepository->create($validatedData);
return $this->transformReportFullView($automatedReport);
}
/**
* Update an Ask Jiminny report.
*/
public function updateAskJiminnyReport(AutomatedReport $report, array $data, User $user): array
{
if (! $report->isAskJiminnyReport()) {
throw new InvalidArgumentException('Report is not an Ask Jiminny report');
}
$validatedData = $this->validateAskJiminnyReportData($data, $user);
$oldCustomName = $report->getCustomName();
$automatedReport = $this->automatedReportsRepository->update($report, $validatedData);
if ($oldCustomName !== $automatedReport->getCustomName()) {
$this->updateResultNames($automatedReport);
}
return $this->transformReportFullView($automatedReport);
}
public function updateAskJiminnyReportStatus(AutomatedReport $report, bool $status): array
{
$this->automatedReportsRepository->update($report, ['status' => $status]);
return $this->transformReportFullView($report->fresh());
}
/**
* Validate and transform data for Ask Jiminny reports.
*/
private function validateAskJiminnyReportData(array $data, User $user): array
{
// Validate name
$name = trim($data['report_name'] ?? '');
if (empty($name)) {
throw new InvalidArgumentException('Report name is required');
}
if (mb_strlen($name) > 50) {
throw new InvalidArgumentException('Report name must be 50 characters or less');
}
// Validate frequency (only daily, weekly, monthly for Ask Jiminny)
$frequency = $data['frequency'] ?? null;
$askJiminnyFrequencies = [self::FREQUENCY_DAILY, self::FREQUENCY_WEEKLY, self::FREQUENCY_MONTHLY];
if (! in_array($frequency, $askJiminnyFrequencies, true)) {
throw new InvalidArgumentException('Frequency must be daily, weekly, or monthly');
}
// Validate expiration date
$expiresAt = $data['expires_on'] ?? null;
if (empty($expiresAt)) {
throw new InvalidArgumentException('Expiration date is required');
}
try {
$expiresAtDate = Carbon::parse($expiresAt);
} catch (InvalidFormatException $e) {
throw new InvalidArgumentException('Expiration date format is invalid');
}
$maxExpiration = Carbon::now()->addYear()->endOfDay();
if ($expiresAtDate->gt($maxExpiration)) {
throw new InvalidArgumentException('Expiration date cannot be more than 1 year from now');
}
if ($expiresAtDate->isPast()) {
throw new InvalidArgumentException('Expiration date cannot be in the past');
}
// Validate saved search
$activitySearchId = $data['saved_search'] ?? null;
if (empty($activitySearchId)) {
throw new InvalidArgumentException('Saved search is required');
}
$savedSearch = $this->activitySearchRepository->findByUuidAndUser($activitySearchId, $user);
if (! $savedSearch) {
throw new InvalidArgumentException('Saved search not found or does not belong to you');
}
// Validate saved prompt
$askAnythingPromptId = $data['ask_jiminny_prompt'] ?? null;
if (empty($askAnythingPromptId)) {
throw new InvalidArgumentException('Ask Jiminny prompt is required');
}
$prompt = $this->askAnythingRepository->getPromptByUuid($askAnythingPromptId);
if (! $prompt) {
throw new InvalidArgumentException('Ask Jiminny prompt not found');
}
// Validate status
$status = $data['enabled'] ?? false;
$recipientUserIds = [$user->getId()];
if (! empty($data['share_users'])) {
$sharedUserIds = $this->validateAndGetUserIdsByTeam(
$user->team,
(array) $data['share_users']
);
$recipientUserIds = array_merge($recipientUserIds, $sharedUserIds);
}
$sharedGroupIds = [];
if (! empty($data['share_teams'])) {
$sharedGroupIds = $this->validateAndGetGroupIds($user->team, (array) $data['share_teams']);
}
$recipientUserIds = array_values(array_unique($recipientUserIds));
return [
'team_id' => $user->getTeamId(),
'type' => self::TYPE_ASK_JIMINNY,
'status' => (bool) $status,
'frequency' => $frequency,
'custom_name' => $name,
'activity_search_id' => $savedSearch->getId(),
'ask_anything_prompt_id' => $prompt->getId(),
'expires_at' => $expiresAtDate->toDateString(),
'media_types' => [self::MEDIA_TYPE_PDF],
'call_types' => [],
'recipients' => ['users' => $recipientUserIds],
'groups' => $sharedGroupIds,
];
}
public static function getAskJiminnyFrequencies(): array
{
return array_map(static function ($frequency) {
return $frequency['id'];
}, self::ASK_JIMINNY_FREQUENCIES);
}
public function getAskJiminnyReportFilters(User $user): array
{
$savedSearches = $this->activitySearchRepository->findByUserOrderedByName($user)
->map(fn (Search $search) => [
'id' => $search->getUuid(),
'name' => $search->getName(),
])
->values()->all();
$prompts = collect(
$this->askAnythingPromptService->get($user, AskAnythingPromptTarget::on_demand)
)->map(fn (AskAnythingPromptDto $prompt) => [
'id' => $prompt->id,
'name' => $prompt->title,
])->values()->all();
return [
[
'id' => 'prompt',
'label' => 'Prompt',
'options' => $prompts,
],
[
'id' => 'saved_search',
'label' => 'Saved Search',
'options' => $savedSearches,
],
];
}
public function getAskJiminnyReportFormData(User $user, ?AutomatedReport $report = null): array
{
$team = $user->getTeam();
$userTimezone = $user->getTimezone();
$savedSearches = $this->activitySearchRepository->findByUserOrderedByName($user)
->map(fn (Search $search) => [
'id' => $search->getUuid(),
'name' => $search->getName(),
])
->values()->all();
$prompts = collect(
$this->askAnythingPromptService->get($user, AskAnythingPromptTarget::on_demand)
)->map(fn (AskAnythingPromptDto $prompt) => [
'id' => $prompt->id,
'name' => $prompt->title,
])->values()->all();
$teamGroups = $this->groupRepository->getAllByTeam($team)->map(fn ($group) => [
'id' => $group->getUuid(),
'name' => $group->getName(),
])->values()->all();
$shareUsers = $this->recipientsService->getRecipientsFieldData(team: $team)['options'] ?? [];
$sharedTeamsValue = [];
$sharedUsersValue = [];
if ($report) {
$sharedTeamsValue = $this->transformGroups($team, $report->getGroups());
$recipientUserIds = $report->getRecipients()['users'] ?? [];
$creatorId = $report->getAttribute('created_by');
$sharedUserIds = array_values(array_filter(
$recipientUserIds,
static fn ($id) => $id !== $creatorId
));
$sharedUsersValue = collect($sharedUserIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->map(fn (User $u) => [
'id' => $u->getUuid(),
'name' => $u->getName(),
])
->values()
->all();
}
return [
'fields' => [
[
'id' => 'enabled',
'inputType' => InputTypeEnum::TOGGLE,
'label' => '',
'value' => $report?->getStatus() ?? false,
],
[
'id' => 'report_name',
'inputType' => InputTypeEnum::TEXT,
'label' => 'Name',
'placeholder' => 'Enter name',
'required' => true,
'validation' => ['maxLength' => 50],
'value' => $report?->getCustomName() ?? '',
],
[
'id' => 'frequency',
'inputType' => InputTypeEnum::DROPDOWN,
'label' => 'Frequency',
'required' => true,
'placeholder' => 'Select',
'options' => self::ASK_JIMINNY_FREQUENCIES,
'value' => $report ? $this->transformFrequency($report->getFrequency()) : null,
],
[
'id' => 'expires_on',
'inputType' => InputTypeEnum::DATE,
'label' => 'Expires on',
'required' => true,
'placeholder' => 'Select',
'validation' => [
'minDate' => now($userTimezone)->toDateString(),
'maxDate' => now($userTimezone)->addYear()->toDateString(),
],
'value' => $report?->getExpiresAt()?->toDateString(),
],
[
'id' => 'share_teams',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'label' => 'Team',
...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#11894 on JY-18909-automated-reports-ask-jiminny, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.12134308,"height":0.025538707},"help_text":"Pull request #11894 exists for current branch JY-18909-automated-reports-ask-jiminny","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8161569,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AutomatedReportsRepositoryTest","depth":6,"bounds":{"left":0.83144945,"top":0.019952115,"width":0.084109046,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AutomatedReportsRepositoryTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AutomatedReportsRepositoryTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.63297874,"top":0.15003991,"width":0.00731383,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"103","depth":4,"bounds":{"left":0.64228725,"top":0.15003991,"width":0.011968086,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"3","depth":4,"bounds":{"left":0.65625,"top":0.15003991,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"34","depth":4,"bounds":{"left":0.6662234,"top":0.15003991,"width":0.010305851,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.6781915,"top":0.14844373,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.68550533,"top":0.14844373,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Kiosk\\AutomatedReports;\n\nuse Carbon\\CarbonImmutable;\nuse Carbon\\CarbonInterface;\nuse Carbon\\Exceptions\\InvalidFormatException;\nuse DateTime;\nuse DateTimeInterface;\nuse DateTimeZone;\nuse Illuminate\\Contracts\\Bus\\Dispatcher as BusDispatcher;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Support\\Carbon;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\Log;\nuse Illuminate\\Support\\Facades\\Storage;\nuse Jiminny\\Component\\ActivitySearch\\FilterDefinition\\InputTypeEnum;\nuse Jiminny\\Component\\AskAnything\\AskAnythingPromptService;\nuse Jiminny\\Component\\AskAnything\\Dtos\\AskAnythingPromptDto;\nuse Jiminny\\Component\\UrlGenerator\\Webhook;\nuse Jiminny\\Contracts\\Repositories\\PlaybookCategoryRepository;\nuse Jiminny\\Contracts\\Repositories\\TeamRepository;\nuse Jiminny\\Contracts\\Repositories\\UserRepository;\nuse Jiminny\\Exceptions\\ApplicationException;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Exceptions\\ModelNotFoundException;\nuse Jiminny\\Jobs\\AutomatedReports\\RequestGenerateReportJob;\nuse Jiminny\\Models\\Activity\\Search;\nuse Jiminny\\Models\\AskAnything\\AskAnythingPrompt;\nuse Jiminny\\Models\\AskAnything\\AskAnythingPromptTarget;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AskAnythingRepository;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\GroupRepository;\nuse Jiminny\\Repositories\\SearchRepository;\nuse Jiminny\\Repositories\\StageRepository;\nuse Throwable;\n\nclass AutomatedReportsService\n{\n public const string TYPE_LOSS_ANALYSIS = 'loss_analysis';\n public const string TYPE_ASK_JIMINNY = 'ask_jiminny';\n\n /**\n * Standard report types (used by kiosk for existing automated reports).\n */\n // @TODO this will add filter, however if we need to control feature by FF we need conditional logic\n public const array TYPES = [\n ['id' => 'exec_summary', 'name' => 'Exec Summary'],\n ['id' => 'coaching_profiles', 'name' => 'Coaching Profiles'],\n ['id' => 'product_feedback', 'name' => 'Product Feedback'],\n ['id' => self::TYPE_LOSS_ANALYSIS, 'name' => 'Loss Analysis'],\n// ['id' => 'questions', 'name' => 'Questions'],\n// ['id' => 'statistical_quant', 'name' => 'Statistical Quantitative'],\n ];\n\n public const array ALL_TYPES = [\n ...self::TYPES,\n ['id' => self::TYPE_ASK_JIMINNY, 'name' => 'Ask Jiminny'],\n ];\n\n public const string FREQUENCY_DAILY = 'daily';\n public const string FREQUENCY_WEEKLY = 'weekly';\n public const string FREQUENCY_MONTHLY = 'monthly';\n public const string FREQUENCY_QUARTERLY = 'quarterly';\n public const string FREQUENCY_ONE_OFF = 'one_off';\n\n /**\n * Frequencies for standard (non-Ask Jiminny) reports.\n */\n public const array FREQUENCIES = [\n ['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],\n ['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],\n ['id' => self::FREQUENCY_QUARTERLY, 'name' => 'Quarterly'],\n ['id' => self::FREQUENCY_ONE_OFF, 'name' => 'One-off'],\n ];\n\n /**\n * Frequencies for Ask Jiminny reports.\n */\n public const array ASK_JIMINNY_FREQUENCIES = [\n ['id' => self::FREQUENCY_DAILY, 'name' => 'Daily'],\n ['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],\n ['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],\n ];\n\n public const string MEDIA_TYPE_PDF = 'pdf';\n public const string MEDIA_TYPE_PODCAST = 'podcast';\n public const array MEDIA_TYPES = [self::MEDIA_TYPE_PDF, self::MEDIA_TYPE_PODCAST];\n public const array MEDIA_TYPE_OBJECT_PDF = ['id' => self::MEDIA_TYPE_PDF, 'name' => 'PDF'];\n public const array MEDIA_TYPE_OBJECT_PODCAST = ['id' => self::MEDIA_TYPE_PODCAST, 'name' => 'Podcast'];\n public const array MEDIA_TYPE_OBJECTS = [self::MEDIA_TYPE_OBJECT_PDF, self::MEDIA_TYPE_OBJECT_PODCAST];\n\n public const array CALL_TYPE_CONFERENCE = ['id' => 'conference', 'name' => 'Conference'];\n public const array CALL_TYPE_DIALER = ['id' => 'dialer', 'name' => 'Dialer'];\n public const int SENT_REPORT_AT_HOURS = 5;\n public const string PDF_KEY = 'pdf';\n public const string AUDIO_KEY = 'audio';\n\n private const array ALL_FREQUENCIES = [\n ['id' => self::FREQUENCY_DAILY, 'name' => 'Daily'],\n ['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],\n ['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],\n ['id' => self::FREQUENCY_QUARTERLY, 'name' => 'Quarterly'],\n ['id' => self::FREQUENCY_ONE_OFF, 'name' => 'One-off'],\n ];\n private const string S3_DIR = 'reports';\n private const array FILE_EXTENSIONS_VARIANTS = ['html', 'MD', 'pdf'];\n private const array FILE_PODCAST_EXTENSIONS_VARIANTS = ['json', 'mp3', 'ssml'];\n\n public function __construct(\n private readonly TeamRepository $teamRepository,\n private readonly GroupRepository $groupRepository,\n private readonly UserRepository $userRepository,\n private readonly StageRepository $stageRepository,\n private readonly DealStagesService $dealStagesService,\n private readonly RecipientsService $recipientsService,\n private readonly AutomatedReportsRepository $automatedReportsRepository,\n private readonly Webhook $webhookService,\n private readonly BusDispatcher $dispatcher,\n private readonly ActivityTypeService $activityTypeService,\n private readonly PlaybookCategoryRepository $playbookCategoryRepository,\n private readonly AskAnythingPromptService $askAnythingPromptService,\n private readonly SearchRepository $activitySearchRepository,\n private readonly AskAnythingRepository $askAnythingRepository,\n ) {\n }\n\n public static function getTypes(): array\n {\n $types = self::TYPES;\n\n return array_map(static function ($type) {\n return $type['id'];\n }, $types);\n }\n\n public static function getCallTypes(): array\n {\n return array_map(static function ($callType) {\n return $callType['id'];\n }, [self::CALL_TYPE_CONFERENCE, self::CALL_TYPE_DIALER]);\n }\n\n public static function getFrequencies(): array\n {\n return array_map(static function ($frequency) {\n return $frequency['id'];\n }, self::FREQUENCIES);\n }\n\n // front-facing structure\n public function getReportEnabledFieldData(bool $value = false): array\n {\n return [\n 'id' => 'report_enabled',\n 'label' => '',\n 'inputType' => InputTypeEnum::TOGGLE,\n 'value' => $value,\n ];\n }\n\n // Organizations = Teams\n public function getOrganizationFieldData(?string $value = null, bool $shortVersion = false): array\n {\n $options = $this->getTeams();\n\n if ($shortVersion) {\n return [\n 'id' => 'organization',\n 'label' => 'Organization',\n 'options' => $options,\n ];\n }\n\n return [\n 'id' => 'organization',\n 'label' => 'Organization',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => $options,\n 'value' => $value,\n 'dependencies' => [\n 'teams',\n 'deal_stage_at_call',\n 'current_deal_stage',\n 'recipients',\n ActivityTypeService::PLAYBOOK_CATEGORIES_KEY,\n ],\n 'dependsOn' => [],\n ];\n }\n\n // Teams = Groups\n public function getTeamFieldData(array $options = [], array $value = [], bool $shortVersion = false): array\n {\n if ($shortVersion) {\n return [\n 'id' => 'teams',\n 'label' => 'Team',\n 'options' => $options,\n ];\n }\n\n return [\n 'id' => 'teams',\n 'label' => 'Team',\n 'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,\n 'required' => false,\n 'placeholder' => 'Select',\n 'options' => $options,\n 'value' => $value, // value should be an array of objects {id, name}\n 'dependencies' => [ActivityTypeService::PLAYBOOK_CATEGORIES_KEY],\n 'dependsOn' => [],\n ];\n }\n\n public function getReportTypeFieldData(?string $value = null, bool $shortVersion = false, ?Team $team = null): array\n {\n $types = [];\n if ($team instanceof Team) {\n if ($team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {\n $types = self::TYPES;\n }\n if ($team->hasFeature(FeatureEnum::ASK_JIMINNY_REPORTS)) {\n $types[] = ['id' => self::TYPE_ASK_JIMINNY, 'name' => 'Ask Jiminny'];\n }\n } else {\n $types = self::TYPES;\n }\n\n if ($shortVersion) {\n return [\n 'id' => 'report_type',\n 'label' => 'Report Type',\n 'options' => $types,\n ];\n }\n\n return [\n 'id' => 'report_type',\n 'label' => 'Report Type',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => $types,\n 'value' => $value,\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getFrequencyFieldData(?string $value = null): array\n {\n return [\n 'id' => 'frequency',\n 'label' => 'Frequency',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => self::FREQUENCIES,\n 'value' => $value,\n 'dependencies' => ['period'],\n 'dependsOn' => [],\n ];\n }\n\n public function getPeriodFieldData(?string $valueStartDate = null, ?string $valueEndDate = null): array\n {\n return [\n 'id' => 'period',\n 'label' => 'Select one-off period',\n 'inputType' => InputTypeEnum::DATE_RANGE,\n 'required' => true,\n 'placeholder' => 'Select',\n 'value' => ['startDate' => $valueStartDate, 'endDate' => $valueEndDate],\n 'queryParams' => [\n 'startDate' => 'start_date_period',\n 'endDate' => 'end_date_period',\n ],\n 'dependencies' => [],\n 'dependsOn' => ['frequency'],\n ];\n }\n\n public function getActivityTypesFieldData(?Team $team = null, array $value = [], array $teamsFilter = []): array\n {\n return $this->activityTypeService->getActivityTypeFieldData(team: $team, value: $value, groupIds: $teamsFilter);\n }\n\n public function getDealStageAtCallFieldData(?Team $team = null, array $value = []): array\n {\n return $this->dealStagesService->getDealStageAtCallFieldData(team: $team, value: $value);\n }\n\n public function getCurrentDealStageFieldData(?Team $team = null, array $value = []): array\n {\n return $this->dealStagesService->getCurrentDealStageFieldData(team: $team, value: $value);\n }\n\n public function getDealValueFieldData(?int $valueMin = null, ?int $valueMax = null): array\n {\n return [\n 'id' => 'deal_value',\n 'label' => 'Deal Value',\n 'inputType' => InputTypeEnum::INTEGER_RANGE,\n 'required' => false,\n 'value' => ['min' => $valueMin, 'max' => $valueMax],\n 'queryParams' => [\n 'min' => 'min_deal_value',\n 'max' => 'max_deal_value',\n ],\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getCallTypeFieldData(bool $conferenceOn = false, bool $dialerOn = false): array\n {\n $value = [];\n $conferenceOn && $value[] = self::CALL_TYPE_CONFERENCE;\n $dialerOn && $value[] = self::CALL_TYPE_DIALER;\n\n return [\n 'id' => 'call_type',\n 'label' => 'Call Type',\n 'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,\n 'required' => true,\n 'options' => [\n self::CALL_TYPE_CONFERENCE,\n self::CALL_TYPE_DIALER,\n ],\n 'value' => $value,\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getMediaTypeFieldData(?AutomatedReport $report = null): array\n {\n $value = [];\n\n if ($report) {\n $value = $this->transformMediaTypes($report);\n }\n\n return [\n 'id' => 'media_types',\n 'label' => 'Export as',\n 'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,\n 'required' => true,\n 'options' => self::MEDIA_TYPE_OBJECTS,\n 'value' => $value,\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getCallDurationFieldData(?int $valueMin = null, ?int $valueMax = null): array\n {\n return [\n 'id' => 'call_duration',\n 'label' => 'Call Duration',\n 'inputType' => InputTypeEnum::INTEGER_RANGE,\n 'required' => false,\n 'value' => ['min' => $valueMin, 'max' => $valueMax],\n 'queryParams' => [\n 'min' => 'min_call_duration',\n 'max' => 'max_call_duration',\n ],\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getRecipientsFieldData(?Team $team = null, array $value = []): array\n {\n return $this->recipientsService->getRecipientsFieldData(team: $team, value: $value);\n }\n\n public function getJiminnyRecipientsFieldData(array $value = []): array\n {\n return $this->recipientsService->getJiminnyRecipientsFieldData($value);\n }\n\n public function getAdditionalPromptInputFieldData(?string $value = null): array\n {\n return [\n 'id' => 'additional_prompt_input',\n 'label' => 'Special requirements',\n 'inputType' => InputTypeEnum::TEXTAREA,\n 'required' => false,\n 'placeholder' => 'What should be the focus of the report?',\n 'value' => $value,\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getCustomReportNameFieldData(?string $value = null): array\n {\n return [\n 'id' => 'custom_name',\n 'label' => 'Custom report name',\n 'inputType' => InputTypeEnum::TEXT,\n 'required' => false,\n 'placeholder' => 'Enter custom name',\n 'value' => $value,\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n // data providers\n public function getTeams(): array\n {\n $teams = $this->teamRepository->getTeamsForKiosk(status: Team::STATUS_ACTIVE);\n\n $teamData = [];\n foreach ($teams as $team) {\n if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {\n continue;\n }\n\n $teamData[] = $this->transformTeam($team);\n }\n\n return $teamData;\n }\n\n public function getTeamGroups(string $teamUuid): array\n {\n $data = [];\n $team = $this->getTeam($teamUuid);\n\n if ($team !== null) {\n $groups = $team->groups()->get();\n\n foreach ($groups as $group) {\n $data[] = [\n 'id' => $group->getUuid(),\n 'name' => $group->getName(),\n ];\n }\n }\n\n return $data;\n }\n\n public function getTeamsGroupsOptions(array $filterTeamUuids = []): array\n {\n $data = [];\n $teams = $this->getTeams();\n\n foreach ($teams as $team) {\n if (! empty($filterTeamUuids) && ! in_array($team['id'], $filterTeamUuids, true)) {\n continue;\n }\n\n $data[] = [\n 'label' => $team['name'],\n 'groups' => $this->getTeamGroups($team['id']),\n ];\n }\n\n return $data;\n }\n\n public function getTeam(string $teamUuid): ?Team\n {\n return $this->teamRepository->idOrUuid($teamUuid);\n }\n\n public function getTeamById(int $teamId): ?Team\n {\n return $this->teamRepository->find($teamId);\n }\n\n public function getGroupsUuids(AutomatedReport $report): array\n {\n $uuids = [];\n $reportGroups = $report->getGroups();\n foreach ($reportGroups as $groupId) {\n if ($group = $this->groupRepository->find($groupId)) {\n $uuids[] = $group->getUuid();\n }\n }\n\n return $uuids;\n }\n\n public function getPlaybookCategoriesUuids(AutomatedReport $report): array\n {\n $uuids = [];\n $playbookCategories = $report->getPlaybookCategories();\n foreach ($playbookCategories as $id) {\n if ($category = $this->playbookCategoryRepository->find($id)) {\n $uuids[] = $category->getUuid();\n }\n }\n\n return $uuids;\n }\n\n public function getDealAtCallStagesUuids(AutomatedReport $report): array\n {\n $uuids = [];\n $reportStages = $report->getDealAtCallStages();\n foreach ($reportStages as $id) {\n if ($stage = $this->stageRepository->find($id)) {\n $uuids[] = $stage->getUuid();\n }\n }\n\n return $uuids;\n }\n\n public function getCurrentDealStagesUuids(AutomatedReport $report): array\n {\n $uuids = [];\n $reportStages = $report->getCurrentDealStages();\n foreach ($reportStages as $id) {\n if ($stage = $this->stageRepository->find($id)) {\n $uuids[] = $stage->getUuid();\n }\n }\n\n return $uuids;\n }\n\n public function getUsersUuids(AutomatedReport $report): array\n {\n return $this->extractUserUuids($report->getRecipients());\n }\n\n public function getJiminnyUsersUuids(AutomatedReport $report): array\n {\n return $this->extractUserUuids($report->getJiminnyRecipients());\n }\n\n /**\n * @param array<string, mixed> $recipients\n */\n private function extractUserUuids(array $recipients): array\n {\n $userIds = $recipients['users'] ?? [];\n\n return collect($userIds)\n ->map(fn ($id) => $this->userRepository->find((int) $id))\n ->filter()\n ->map(fn (UserContract $user) => $user->getUuid())\n ->values()\n ->all();\n }\n\n // get mail data\n public function getRecipientUsers(AutomatedReport $report): array\n {\n return $this->buildRecipientUsers($report->getRecipients());\n }\n\n /**\n * @return array<UserContract>\n */\n public function getRecipientUserObjects(AutomatedReport $report): array\n {\n $userIds = $report->getRecipients()['users'] ?? [];\n\n return collect($userIds)\n ->map(fn ($id) => $this->userRepository->find((int) $id))\n ->filter()\n ->values()\n ->all();\n }\n\n private function getJiminnyRecipientUsers(AutomatedReport $report): array\n {\n return $this->buildRecipientUsers($report->getJiminnyRecipients());\n }\n\n /**\n * @param array<string, mixed> $recipients\n */\n private function buildRecipientUsers(array $recipients): array\n {\n $userIds = $recipients['users'] ?? [];\n\n return collect($userIds)\n ->map(fn ($id) => $this->userRepository->find((int) $id))\n ->filter()\n ->map(fn (UserContract $user) => [\n 'email' => $user->getEmailAddress(),\n 'name' => $user->getName(),\n 'timezone' => $user->getTimezone()->getName(),\n ])\n ->values()\n ->all();\n }\n\n public function getValidRecipientUsers(AutomatedReport $report, bool $includeJiminny = false): array\n {\n if ($report->isAskJiminnyReport()) {\n $recipients = $this->resolveAskJiminnyRecipients($report);\n } else {\n $recipients = $this->getRecipientUsers($report);\n if ($includeJiminny) {\n $recipients = array_merge($recipients, $this->getJiminnyRecipientUsers($report));\n }\n }\n\n $emails = [];\n\n return array_values(array_filter(\n $recipients,\n static function ($recipient) use (&$emails) {\n if (empty($recipient['email']) || in_array($recipient['email'], $emails, true)) {\n return false;\n }\n $emails[] = $recipient['email'];\n\n return true;\n }\n ));\n }\n\n private function resolveAskJiminnyRecipients(AutomatedReport $report): array\n {\n $recipients = [];\n\n $creator = $report->getCreator();\n if ($creator !== null) {\n $recipients[] = [\n 'email' => $creator->getEmailAddress(),\n 'name' => $creator->getName(),\n 'timezone' => $creator->getTimezone()->getName(),\n ];\n }\n\n return array_merge(\n $recipients,\n $this->buildRecipientUsers($report->getRecipients()),\n $this->getGroupRecipientUsers($report),\n );\n }\n\n private function getGroupRecipientUsers(AutomatedReport $report): array\n {\n $users = [];\n foreach ($report->getGroups() as $groupId) {\n $group = $this->groupRepository->find($groupId);\n if ($group === null) {\n continue;\n }\n foreach ($group->getMembers() as $member) {\n $users[] = [\n 'email' => $member->getEmailAddress(),\n 'name' => $member->getName(),\n 'timezone' => $member->getTimezone()->getName(),\n ];\n }\n }\n\n return $users;\n }\n\n public function getReportTypeName(AutomatedReportResult $report): string\n {\n $type = $report->getReport()->getType();\n\n $getType = $this->transformReportType($type);\n\n return $getType['name'];\n }\n\n public function getReportPeriodName(AutomatedReportResult $report): string\n {\n $from = $report->getFromDate();\n $to = $report->getToDate();\n $frequency = $report->getReport()->getFrequency();\n\n if ($from === null || $to === null) {\n if (! $report->getReport()->isAskJiminnyReport()) {\n $invalidPeriod = $from === null ? 'from' : 'to';\n\n throw new ApplicationException('Report period is invalid: ' . $invalidPeriod);\n }\n\n $period = $this->calculateFromAndToDatePeriod($frequency);\n $from = $period['fromDate'];\n $to = $period['toDate'];\n }\n\n return $this->formatReportPeriodName($frequency, $from, $to);\n }\n\n private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string\n {\n $fromYear = $from->format('Y');\n $toYear = $to->format('Y');\n $differentYears = $fromYear !== $toYear;\n\n switch ($frequency) {\n case self::FREQUENCY_DAILY:\n return $from->format('j M Y');\n\n case self::FREQUENCY_QUARTERLY:\n // 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ\n $startMonth = $from->format('M');\n $endMonth = $to->copy()->subMonth();\n $endMonthName = $endMonth->format('M');\n $endMonthYear = $endMonth->format('Y');\n\n if ($differentYears) {\n return \"{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}\";\n }\n\n return \"{$startMonth} - {$endMonthName} {$toYear}\";\n\n case self::FREQUENCY_MONTHLY:\n // 'May 2025' - monthly reports are always within the same year\n return $from->format('M Y');\n\n case self::FREQUENCY_WEEKLY:\n // '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ\n $startDay = $from->format('j');\n $endDay = $to->format('j');\n $startMonth = $from->format('M');\n $endMonth = $to->format('M');\n\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n if ($startMonth !== $endMonth) {\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n return \"{$startDay} - {$endDay} {$endMonth} {$toYear}\";\n\n case self::FREQUENCY_ONE_OFF:\n // '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ\n $startDay = $from->format('j');\n $startMonth = $from->format('M');\n $endDay = $to->format('j');\n $endMonth = $to->format('M');\n\n // If same month and year, use a format like '2-31 May 2025'\n if ($startMonth === $endMonth && ! $differentYears) {\n return \"{$startDay} - {$endDay} {$startMonth} {$toYear}\";\n }\n\n // If different years, include both years\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n // Same year but different months\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n\n default:\n // Default format for unknown frequencies\n return $from->format('j M Y') . ' - ' . $to->format('j M Y');\n }\n }\n\n public function getReportTeamsName(AutomatedReportResult $report): string\n {\n $groups = $report->getGroups();\n\n if (empty($groups)) {\n return 'All';\n }\n\n // Get group names from repository\n $groupNames = [];\n foreach ($groups as $groupId) {\n $group = $this->groupRepository->find($groupId);\n if ($group) {\n $groupNames[] = $group->getName();\n }\n }\n\n if (count($groupNames) === 1) {\n // Single team format\n $teamsName = $groupNames[0];\n } else {\n // Multiple teams format\n $teamsName = implode(', ', $groupNames);\n }\n\n return $teamsName;\n }\n\n public function getReportFileName(AutomatedReportResult $report): string\n {\n $customName = $report->getReport()->getCustomName();\n $periodName = $this->getReportPeriodName($report);\n $filenameSuffix = $this->getFilenameSuffix($report);\n\n if ($customName) {\n if ($filenameSuffix) {\n $customName .= \" {$filenameSuffix}\";\n }\n\n return $this->sanitizeFileName(\"{$customName} - {$periodName}\");\n }\n\n $baseName = $this->getReportTypeName($report);\n\n if ($filenameSuffix) {\n $baseName .= \" {$filenameSuffix}\";\n }\n\n return $this->sanitizeFileName(\"{$baseName} - {$periodName} - {$this->getReportTeamsName($report)}\");\n }\n\n public function getReportFileNameWithExtension(AutomatedReportResult $result): string\n {\n $extension = $this->getMediaTypeMetadata($result)['extension'];\n\n return $this->getReportFileName($result) . '.' . $extension;\n }\n\n public function sanitizeFileName(string $fileName): string\n {\n return str_replace(['/', '\\\\'], '-', $fileName);\n }\n\n public function isUserRecipientOfReport(User $user, AutomatedReport $report): bool\n {\n $recipients = array_map('intval', $report->getRecipients()['users'] ?? []);\n\n return in_array($user->getId(), $recipients);\n }\n\n public function transformReportResults(Collection $automatedReportResults): array\n {\n $data = [];\n foreach ($automatedReportResults as $automatedReportResult) {\n /** @var AutomatedReportResult $automatedReportResult */\n\n $report = $automatedReportResult->getReport();\n\n $createdBy = $report->getCreator();\n $creator = [\n 'id' => $createdBy?->getUuid(),\n 'name' => $createdBy?->getName(),\n 'email' => $createdBy?->getEmailAddress(),\n 'photoUrl' => $createdBy?->getPhotoUrl(),\n ];\n\n $recipients = $this->buildRecipients($report);\n\n $data[] = [\n 'id' => $automatedReportResult->getUuid(),\n 'name' => $automatedReportResult->getName(),\n 'frequency' => $this->transformFrequency($report->getFrequency()),\n 'recipients' => [\n ...array_values($this->transformGroups(team: $report->getTeam(), groupsIds: $report->getGroups())),\n ...array_values($this->transformRecipients($report->getRecipients())),\n ],\n 'report_type' => $this->transformReportType($report->getType()),\n 'media_type' => $automatedReportResult->getMediaType(),\n 'downloadUrl' => $this->generateReportResultDownloadUrl($automatedReportResult),\n 'viewUrl' => $this->generateReportResultViewUrl($automatedReportResult),\n 'generated_at' => $automatedReportResult->getGeneratedAt()?->toIso8601String(),\n 'creator' => $creator,\n ];\n }\n\n return $data;\n }\n\n public function hasCallTypeConference(AutomatedReport $report): bool\n {\n return in_array(self::CALL_TYPE_CONFERENCE['id'], $report->getCallTypes(), true);\n }\n\n public function hasCallTypeDialer(AutomatedReport $report): bool\n {\n return in_array(self::CALL_TYPE_DIALER['id'], $report->getCallTypes(), true);\n }\n\n // transformers\n private function transformTeam(Team $team): array\n {\n if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {\n return [];\n }\n\n return [\n 'id' => $team->getUuid(),\n 'name' => $team->getName(),\n ];\n }\n\n private function transformReportFullView(AutomatedReport $report): array\n {\n $base = $this->transformReportBase($report);\n\n return $report->getType() === self::TYPE_ASK_JIMINNY\n ? $base + $this->transformAskJiminnyFields($report)\n : $base + $this->transformStandardReportFields($report);\n }\n\n private function transformReportBase(AutomatedReport $report): array\n {\n return [\n 'id' => $report->getUuid(),\n 'organization' => $this->transformOrganization(team: $report->getTeam()),\n 'report_type' => $this->transformReportType($report->getType()),\n 'frequency' => $this->transformFrequency($report->getFrequency()),\n ];\n }\n\n private function transformStandardReportFields(AutomatedReport $report): array\n {\n $team = $report->getTeam();\n\n return [\n 'report_enabled' => $report->getStatus(),\n 'start_date_period' => $report->getFrom()?->format('Y-m-d H:i:s'),\n 'end_date_period' => $report->getTo()?->format('Y-m-d H:i:s'),\n 'deal_value_min' => $report->getDealValueMin(),\n 'deal_value_max' => $report->getDealValueMax(),\n 'call_types' => $this->transformCallType($report->getCallTypes()),\n 'media_types' => $this->transformMediaTypes($report),\n 'call_duration_min' => $this->transformDurationToMinutes($report->getCallDurationMin()),\n 'call_duration_max' => $this->transformDurationToMinutes($report->getCallDurationMax()),\n 'teams' => $this->transformGroups(team: $team, groupsIds: $report->getGroups()),\n 'deal_at_call_stages' => $this->transformStages(team: $team, stagesIds: $report->getDealAtCallStages()),\n 'current_deal_stages' => $this->transformStages(team: $team, stagesIds: $report->getCurrentDealStages()),\n 'recipients' => $this->transformRecipients($report->getRecipients()),\n 'created_by' => $this->transformCreator($report->getCreator()),\n 'additional_prompt_input' => $report->getAdditionalPromptInput(),\n 'custom_name' => $report->getCustomName(),\n 'created_at' => $report->getCreatedAt()->format('Y-m-d H:i:s'),\n 'updated_at' => $report->getUpdatedAt()->format('Y-m-d H:i:s'),\n 'deleted_at' => $report->getDeletedAt()?->format('Y-m-d H:i:s'),\n ];\n }\n\n private function transformAskJiminnyFields(AutomatedReport $report): array\n {\n $team = $report->getTeam();\n $creatorId = $report->getAttribute('created_by');\n $explicitUserIds = array_values(array_filter(\n $report->getRecipients()['users'] ?? [],\n static fn ($id) => $id !== $creatorId\n ));\n\n return [\n 'report_name' => $report->getCustomName(),\n 'enabled' => $report->getStatus(),\n 'share_teams' => $this->transformGroups(team: $team, groupsIds: $report->getGroups()),\n 'share_users' => $this->transformRecipients(['users' => $explicitUserIds]),\n 'saved_search' => $this->transformSafeSearch($report->getSavedSearch()),\n 'ask_jiminny_prompt' => $this->transformAskJiminnyPrompt($report->getAskAnythingPrompt()),\n 'expires_on' => $report->getExpiresAt()?->format('Y-m-d'),\n ];\n }\n\n private function transformOrganization(?Team $team): array\n {\n return [\n 'id' => $team?->getUuid(),\n 'name' => $team?->getName(),\n ];\n }\n\n private function transformReportType(string $type): array\n {\n foreach (self::ALL_TYPES as $typeItem) {\n if ($typeItem['id'] === $type) {\n return $typeItem;\n }\n }\n\n return [];\n }\n\n private function transformCallType(array $types): array\n {\n $result = [];\n $callTypes = [self::CALL_TYPE_CONFERENCE, self::CALL_TYPE_DIALER];\n\n foreach ($types as $type) {\n foreach ($callTypes as $callTypeItem) {\n if ($callTypeItem['id'] === $type) {\n $result[] = $callTypeItem;\n\n break;\n }\n }\n }\n\n return $result;\n }\n\n private function transformMediaTypes(AutomatedReport $report): array\n {\n $values = [];\n\n foreach ($report->getMediaTypes() as $mediaType) {\n if (! in_array($mediaType, self::MEDIA_TYPES, true)) {\n continue;\n }\n\n $values[] = match ($mediaType) {\n self::MEDIA_TYPE_PDF => self::MEDIA_TYPE_OBJECT_PDF,\n self::MEDIA_TYPE_PODCAST => self::MEDIA_TYPE_OBJECT_PODCAST,\n };\n }\n\n return $values;\n }\n\n private function transformFrequency(string $frequency): array\n {\n foreach (self::ALL_FREQUENCIES as $frequencyItem) {\n if ($frequencyItem['id'] === $frequency) {\n return $frequencyItem;\n }\n }\n\n return [];\n }\n\n public function transformDurationToMinutes(?int $duration): ?int\n {\n if (! $duration) {\n return null;\n }\n\n return (int) ($duration / 60);\n }\n\n private function transformGroups(?Team $team, array $groupsIds): array\n {\n if (empty($groupsIds) || ! $team) {\n return [];\n }\n\n $data = [];\n foreach ($groupsIds as $groupId) {\n $group = $team->groups()->where('id', $groupId)->first();\n\n if ($group) {\n $data[] = [\n 'id' => $group->getUuid(),\n 'name' => $group->getName(),\n 'photoUrl' => $group->getPhotoUrl(),\n ];\n }\n }\n\n return $data;\n }\n\n private function transformStages(?Team $team, array $stagesIds): array\n {\n if (empty($stagesIds) || ! $team) {\n return [];\n }\n\n $data = [];\n foreach ($stagesIds as $stageId) {\n $stage = $team->stages()->where('id', $stageId)->first();\n\n if ($stage) {\n $data[] = [\n 'id' => $stage->getUuid(),\n 'name' => $stage->getName(),\n ];\n }\n }\n\n return $data;\n }\n\n private function transformRecipients(array $recipients): array\n {\n $users = [];\n foreach ($recipients['users'] ?? [] as $userId) {\n $users[] = $this->transformUser($userId);\n }\n\n return $users;\n }\n\n private function transformCreator(?User $user): ?array\n {\n if ($user === null) {\n return null;\n }\n\n return $this->transformUser($user->getId());\n }\n\n private function transformAskJiminnyPrompt(?AskAnythingPrompt $prompt): ?array\n {\n if ($prompt === null) {\n return null;\n }\n\n return [\n 'id' => $prompt->getUuid(),\n 'name' => $prompt->getTitle(),\n ];\n }\n\n private function transformSafeSearch(?Search $search): ?array\n {\n if ($search === null) {\n return null;\n }\n\n return [\n 'id' => $search->getUuid(),\n 'name' => $search->getName(),\n ];\n }\n\n private function transformUser(int $userId): array\n {\n /* @var ?User $user */\n $user = $this->userRepository->find($userId);\n\n return [\n 'id' => $user?->getUuid(),\n 'name' => $user?->getName(),\n 'email' => $user?->getEmailAddress(),\n 'photoUrl' => $user?->getPhotoUrl(),\n ];\n }\n\n public function create(array $data): array\n {\n $validatedData = $this->validateAndTransformData($data);\n $validatedData['created_by'] = auth()->id();\n\n $automatedReport = $this->automatedReportsRepository->create($validatedData);\n\n $this->generateOneOffReport($automatedReport);\n\n return $this->transformReportFullView($automatedReport);\n }\n\n public function update(string $uuid, array $data): array\n {\n $validatedData = $this->validateAndTransformData($data);\n $report = $this->automatedReportsRepository->findByUuid($uuid);\n\n if (! $report) {\n throw new InvalidArgumentException('Report not found');\n }\n\n $oldCustomName = $report->getCustomName();\n\n $automatedReport = $this->automatedReportsRepository->update($report, $validatedData);\n\n if ($oldCustomName !== $automatedReport->getCustomName()) {\n $this->updateResultNames($automatedReport);\n }\n\n $this->generateOneOffReport($automatedReport);\n\n return $this->transformReportFullView($automatedReport);\n }\n\n /**\n * Create an Ask Jiminny report.\n */\n public function createAskJiminnyReport(array $data, User $creator): array\n {\n $validatedData = $this->validateAskJiminnyReportData($data, $creator);\n $validatedData['created_by'] = $creator->getId();\n\n $automatedReport = $this->automatedReportsRepository->create($validatedData);\n\n return $this->transformReportFullView($automatedReport);\n }\n\n /**\n * Update an Ask Jiminny report.\n */\n public function updateAskJiminnyReport(AutomatedReport $report, array $data, User $user): array\n {\n if (! $report->isAskJiminnyReport()) {\n throw new InvalidArgumentException('Report is not an Ask Jiminny report');\n }\n\n $validatedData = $this->validateAskJiminnyReportData($data, $user);\n\n $oldCustomName = $report->getCustomName();\n\n $automatedReport = $this->automatedReportsRepository->update($report, $validatedData);\n\n if ($oldCustomName !== $automatedReport->getCustomName()) {\n $this->updateResultNames($automatedReport);\n }\n\n return $this->transformReportFullView($automatedReport);\n }\n\n public function updateAskJiminnyReportStatus(AutomatedReport $report, bool $status): array\n {\n $this->automatedReportsRepository->update($report, ['status' => $status]);\n\n return $this->transformReportFullView($report->fresh());\n }\n\n /**\n * Validate and transform data for Ask Jiminny reports.\n */\n private function validateAskJiminnyReportData(array $data, User $user): array\n {\n // Validate name\n $name = trim($data['report_name'] ?? '');\n if (empty($name)) {\n throw new InvalidArgumentException('Report name is required');\n }\n if (mb_strlen($name) > 50) {\n throw new InvalidArgumentException('Report name must be 50 characters or less');\n }\n\n // Validate frequency (only daily, weekly, monthly for Ask Jiminny)\n $frequency = $data['frequency'] ?? null;\n $askJiminnyFrequencies = [self::FREQUENCY_DAILY, self::FREQUENCY_WEEKLY, self::FREQUENCY_MONTHLY];\n if (! in_array($frequency, $askJiminnyFrequencies, true)) {\n throw new InvalidArgumentException('Frequency must be daily, weekly, or monthly');\n }\n\n // Validate expiration date\n $expiresAt = $data['expires_on'] ?? null;\n if (empty($expiresAt)) {\n throw new InvalidArgumentException('Expiration date is required');\n }\n\n try {\n $expiresAtDate = Carbon::parse($expiresAt);\n } catch (InvalidFormatException $e) {\n throw new InvalidArgumentException('Expiration date format is invalid');\n }\n $maxExpiration = Carbon::now()->addYear()->endOfDay();\n if ($expiresAtDate->gt($maxExpiration)) {\n throw new InvalidArgumentException('Expiration date cannot be more than 1 year from now');\n }\n if ($expiresAtDate->isPast()) {\n throw new InvalidArgumentException('Expiration date cannot be in the past');\n }\n\n // Validate saved search\n $activitySearchId = $data['saved_search'] ?? null;\n if (empty($activitySearchId)) {\n throw new InvalidArgumentException('Saved search is required');\n }\n $savedSearch = $this->activitySearchRepository->findByUuidAndUser($activitySearchId, $user);\n if (! $savedSearch) {\n throw new InvalidArgumentException('Saved search not found or does not belong to you');\n }\n\n // Validate saved prompt\n $askAnythingPromptId = $data['ask_jiminny_prompt'] ?? null;\n if (empty($askAnythingPromptId)) {\n throw new InvalidArgumentException('Ask Jiminny prompt is required');\n }\n $prompt = $this->askAnythingRepository->getPromptByUuid($askAnythingPromptId);\n if (! $prompt) {\n throw new InvalidArgumentException('Ask Jiminny prompt not found');\n }\n\n // Validate status\n $status = $data['enabled'] ?? false;\n\n $recipientUserIds = [$user->getId()];\n\n if (! empty($data['share_users'])) {\n $sharedUserIds = $this->validateAndGetUserIdsByTeam(\n $user->team,\n (array) $data['share_users']\n );\n $recipientUserIds = array_merge($recipientUserIds, $sharedUserIds);\n }\n\n $sharedGroupIds = [];\n if (! empty($data['share_teams'])) {\n $sharedGroupIds = $this->validateAndGetGroupIds($user->team, (array) $data['share_teams']);\n }\n\n $recipientUserIds = array_values(array_unique($recipientUserIds));\n\n return [\n 'team_id' => $user->getTeamId(),\n 'type' => self::TYPE_ASK_JIMINNY,\n 'status' => (bool) $status,\n 'frequency' => $frequency,\n 'custom_name' => $name,\n 'activity_search_id' => $savedSearch->getId(),\n 'ask_anything_prompt_id' => $prompt->getId(),\n 'expires_at' => $expiresAtDate->toDateString(),\n 'media_types' => [self::MEDIA_TYPE_PDF],\n 'call_types' => [],\n 'recipients' => ['users' => $recipientUserIds],\n 'groups' => $sharedGroupIds,\n ];\n }\n\n public static function getAskJiminnyFrequencies(): array\n {\n return array_map(static function ($frequency) {\n return $frequency['id'];\n }, self::ASK_JIMINNY_FREQUENCIES);\n }\n\n public function getAskJiminnyReportFilters(User $user): array\n {\n $savedSearches = $this->activitySearchRepository->findByUserOrderedByName($user)\n ->map(fn (Search $search) => [\n 'id' => $search->getUuid(),\n 'name' => $search->getName(),\n ])\n ->values()->all();\n\n $prompts = collect(\n $this->askAnythingPromptService->get($user, AskAnythingPromptTarget::on_demand)\n )->map(fn (AskAnythingPromptDto $prompt) => [\n 'id' => $prompt->id,\n 'name' => $prompt->title,\n ])->values()->all();\n\n return [\n [\n 'id' => 'prompt',\n 'label' => 'Prompt',\n 'options' => $prompts,\n ],\n [\n 'id' => 'saved_search',\n 'label' => 'Saved Search',\n 'options' => $savedSearches,\n ],\n ];\n }\n\n public function getAskJiminnyReportFormData(User $user, ?AutomatedReport $report = null): array\n {\n $team = $user->getTeam();\n $userTimezone = $user->getTimezone();\n\n $savedSearches = $this->activitySearchRepository->findByUserOrderedByName($user)\n ->map(fn (Search $search) => [\n 'id' => $search->getUuid(),\n 'name' => $search->getName(),\n ])\n ->values()->all();\n\n $prompts = collect(\n $this->askAnythingPromptService->get($user, AskAnythingPromptTarget::on_demand)\n )->map(fn (AskAnythingPromptDto $prompt) => [\n 'id' => $prompt->id,\n 'name' => $prompt->title,\n ])->values()->all();\n\n $teamGroups = $this->groupRepository->getAllByTeam($team)->map(fn ($group) => [\n 'id' => $group->getUuid(),\n 'name' => $group->getName(),\n ])->values()->all();\n\n $shareUsers = $this->recipientsService->getRecipientsFieldData(team: $team)['options'] ?? [];\n\n $sharedTeamsValue = [];\n $sharedUsersValue = [];\n if ($report) {\n $sharedTeamsValue = $this->transformGroups($team, $report->getGroups());\n\n $recipientUserIds = $report->getRecipients()['users'] ?? [];\n $creatorId = $report->getAttribute('created_by');\n $sharedUserIds = array_values(array_filter(\n $recipientUserIds,\n static fn ($id) => $id !== $creatorId\n ));\n $sharedUsersValue = collect($sharedUserIds)\n ->map(fn ($id) => $this->userRepository->find((int) $id))\n ->filter()\n ->map(fn (User $u) => [\n 'id' => $u->getUuid(),\n 'name' => $u->getName(),\n ])\n ->values()\n ->all();\n }\n\n return [\n 'fields' => [\n [\n 'id' => 'enabled',\n 'inputType' => InputTypeEnum::TOGGLE,\n 'label' => '',\n 'value' => $report?->getStatus() ?? false,\n ],\n [\n 'id' => 'report_name',\n 'inputType' => InputTypeEnum::TEXT,\n 'label' => 'Name',\n 'placeholder' => 'Enter name',\n 'required' => true,\n 'validation' => ['maxLength' => 50],\n 'value' => $report?->getCustomName() ?? '',\n ],\n [\n 'id' => 'frequency',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'label' => 'Frequency',\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => self::ASK_JIMINNY_FREQUENCIES,\n 'value' => $report ? $this->transformFrequency($report->getFrequency()) : null,\n ],\n [\n 'id' => 'expires_on',\n 'inputType' => InputTypeEnum::DATE,\n 'label' => 'Expires on',\n 'required' => true,\n 'placeholder' => 'Select',\n 'validation' => [\n 'minDate' => now($userTimezone)->toDateString(),\n 'maxDate' => now($userTimezone)->addYear()->toDateString(),\n ],\n 'value' => $report?->getExpiresAt()?->toDateString(),\n ],\n [\n 'id' => 'share_teams',\n 'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,\n 'label' => 'Team',\n 'required' => false,\n 'placeholder' => 'Select',\n 'options' => $teamGroups,\n 'value' => $sharedTeamsValue,\n ],\n [\n 'id' => 'share_users',\n 'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,\n 'label' => 'Team member',\n 'required' => false,\n 'placeholder' => 'Select',\n 'groupLabelKey' => 'label',\n 'groupValuesKey' => 'users',\n 'optionLabelKey' => 'name',\n 'optionValueKey' => 'id',\n 'options' => $shareUsers,\n 'value' => $sharedUsersValue,\n ],\n [\n 'id' => 'saved_search',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'label' => 'Saved search',\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => $savedSearches,\n 'value' => $report && $report->getSavedSearch() ? [\n 'id' => $report->getSavedSearch()->getUuid(),\n 'name' => $report->getSavedSearch()->getName(),\n ] : null,\n ],\n [\n 'id' => 'ask_jiminny_prompt',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'label' => 'Ask Jiminny prompt',\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => $prompts,\n 'value' => $report && $report->getAskAnythingPrompt() ? [\n 'id' => $report->getAskAnythingPrompt()->getUuid(),\n 'name' => $report->getAskAnythingPrompt()->getTitle(),\n ] : null,\n ],\n ],\n ];\n }\n\n private function updateResultNames(AutomatedReport $automatedReport): void\n {\n $results = $this->automatedReportsRepository->getResultsByReport($automatedReport);\n\n foreach ($results as $result) {\n $result->update(['name' => $this->getReportFileName($result)]);\n }\n }\n\n public function updateStatus(string $uuid, array $data): array\n {\n $automatedReport = $this->automatedReportsRepository->findByUuid($uuid);\n\n if (! $automatedReport) {\n throw new ModelNotFoundException('Report not found');\n }\n\n $status = $this->validateReportStatus($data['report_enabled'] ?? null);\n $automatedReport->update([\n 'status' => $status,\n ]);\n\n $this->generateOneOffReport($automatedReport);\n\n return $this->transformReportFullView($automatedReport);\n }\n\n private function generateOneOffReport(AutomatedReport $automatedReport): void\n {\n // the scheduler handles all the other frequency types\n if ($automatedReport->getStatus() === false || $automatedReport->getFrequency() !== self::FREQUENCY_ONE_OFF) {\n return;\n }\n\n $this->dispatcher->dispatch(new RequestGenerateReportJob($automatedReport->getUuid()));\n }\n\n public function getReport(string $uuid): AutomatedReport\n {\n $automatedReport = $this->automatedReportsRepository->findByUuid($uuid);\n\n if (! $automatedReport) {\n throw new ModelNotFoundException('Report not found');\n }\n\n return $automatedReport;\n }\n\n public function get(string $uuid): array\n {\n $automatedReport = $this->getReport($uuid);\n\n return $this->transformReportFullView($automatedReport);\n }\n\n public function list(string $sortColumn = 'created_at', string $sortDirection = 'desc'): array\n {\n $results = [];\n $collection = $this->automatedReportsRepository->getAllStandardReports($sortColumn, $sortDirection);\n\n /** @var AutomatedReport $report */\n foreach ($collection as $report) {\n $results[] = $this->transformReportFullView($report);\n }\n\n return ['data' => $results];\n }\n\n public function listAskJiminnyReports(\n User $user,\n string $sortColumn = 'created_at',\n string $sortDirection = 'desc'\n ): array {\n $results = [];\n $collection = $this->automatedReportsRepository->getAskJiminnyReportsByUser($user, $sortColumn, $sortDirection);\n\n /** @var AutomatedReport $report */\n foreach ($collection as $report) {\n $results[] = $this->transformReportFullView($report);\n }\n\n return ['data' => $results];\n }\n\n public function delete(string $uuid): void\n {\n $automatedReport = $this->automatedReportsRepository->findByUuid($uuid);\n\n if (! $automatedReport) {\n throw new ModelNotFoundException('Report not found');\n }\n\n $automatedReport->delete();\n }\n\n public function createReportResult(AutomatedReport $automatedReport, array $data = []): AutomatedReportResult\n {\n return $this->automatedReportsRepository->createResult(\n array_merge(\n [\n 'report_id' => $automatedReport->getId(),\n 'status' => AutomatedReportResult::STATUS_DEFAULT,\n ],\n $data\n )\n );\n }\n\n public function getOrCreateReportResult(AutomatedReport $automatedReport, array $data = []): AutomatedReportResult\n {\n $existing = $this->automatedReportsRepository->findLatestDefaultOrFailedResult($automatedReport);\n\n if ($existing !== null) {\n $existing->update(['status' => AutomatedReportResult::STATUS_DEFAULT]);\n\n return $existing;\n }\n\n return $this->createReportResult($automatedReport, $data);\n }\n\n public function getReportResult(string $resultUuid): AutomatedReportResult\n {\n $report = $this->automatedReportsRepository->findResultByUuid($resultUuid);\n\n if (! $report) {\n throw new ModelNotFoundException('Report Result not found');\n }\n\n return $report;\n }\n\n public function findChildResult(AutomatedReportResult $result, string $type): ?AutomatedReportResult\n {\n return $this->automatedReportsRepository->findChildResult($result, $type);\n }\n\n // prophet API calls\n /**\n * @throws ApplicationException\n */\n public function getGenerateReportPayload(AutomatedReport $automatedReport, string $reportResultUuid): array\n {\n $period = $this->calculateFromAndToDate($automatedReport);\n $fromDate = $period['fromDate'];\n $toDate = $period['toDate'];\n\n return [\n 'team_id' => $automatedReport->getTeamId(),\n 'request_id' => $reportResultUuid,\n 'report_type' => $automatedReport->getType(),\n 'media_types' => $automatedReport->getMediaTypes(),\n 'from_date' => $fromDate->startOfDay()->format(DateTimeInterface::RFC3339),\n 'to_date' => $toDate->endOfDay()->format(DateTimeInterface::RFC3339),\n 'group_ids' => $automatedReport->getGroups(),\n 'call_deal_stage' => $automatedReport->getDealAtCallStages(),\n 'current_deal_stage' => $automatedReport->getCurrentDealStages(),\n 'deal_min_value' => $automatedReport->getDealValueMin(),\n 'deal_max_value' => $automatedReport->getDealValueMax(),\n 'call_types' => $automatedReport->getCallTypes(),\n 'call_duration_min_seconds' => $automatedReport->getCallDurationMin(),\n 'call_duration_max_seconds' => $automatedReport->getCallDurationMax(),\n 'special_requirements' => $automatedReport->getAdditionalPromptInput(),\n 'callback_url' => $this->getCallbackUrl(),\n 'report_period' => $this->formatReportPeriodName(\n $automatedReport->getFrequency(),\n $fromDate,\n $toDate,\n ),\n 'playbook_categories' => $automatedReport->getPlaybookCategories(),\n 'custom_name' => $automatedReport->getCustomName(),\n ];\n }\n\n // $inputPayload - FE payload structure\n public function getActivitiesCountPayload(array $inputPayload): array\n {\n // Use validateAndTransformData to validate and normalize input\n $validatedData = $this->validateAndTransformData($inputPayload);\n $period = $this->calculateFromAndToDatePeriod(\n $validatedData['frequency'],\n Carbon::parse($validatedData['from']),\n Carbon::parse($validatedData['to']),\n );\n $fromDate = $period['fromDate'];\n $toDate = $period['toDate'];\n\n // Create payload similar to getGenerateReportPayload\n return [\n 'team_id' => $validatedData['team_id'],\n 'group_ids' => $validatedData['groups'] ?? [],\n 'report_type' => $validatedData['type'],\n 'from_date' => $fromDate->format(DateTimeInterface::RFC3339),\n 'to_date' => $toDate->format(DateTimeInterface::RFC3339),\n 'call_deal_stage' => $validatedData['deal_at_call_stages'] ?? [],\n 'current_deal_stage' => $validatedData['current_deal_stages'] ?? [],\n 'deal_min_value' => $validatedData['deal_value_min'] ?? null,\n 'deal_max_value' => $validatedData['deal_value_max'] ?? null,\n 'call_types' => $validatedData['call_types'],\n 'call_duration_min_seconds' => $validatedData['call_duration_min'] ?? null,\n 'call_duration_max_seconds' => $validatedData['call_duration_max'] ?? null,\n 'special_requirements' => $validatedData['additional_prompt_input'] ?? null,\n 'playbook_categories' => $validatedData['playbook_categories'] ?? [],\n 'request_id' => null,\n 'callback_url' => null,\n ];\n }\n\n public function shouldSendReport(array $users, ?CarbonInterface $generatedAt = null): bool\n {\n if (empty($users)) {\n return false;\n }\n\n $earliestTz = collect($users)\n ->mapWithKeys(function (array $user) {\n $tz = new DateTimeZone($user['timezone']);\n $nowUtc = new DateTime('now', new DateTimeZone('UTC'));\n $offset = $tz->getOffset($nowUtc);\n\n return [$user['timezone'] => $offset];\n })\n ->sortDesc()\n ->keys()\n ->first();\n\n $now = Carbon::now($earliestTz);\n $isScheduledTime = (int) $now->format('H') === self::SENT_REPORT_AT_HOURS;\n\n if ($isScheduledTime) {\n return true;\n }\n\n return $this->hasPassedScheduledTime($generatedAt, $earliestTz);\n }\n\n public function hasPassedScheduledTime(?CarbonInterface $generatedAt, string $timezone): bool\n {\n if ($generatedAt === null) {\n return false;\n }\n\n $now = Carbon::now($timezone);\n $scheduledTime = $now->copy()->setTime(self::SENT_REPORT_AT_HOURS, 0, 0);\n\n if ($now->hour < self::SENT_REPORT_AT_HOURS) {\n $scheduledTime = $scheduledTime->subDay();\n }\n\n $scheduledTimeUtc = $scheduledTime->copy()->utc();\n $generatedAtUtc = $generatedAt->copy()->utc();\n $nowUtc = $now->copy()->utc();\n\n return $generatedAtUtc->lt($scheduledTimeUtc) && $nowUtc->gt($scheduledTimeUtc);\n }\n\n public function calculateFromAndToDatePeriod(\n string $frequency,\n ?Carbon $fromDate = null,\n ?Carbon $toDate = null\n ): array {\n if ($frequency === self::FREQUENCY_ONE_OFF) {\n return [\n 'fromDate' => $fromDate,\n 'toDate' => $toDate,\n ];\n }\n\n $now = Carbon::now();\n\n return match ($frequency) {\n self::FREQUENCY_DAILY => [\n 'fromDate' => $now->copy()->subDay()->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_WEEKLY => [\n 'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_MONTHLY => [\n 'fromDate' => $now->copy()->subMonths(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_QUARTERLY => [\n 'fromDate' => $now->copy()->subMonths(3)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n default => throw new InvalidArgumentException(\"Unsupported frequency: {$frequency}\"),\n };\n }\n\n private function calculateFromAndToDate(AutomatedReport $automatedReport): array\n {\n return $this->calculateFromAndToDatePeriod(\n $automatedReport->getFrequency(),\n $automatedReport->getFrom(),\n $automatedReport->getTo()\n );\n }\n\n public function getAskJiminnyGenerateReportPayload(\n AutomatedReport $automatedReport,\n AutomatedReportResult $reportResult,\n array $activityIds,\n ): array {\n return [\n 'user_question' => $automatedReport->getAskAnythingPrompt()?->getContent(),\n 'call_ids' => array_map('strval', $activityIds),\n 'team_id' => $automatedReport->getTeamId(),\n 'request_id' => $reportResult->getUuid(),\n 'callback_url' => $this->getCallbackUrl(),\n 'report_period' => $this->getReportPeriodName($reportResult),\n 'report_name' => $automatedReport->getCustomName(),\n ];\n }\n\n private function getCallbackUrl(): string\n {\n return $this->webhookService->route('jiminny.webhook.reports.ready');\n }\n\n /**\n * Validate and transform payload data for automated reports\n *\n * @param array $data\n *\n * @throws InvalidArgumentException\n *\n * @return array\n */\n private function validateAndTransformData(array $data): array\n {\n // Validate organization (team) and check feature\n $team = $this->validateOrganization($data['organization'] ?? null);\n\n $status = $this->validateReportStatus($data['report_enabled'] ?? null);\n $type = $this->validateReportType($data['report_type'] ?? null);\n $frequency = $this->validateFrequency($data['frequency'] ?? null);\n $additionalPromptInput = $this->validateAdditionalPromptInput(\n $data['additional_prompt_input'] ?? null\n );\n $customReportName = $this->validateCustomReportName($data['custom_name'] ?? null);\n\n // Prepare data for the database\n $reportData = [\n 'team_id' => $team->getId(),\n 'type' => $type,\n 'status' => $status,\n 'frequency' => $frequency,\n 'additional_prompt_input' => $additionalPromptInput,\n 'custom_name' => $customReportName,\n ];\n\n // Validate deal values\n $reportData = $this->validateDealValues($data, $reportData);\n\n // Validate date range\n $reportData = $this->validateDateRange($data, $reportData, $frequency);\n\n // Validate call durations\n $reportData = $this->validateCallDurations($data, $reportData);\n\n // Validate call types\n $reportData = $this->validateCallTypes($data, $reportData);\n\n // Validate media types\n $reportData = $this->validateMediaTypes($data, $reportData);\n\n // Validate groups\n if (isset($data['teams'])) {\n $reportData['groups'] = $this->validateAndGetGroupIds($team, $data['teams']);\n }\n\n // Validate deal stages\n $reportData = $this->validateDealStages($data, $reportData, $team, $type);\n\n // Validate playbook categories\n $reportData = $this->validatePlaybookCategories($data, $reportData, $team);\n\n // Validate recipients\n $reportData['recipients'] = [\n 'users' => $this->validateAndGetUserIdsByTeam($team, $data['recipients'] ?? []),\n ];\n\n if (isset($data['jiminny_recipients'])) {\n // Validate Jiminny recipients\n $reportData['jiminny_recipients'] = [\n 'users' => $this->validateAndGetJiminnyUserIds((array) $data['jiminny_recipients']),\n ];\n }\n\n return $reportData;\n }\n\n private function validateDealValues(array $data, array $reportData): array\n {\n if (isset($data['min_deal_value'])) {\n $reportData['deal_value_min'] = (int) $data['min_deal_value'];\n\n if ($reportData['deal_value_min'] > 4294967295 || $reportData['deal_value_min'] < 0) {\n throw new InvalidArgumentException('Min deal value should be between 0 and 4294967295');\n }\n }\n\n if (isset($data['max_deal_value'])) {\n $reportData['deal_value_max'] = (int) $data['max_deal_value'];\n\n if ($reportData['deal_value_max'] > 4294967295 || $reportData['deal_value_max'] < 0) {\n throw new InvalidArgumentException('Max deal value should be between 0 and 4294967295');\n }\n }\n\n if (isset($data['min_deal_value'], $data['max_deal_value'])\n && $data['min_deal_value'] > $data['max_deal_value']\n ) {\n throw new InvalidArgumentException('Min deal value cannot be greater than max deal value');\n }\n\n return $reportData;\n }\n\n private function validateDateRange(array $data, array $reportData, string $frequency): array\n {\n // Set date range only for one_off frequency\n if ($frequency === 'one_off') {\n if (isset($data['start_date_period'])) {\n $reportData['from'] = $this->parseDate($data['start_date_period']);\n }\n\n if (isset($data['end_date_period'])) {\n $reportData['to'] = $this->parseDate($data['end_date_period']);\n }\n\n if (empty($reportData['from']) || empty($reportData['to'])) {\n throw new InvalidArgumentException(\n 'Start date and end date are required for one_off frequency'\n );\n }\n } else {\n $reportData['from'] = null;\n $reportData['to'] = null;\n }\n\n return $reportData;\n }\n\n private function validateCallDurations(array $data, array $reportData): array\n {\n // Convert call durations from minutes to seconds\n if (isset($data['min_call_duration'])) {\n $reportData['call_duration_min'] = (int) $data['min_call_duration'] * 60;\n\n if ($reportData['call_duration_min'] > 4294967295 || $reportData['call_duration_min'] < 0) {\n throw new InvalidArgumentException('Min call duration should be between 0 and 4294967295');\n }\n }\n\n if (isset($data['max_call_duration'])) {\n $reportData['call_duration_max'] = (int) $data['max_call_duration'] * 60;\n\n if ($reportData['call_duration_max'] > 4294967295 || $reportData['call_duration_max'] < 0) {\n throw new InvalidArgumentException('Max call duration should be between 0 and 4294967295');\n }\n }\n\n return $reportData;\n }\n\n private function validateCallTypes(array $data, array $reportData): array\n {\n // Set call types\n $reportData['call_types'] = $data['call_type'] ?? [];\n if (empty($reportData['call_types'])) {\n $reportData['call_types'] = self::getCallTypes();\n }\n\n foreach ($reportData['call_types'] as $callType) {\n if (! in_array($callType, self::getCallTypes(), true)) {\n throw new InvalidArgumentException(sprintf('Call type %s is invalid', $callType));\n }\n }\n\n return $reportData;\n }\n\n private function validateMediaTypes(array $data, array $reportData): array\n {\n // Set media types from input data\n $reportData['media_types'] = $data['media_types'] ?? [];\n\n if (empty($reportData['media_types'])) {\n throw new InvalidArgumentException('Media types are required');\n }\n\n foreach ($reportData['media_types'] as $mediaType) {\n if (! in_array($mediaType, self::MEDIA_TYPES, true)) {\n throw new InvalidArgumentException(sprintf('Media type %s is invalid', $mediaType));\n }\n }\n\n return $reportData;\n }\n\n private function validateDealStages(array $data, array $reportData, Team $team, string $reportType): array\n {\n // Validate and set deal stages\n if (isset($data['deal_stage_at_call'])) {\n $reportData['deal_at_call_stages'] =\n $this->validateAndGetDealStageIds($team, $data['deal_stage_at_call'], 'Deal stage at call');\n }\n\n if (isset($data['current_deal_stage'])) {\n $reportData['current_deal_stages'] =\n $this->validateAndGetDealStageIds($team, $data['current_deal_stage'], 'Current deal stage');\n }\n\n // Ensure current_deal_stage is not provided for loss_analysis report type\n if ($reportType === self::TYPE_LOSS_ANALYSIS && ! empty($data['current_deal_stage'])) {\n throw new InvalidArgumentException('Current deal stage is not applicable for Loss Analysis reports');\n }\n\n return $reportData;\n }\n\n // transform uuid to id\n private function validatePlaybookCategories(array $data, array $reportData, Team $team): array\n {\n $key = 'playbook_categories';\n\n if (isset($data[$key])) {\n $payloadIds = $data[$key];\n $ids = [];\n\n foreach ($payloadIds as $uuid) {\n $uuid = (string) $uuid;\n\n try {\n $playbookCategory = $this->playbookCategoryRepository->findByUuid($uuid);\n } catch (Throwable $throwable) {\n Log::error(__METHOD__ . ' ' . $throwable->getMessage());\n\n throw new InvalidArgumentException(sprintf('Playbook category %s not found', $uuid));\n }\n\n if (! $playbookCategory) {\n throw new InvalidArgumentException(sprintf('Playbook category %s not found', $uuid));\n }\n\n if (! $playbookCategory->hasPlaybook()) {\n throw new InvalidArgumentException(sprintf('Playbook category %s has no playbook', $uuid));\n }\n\n if ($playbookCategory->getPlaybook()->getTeamId() !== $team->getId()) {\n throw new InvalidArgumentException(\n sprintf('Playbook category %s not found for team %s', $uuid, $team->getUuid())\n );\n }\n\n $ids[] = $playbookCategory->getId();\n }\n\n $reportData[$key] = $ids;\n }\n\n return $reportData;\n }\n\n private function validateReportStatus($status): bool\n {\n if (! in_array($status, [true, false], true)) {\n throw new InvalidArgumentException('Report status is invalid');\n }\n\n return $status;\n }\n\n private function validateReportType($type): string\n {\n if (! in_array($type, self::getTypes(), true)) {\n throw new InvalidArgumentException(sprintf('Report type is invalid: %s', $type));\n }\n\n return $type;\n }\n\n private function validateFrequency($frequency): string\n {\n if (! in_array($frequency, self::getFrequencies(), true)) {\n throw new InvalidArgumentException('Frequency is invalid');\n }\n\n return $frequency;\n }\n\n private function validateAdditionalPromptInput(?string $additionalPromptInput): ?string\n {\n if ($additionalPromptInput && strlen($additionalPromptInput) > 5000) {\n throw new InvalidArgumentException('Additional Prompt Input should be less than 5000 characters');\n }\n\n return $additionalPromptInput;\n }\n\n private function validateCustomReportName(?string $customReportName): ?string\n {\n if ($customReportName === null || $customReportName === '') {\n return null;\n }\n\n if (strlen($customReportName) > 70) {\n throw new InvalidArgumentException('Custom report name should be less than 70 characters');\n }\n\n return $customReportName;\n }\n\n private function validateOrganization(?string $organizationUuid): Team\n {\n if (! $organizationUuid) {\n throw new InvalidArgumentException('Organization is required');\n }\n\n $team = $this->teamRepository->idOrUuid($organizationUuid);\n\n if (! $team) {\n throw new InvalidArgumentException('Organization not found');\n }\n\n if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {\n throw new InvalidArgumentException('Organization does not have the Automated Reports feature');\n }\n\n return $team;\n }\n\n private function validateAndGetGroupIds(Team $team, array $teamUuids): array\n {\n $groupIds = [];\n\n foreach ($teamUuids as $uuid) {\n $group = $this->groupRepository->findByUuid($uuid);\n\n if ($group === null || $group->getTeamId() !== $team->getId()) {\n throw new InvalidArgumentException(\n sprintf('Group %s not found for team %s', $uuid, $team->getUuid())\n );\n }\n\n $groupIds[] = $group->getId();\n\n }\n\n return $groupIds;\n }\n\n private function validateAndGetDealStageIds(Team $team, array $stageUuids, string $propertyLabel): array\n {\n $stageIds = [];\n\n foreach ($stageUuids as $uuid) {\n $stage = $this->stageRepository->findByUuid($uuid);\n\n if ($stage === null || $stage->getTeamId() !== $team->getId()) {\n throw new InvalidArgumentException(\n sprintf('Stage %s not found for team %s for %s', $uuid, $team->getUuid(), $propertyLabel)\n );\n }\n\n $stageIds[] = $stage->getId();\n }\n\n return $stageIds;\n }\n\n private function validateAndGetUserIds(array $userUuids, callable $teamCheck): array\n {\n if (empty($userUuids)) {\n return [];\n }\n\n $userIds = [];\n\n foreach ($userUuids as $uuid) {\n $user = $this->userRepository->findByUuid($uuid);\n\n if (! $user || ! $user->isStatusActive()) {\n throw new InvalidArgumentException(\n sprintf('User %s not found or is not active', $uuid)\n );\n }\n\n if (! $teamCheck($user)) {\n throw new InvalidArgumentException(\n sprintf('User %s does not belong to the allowed team(s)', $uuid)\n );\n }\n\n $userIds[] = $user->getId();\n }\n\n return $userIds;\n }\n\n private function validateAndGetUserIdsByTeam(Team $team, array $userUuids): array\n {\n return $this->validateAndGetUserIds($userUuids, fn ($user) => $user->getTeamId() === $team->getId());\n }\n\n private function validateAndGetJiminnyUserIds(array $userUuids): array\n {\n $allowedTeamIds = config('kiosk.teamIds', []);\n\n return $this->validateAndGetUserIds($userUuids, fn ($user) => in_array($user->getTeamId(), $allowedTeamIds, true));\n }\n\n private function parseDate(string $dateString): string\n {\n return date('Y-m-d H:i:s', strtotime($dateString));\n }\n\n private function generateReportResultViewUrl(AutomatedReportResult $result): string\n {\n $mediaResource = $this->getReportMediaRouteResource($result);\n\n return route('ai-reports.' . $mediaResource . '.view', ['uuid' => $result->getUuid()]);\n }\n\n private function generateReportResultDownloadUrl(AutomatedReportResult $result): string\n {\n $mediaResource = $this->getReportMediaRouteResource($result);\n\n return route('ai-reports.' . $mediaResource . '.download', ['uuid' => $result->getUuid()]);\n }\n\n private function getReportMediaRouteResource(AutomatedReportResult $result): string\n {\n if ($result->getMediaType() === self::MEDIA_TYPE_PDF) {\n return self::PDF_KEY;\n } elseif ($result->getMediaType() === self::MEDIA_TYPE_PODCAST) {\n return self::AUDIO_KEY;\n }\n\n throw new \\InvalidArgumentException('Unknown media type.');\n }\n\n public function getMediaPath(AutomatedReportResult $result): ?string\n {\n $url = match ($result->getMediaType()) {\n self::MEDIA_TYPE_PDF => $result->getPdfUrl(),\n self::MEDIA_TYPE_PODCAST => $result->getPodcastAudioUrl(),\n default => null,\n };\n\n if ($url === null) {\n return null;\n }\n\n $path = parse_url(trim($url, '\"\\''), PHP_URL_PATH);\n\n return $path ?: null;\n }\n\n public function getFilenameSuffix(AutomatedReportResult $result): ?string\n {\n return match ($result->getMediaType()) {\n self::MEDIA_TYPE_PODCAST => 'Podcast',\n default => null,\n };\n }\n\n public function getMailSubjectSuffix(AutomatedReportResult $result): string\n {\n return match ($result->getMediaType()) {\n self::MEDIA_TYPE_PDF => 'report',\n self::MEDIA_TYPE_PODCAST => 'podcast',\n default => '',\n };\n }\n\n public function getMediaTypeMetadata(AutomatedReportResult $result): array\n {\n return match ($result->getMediaType()) {\n self::MEDIA_TYPE_PODCAST => ['extension' => 'mp3', 'mime' => 'audio/mpeg'],\n self::MEDIA_TYPE_PDF => ['extension' => 'pdf', 'mime' => 'application/pdf'],\n default => ['extension' => null, 'mime' => null],\n };\n }\n\n public function deleteS3Files(AutomatedReportResult $result): void\n {\n $teamUuid = $result->getReport()->getTeam()->getUuid();\n $reportUuid = $result->getUuid();\n\n // delete all files for a report uuid no mather of pdf, podcast, or both\n // in case of both - the podcast files are linked to the pdf (parent) uuid\n // pdf and podcast date times should be close\n $path = sprintf('%s/%s/%s', $teamUuid, self::S3_DIR, $reportUuid);\n\n foreach (self::FILE_EXTENSIONS_VARIANTS as $extension) {\n $file = $path . '.' . $extension;\n\n if (Storage::exists($file)) {\n Storage::delete($file);\n Log::info('[Reports] Deleted S3 file', [\n 'path' => $file,\n ]);\n }\n }\n\n foreach (self::FILE_PODCAST_EXTENSIONS_VARIANTS as $extension) {\n $file = $path . '_podcast.' . $extension;\n\n if (Storage::exists($file)) {\n Storage::delete($file);\n Log::info('[Reports] Deleted Podcast S3 file', [\n 'path' => $file,\n ]);\n }\n }\n }\n\n /**\n *\n * @param int|null $teamId Optional team ID to filter results\n *\n * @return Collection<int, int> Collection of team IDs\n */\n public function getTeamIdsWithReportsResults(?int $teamId = null): Collection\n {\n return $this->automatedReportsRepository->getTeamIdsWithReportsResults($teamId);\n }\n\n /**\n * Core delete logic for report results using a query\n *\n * @param Builder $query\n * @param array $logContext\n *\n * @return int\n */\n private function deleteReportResultsByQuery(Builder $query, array $logContext = []): int\n {\n $deletedCount = 0;\n\n if ($query->exists()) {\n Log::info(\n 'Run delete report results',\n array_merge(\n $logContext,\n [\n 'service' => 'AutomatedReportsService',\n ]\n )\n );\n\n $query->chunkById(50, function ($results) use (&$deletedCount, $logContext) {\n foreach ($results as $result) {\n $this->deleteReportResult($result);\n $deletedCount++;\n\n Log::info(\n 'Deleted a report result',\n array_merge(\n $logContext,\n [\n 'result_id' => $result->getId(),\n 'report_id' => $result->getReportId(),\n ]\n )\n );\n }\n });\n }\n\n return $deletedCount;\n }\n\n /**\n * Delete report results for a team by retention period\n *\n * @param Team $team\n * @param CarbonImmutable $retentionDate\n *\n * @return int Number of deleted report results\n */\n public function deleteReportsResultsInRetentionPeriod(Team $team, CarbonImmutable $retentionDate): int\n {\n $reportIds = $this->automatedReportsRepository->getReportIdsByTeam($team);\n\n if ($reportIds->isEmpty()) {\n return 0;\n }\n\n $query = $this->automatedReportsRepository\n ->getReportResultsQueryForRetention($team, $retentionDate);\n\n return $this->deleteReportResultsByQuery($query, [\n 'team_id' => $team->getId(),\n 'retention_date' => $retentionDate->toDateTimeString(),\n ]);\n }\n\n /**\n * Delete ALL report results for a specific automated report\n *\n * @param string $uuid\n *\n * @return int\n */\n public function deleteReportResults(string $uuid): int\n {\n $report = $this->getReport($uuid);\n\n $query = $this->automatedReportsRepository->getResultsByReportQuery($report);\n\n return $this->deleteReportResultsByQuery($query, [\n 'report_uuid' => $uuid,\n 'report_id' => $report->getId(),\n ]);\n }\n\n public function deleteReportResult(AutomatedReportResult $result): void\n {\n $this->deleteS3Files($result);\n\n $result->delete();\n }\n\n /**\n * Get all reports for a specific team\n *\n * @param Team $team\n *\n * @return \\Illuminate\\Database\\Eloquent\\Collection\n */\n public function getTeamReports(Team $team): \\Illuminate\\Database\\Eloquent\\Collection\n {\n return $this->automatedReportsRepository->getReportsByTeam($team);\n }\n\n /**\n * Get all report results for a specific report\n *\n * @param AutomatedReport $report\n *\n * @return \\Illuminate\\Database\\Eloquent\\Collection\n */\n public function getReportResults(AutomatedReport $report): \\Illuminate\\Database\\Eloquent\\Collection\n {\n return $this->automatedReportsRepository->getResultsByReport($report);\n }\n\n public function deleteAllReportResults(AutomatedReport $report): void\n {\n $results = $this->getReportResults($report);\n\n /** @var AutomatedReportResult $result */\n foreach ($results as $result) {\n Log::info('Deleting result', [\n 'report' => $report->getId(),\n 'result' => $result->getId(),\n ]);\n\n $this->deleteReportResult($result);\n }\n }\n\n public function deleteAllData(Team $team): void\n {\n Log::info('Deleting automated report and results for team', [\n 'team' => $team->getId(),\n ]);\n\n $reports = $this->getTeamReports($team);\n\n /** @var AutomatedReport $report */\n foreach ($reports as $report) {\n Log::info('Deleting report', [\n 'team' => $team->getId(),\n 'report' => $report->getId(),\n ]);\n\n $this->deleteAllReportResults($report);\n\n $report->delete();\n }\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Kiosk\\AutomatedReports;\n\nuse Carbon\\CarbonImmutable;\nuse Carbon\\CarbonInterface;\nuse Carbon\\Exceptions\\InvalidFormatException;\nuse DateTime;\nuse DateTimeInterface;\nuse DateTimeZone;\nuse Illuminate\\Contracts\\Bus\\Dispatcher as BusDispatcher;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Support\\Carbon;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\Log;\nuse Illuminate\\Support\\Facades\\Storage;\nuse Jiminny\\Component\\ActivitySearch\\FilterDefinition\\InputTypeEnum;\nuse Jiminny\\Component\\AskAnything\\AskAnythingPromptService;\nuse Jiminny\\Component\\AskAnything\\Dtos\\AskAnythingPromptDto;\nuse Jiminny\\Component\\UrlGenerator\\Webhook;\nuse Jiminny\\Contracts\\Repositories\\PlaybookCategoryRepository;\nuse Jiminny\\Contracts\\Repositories\\TeamRepository;\nuse Jiminny\\Contracts\\Repositories\\UserRepository;\nuse Jiminny\\Exceptions\\ApplicationException;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Exceptions\\ModelNotFoundException;\nuse Jiminny\\Jobs\\AutomatedReports\\RequestGenerateReportJob;\nuse Jiminny\\Models\\Activity\\Search;\nuse Jiminny\\Models\\AskAnything\\AskAnythingPrompt;\nuse Jiminny\\Models\\AskAnything\\AskAnythingPromptTarget;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AskAnythingRepository;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\GroupRepository;\nuse Jiminny\\Repositories\\SearchRepository;\nuse Jiminny\\Repositories\\StageRepository;\nuse Throwable;\n\nclass AutomatedReportsService\n{\n public const string TYPE_LOSS_ANALYSIS = 'loss_analysis';\n public const string TYPE_ASK_JIMINNY = 'ask_jiminny';\n\n /**\n * Standard report types (used by kiosk for existing automated reports).\n */\n // @TODO this will add filter, however if we need to control feature by FF we need conditional logic\n public const array TYPES = [\n ['id' => 'exec_summary', 'name' => 'Exec Summary'],\n ['id' => 'coaching_profiles', 'name' => 'Coaching Profiles'],\n ['id' => 'product_feedback', 'name' => 'Product Feedback'],\n ['id' => self::TYPE_LOSS_ANALYSIS, 'name' => 'Loss Analysis'],\n// ['id' => 'questions', 'name' => 'Questions'],\n// ['id' => 'statistical_quant', 'name' => 'Statistical Quantitative'],\n ];\n\n public const array ALL_TYPES = [\n ...self::TYPES,\n ['id' => self::TYPE_ASK_JIMINNY, 'name' => 'Ask Jiminny'],\n ];\n\n public const string FREQUENCY_DAILY = 'daily';\n public const string FREQUENCY_WEEKLY = 'weekly';\n public const string FREQUENCY_MONTHLY = 'monthly';\n public const string FREQUENCY_QUARTERLY = 'quarterly';\n public const string FREQUENCY_ONE_OFF = 'one_off';\n\n /**\n * Frequencies for standard (non-Ask Jiminny) reports.\n */\n public const array FREQUENCIES = [\n ['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],\n ['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],\n ['id' => self::FREQUENCY_QUARTERLY, 'name' => 'Quarterly'],\n ['id' => self::FREQUENCY_ONE_OFF, 'name' => 'One-off'],\n ];\n\n /**\n * Frequencies for Ask Jiminny reports.\n */\n public const array ASK_JIMINNY_FREQUENCIES = [\n ['id' => self::FREQUENCY_DAILY, 'name' => 'Daily'],\n ['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],\n ['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],\n ];\n\n public const string MEDIA_TYPE_PDF = 'pdf';\n public const string MEDIA_TYPE_PODCAST = 'podcast';\n public const array MEDIA_TYPES = [self::MEDIA_TYPE_PDF, self::MEDIA_TYPE_PODCAST];\n public const array MEDIA_TYPE_OBJECT_PDF = ['id' => self::MEDIA_TYPE_PDF, 'name' => 'PDF'];\n public const array MEDIA_TYPE_OBJECT_PODCAST = ['id' => self::MEDIA_TYPE_PODCAST, 'name' => 'Podcast'];\n public const array MEDIA_TYPE_OBJECTS = [self::MEDIA_TYPE_OBJECT_PDF, self::MEDIA_TYPE_OBJECT_PODCAST];\n\n public const array CALL_TYPE_CONFERENCE = ['id' => 'conference', 'name' => 'Conference'];\n public const array CALL_TYPE_DIALER = ['id' => 'dialer', 'name' => 'Dialer'];\n public const int SENT_REPORT_AT_HOURS = 5;\n public const string PDF_KEY = 'pdf';\n public const string AUDIO_KEY = 'audio';\n\n private const array ALL_FREQUENCIES = [\n ['id' => self::FREQUENCY_DAILY, 'name' => 'Daily'],\n ['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],\n ['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],\n ['id' => self::FREQUENCY_QUARTERLY, 'name' => 'Quarterly'],\n ['id' => self::FREQUENCY_ONE_OFF, 'name' => 'One-off'],\n ];\n private const string S3_DIR = 'reports';\n private const array FILE_EXTENSIONS_VARIANTS = ['html', 'MD', 'pdf'];\n private const array FILE_PODCAST_EXTENSIONS_VARIANTS = ['json', 'mp3', 'ssml'];\n\n public function __construct(\n private readonly TeamRepository $teamRepository,\n private readonly GroupRepository $groupRepository,\n private readonly UserRepository $userRepository,\n private readonly StageRepository $stageRepository,\n private readonly DealStagesService $dealStagesService,\n private readonly RecipientsService $recipientsService,\n private readonly AutomatedReportsRepository $automatedReportsRepository,\n private readonly Webhook $webhookService,\n private readonly BusDispatcher $dispatcher,\n private readonly ActivityTypeService $activityTypeService,\n private readonly PlaybookCategoryRepository $playbookCategoryRepository,\n private readonly AskAnythingPromptService $askAnythingPromptService,\n private readonly SearchRepository $activitySearchRepository,\n private readonly AskAnythingRepository $askAnythingRepository,\n ) {\n }\n\n public static function getTypes(): array\n {\n $types = self::TYPES;\n\n return array_map(static function ($type) {\n return $type['id'];\n }, $types);\n }\n\n public static function getCallTypes(): array\n {\n return array_map(static function ($callType) {\n return $callType['id'];\n }, [self::CALL_TYPE_CONFERENCE, self::CALL_TYPE_DIALER]);\n }\n\n public static function getFrequencies(): array\n {\n return array_map(static function ($frequency) {\n return $frequency['id'];\n }, self::FREQUENCIES);\n }\n\n // front-facing structure\n public function getReportEnabledFieldData(bool $value = false): array\n {\n return [\n 'id' => 'report_enabled',\n 'label' => '',\n 'inputType' => InputTypeEnum::TOGGLE,\n 'value' => $value,\n ];\n }\n\n // Organizations = Teams\n public function getOrganizationFieldData(?string $value = null, bool $shortVersion = false): array\n {\n $options = $this->getTeams();\n\n if ($shortVersion) {\n return [\n 'id' => 'organization',\n 'label' => 'Organization',\n 'options' => $options,\n ];\n }\n\n return [\n 'id' => 'organization',\n 'label' => 'Organization',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => $options,\n 'value' => $value,\n 'dependencies' => [\n 'teams',\n 'deal_stage_at_call',\n 'current_deal_stage',\n 'recipients',\n ActivityTypeService::PLAYBOOK_CATEGORIES_KEY,\n ],\n 'dependsOn' => [],\n ];\n }\n\n // Teams = Groups\n public function getTeamFieldData(array $options = [], array $value = [], bool $shortVersion = false): array\n {\n if ($shortVersion) {\n return [\n 'id' => 'teams',\n 'label' => 'Team',\n 'options' => $options,\n ];\n }\n\n return [\n 'id' => 'teams',\n 'label' => 'Team',\n 'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,\n 'required' => false,\n 'placeholder' => 'Select',\n 'options' => $options,\n 'value' => $value, // value should be an array of objects {id, name}\n 'dependencies' => [ActivityTypeService::PLAYBOOK_CATEGORIES_KEY],\n 'dependsOn' => [],\n ];\n }\n\n public function getReportTypeFieldData(?string $value = null, bool $shortVersion = false, ?Team $team = null): array\n {\n $types = [];\n if ($team instanceof Team) {\n if ($team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {\n $types = self::TYPES;\n }\n if ($team->hasFeature(FeatureEnum::ASK_JIMINNY_REPORTS)) {\n $types[] = ['id' => self::TYPE_ASK_JIMINNY, 'name' => 'Ask Jiminny'];\n }\n } else {\n $types = self::TYPES;\n }\n\n if ($shortVersion) {\n return [\n 'id' => 'report_type',\n 'label' => 'Report Type',\n 'options' => $types,\n ];\n }\n\n return [\n 'id' => 'report_type',\n 'label' => 'Report Type',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => $types,\n 'value' => $value,\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getFrequencyFieldData(?string $value = null): array\n {\n return [\n 'id' => 'frequency',\n 'label' => 'Frequency',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => self::FREQUENCIES,\n 'value' => $value,\n 'dependencies' => ['period'],\n 'dependsOn' => [],\n ];\n }\n\n public function getPeriodFieldData(?string $valueStartDate = null, ?string $valueEndDate = null): array\n {\n return [\n 'id' => 'period',\n 'label' => 'Select one-off period',\n 'inputType' => InputTypeEnum::DATE_RANGE,\n 'required' => true,\n 'placeholder' => 'Select',\n 'value' => ['startDate' => $valueStartDate, 'endDate' => $valueEndDate],\n 'queryParams' => [\n 'startDate' => 'start_date_period',\n 'endDate' => 'end_date_period',\n ],\n 'dependencies' => [],\n 'dependsOn' => ['frequency'],\n ];\n }\n\n public function getActivityTypesFieldData(?Team $team = null, array $value = [], array $teamsFilter = []): array\n {\n return $this->activityTypeService->getActivityTypeFieldData(team: $team, value: $value, groupIds: $teamsFilter);\n }\n\n public function getDealStageAtCallFieldData(?Team $team = null, array $value = []): array\n {\n return $this->dealStagesService->getDealStageAtCallFieldData(team: $team, value: $value);\n }\n\n public function getCurrentDealStageFieldData(?Team $team = null, array $value = []): array\n {\n return $this->dealStagesService->getCurrentDealStageFieldData(team: $team, value: $value);\n }\n\n public function getDealValueFieldData(?int $valueMin = null, ?int $valueMax = null): array\n {\n return [\n 'id' => 'deal_value',\n 'label' => 'Deal Value',\n 'inputType' => InputTypeEnum::INTEGER_RANGE,\n 'required' => false,\n 'value' => ['min' => $valueMin, 'max' => $valueMax],\n 'queryParams' => [\n 'min' => 'min_deal_value',\n 'max' => 'max_deal_value',\n ],\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getCallTypeFieldData(bool $conferenceOn = false, bool $dialerOn = false): array\n {\n $value = [];\n $conferenceOn && $value[] = self::CALL_TYPE_CONFERENCE;\n $dialerOn && $value[] = self::CALL_TYPE_DIALER;\n\n return [\n 'id' => 'call_type',\n 'label' => 'Call Type',\n 'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,\n 'required' => true,\n 'options' => [\n self::CALL_TYPE_CONFERENCE,\n self::CALL_TYPE_DIALER,\n ],\n 'value' => $value,\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getMediaTypeFieldData(?AutomatedReport $report = null): array\n {\n $value = [];\n\n if ($report) {\n $value = $this->transformMediaTypes($report);\n }\n\n return [\n 'id' => 'media_types',\n 'label' => 'Export as',\n 'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,\n 'required' => true,\n 'options' => self::MEDIA_TYPE_OBJECTS,\n 'value' => $value,\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getCallDurationFieldData(?int $valueMin = null, ?int $valueMax = null): array\n {\n return [\n 'id' => 'call_duration',\n 'label' => 'Call Duration',\n 'inputType' => InputTypeEnum::INTEGER_RANGE,\n 'required' => false,\n 'value' => ['min' => $valueMin, 'max' => $valueMax],\n 'queryParams' => [\n 'min' => 'min_call_duration',\n 'max' => 'max_call_duration',\n ],\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getRecipientsFieldData(?Team $team = null, array $value = []): array\n {\n return $this->recipientsService->getRecipientsFieldData(team: $team, value: $value);\n }\n\n public function getJiminnyRecipientsFieldData(array $value = []): array\n {\n return $this->recipientsService->getJiminnyRecipientsFieldData($value);\n }\n\n public function getAdditionalPromptInputFieldData(?string $value = null): array\n {\n return [\n 'id' => 'additional_prompt_input',\n 'label' => 'Special requirements',\n 'inputType' => InputTypeEnum::TEXTAREA,\n 'required' => false,\n 'placeholder' => 'What should be the focus of the report?',\n 'value' => $value,\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n public function getCustomReportNameFieldData(?string $value = null): array\n {\n return [\n 'id' => 'custom_name',\n 'label' => 'Custom report name',\n 'inputType' => InputTypeEnum::TEXT,\n 'required' => false,\n 'placeholder' => 'Enter custom name',\n 'value' => $value,\n 'dependencies' => [],\n 'dependsOn' => [],\n ];\n }\n\n // data providers\n public function getTeams(): array\n {\n $teams = $this->teamRepository->getTeamsForKiosk(status: Team::STATUS_ACTIVE);\n\n $teamData = [];\n foreach ($teams as $team) {\n if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {\n continue;\n }\n\n $teamData[] = $this->transformTeam($team);\n }\n\n return $teamData;\n }\n\n public function getTeamGroups(string $teamUuid): array\n {\n $data = [];\n $team = $this->getTeam($teamUuid);\n\n if ($team !== null) {\n $groups = $team->groups()->get();\n\n foreach ($groups as $group) {\n $data[] = [\n 'id' => $group->getUuid(),\n 'name' => $group->getName(),\n ];\n }\n }\n\n return $data;\n }\n\n public function getTeamsGroupsOptions(array $filterTeamUuids = []): array\n {\n $data = [];\n $teams = $this->getTeams();\n\n foreach ($teams as $team) {\n if (! empty($filterTeamUuids) && ! in_array($team['id'], $filterTeamUuids, true)) {\n continue;\n }\n\n $data[] = [\n 'label' => $team['name'],\n 'groups' => $this->getTeamGroups($team['id']),\n ];\n }\n\n return $data;\n }\n\n public function getTeam(string $teamUuid): ?Team\n {\n return $this->teamRepository->idOrUuid($teamUuid);\n }\n\n public function getTeamById(int $teamId): ?Team\n {\n return $this->teamRepository->find($teamId);\n }\n\n public function getGroupsUuids(AutomatedReport $report): array\n {\n $uuids = [];\n $reportGroups = $report->getGroups();\n foreach ($reportGroups as $groupId) {\n if ($group = $this->groupRepository->find($groupId)) {\n $uuids[] = $group->getUuid();\n }\n }\n\n return $uuids;\n }\n\n public function getPlaybookCategoriesUuids(AutomatedReport $report): array\n {\n $uuids = [];\n $playbookCategories = $report->getPlaybookCategories();\n foreach ($playbookCategories as $id) {\n if ($category = $this->playbookCategoryRepository->find($id)) {\n $uuids[] = $category->getUuid();\n }\n }\n\n return $uuids;\n }\n\n public function getDealAtCallStagesUuids(AutomatedReport $report): array\n {\n $uuids = [];\n $reportStages = $report->getDealAtCallStages();\n foreach ($reportStages as $id) {\n if ($stage = $this->stageRepository->find($id)) {\n $uuids[] = $stage->getUuid();\n }\n }\n\n return $uuids;\n }\n\n public function getCurrentDealStagesUuids(AutomatedReport $report): array\n {\n $uuids = [];\n $reportStages = $report->getCurrentDealStages();\n foreach ($reportStages as $id) {\n if ($stage = $this->stageRepository->find($id)) {\n $uuids[] = $stage->getUuid();\n }\n }\n\n return $uuids;\n }\n\n public function getUsersUuids(AutomatedReport $report): array\n {\n return $this->extractUserUuids($report->getRecipients());\n }\n\n public function getJiminnyUsersUuids(AutomatedReport $report): array\n {\n return $this->extractUserUuids($report->getJiminnyRecipients());\n }\n\n /**\n * @param array<string, mixed> $recipients\n */\n private function extractUserUuids(array $recipients): array\n {\n $userIds = $recipients['users'] ?? [];\n\n return collect($userIds)\n ->map(fn ($id) => $this->userRepository->find((int) $id))\n ->filter()\n ->map(fn (UserContract $user) => $user->getUuid())\n ->values()\n ->all();\n }\n\n // get mail data\n public function getRecipientUsers(AutomatedReport $report): array\n {\n return $this->buildRecipientUsers($report->getRecipients());\n }\n\n /**\n * @return array<UserContract>\n */\n public function getRecipientUserObjects(AutomatedReport $report): array\n {\n $userIds = $report->getRecipients()['users'] ?? [];\n\n return collect($userIds)\n ->map(fn ($id) => $this->userRepository->find((int) $id))\n ->filter()\n ->values()\n ->all();\n }\n\n private function getJiminnyRecipientUsers(AutomatedReport $report): array\n {\n return $this->buildRecipientUsers($report->getJiminnyRecipients());\n }\n\n /**\n * @param array<string, mixed> $recipients\n */\n private function buildRecipientUsers(array $recipients): array\n {\n $userIds = $recipients['users'] ?? [];\n\n return collect($userIds)\n ->map(fn ($id) => $this->userRepository->find((int) $id))\n ->filter()\n ->map(fn (UserContract $user) => [\n 'email' => $user->getEmailAddress(),\n 'name' => $user->getName(),\n 'timezone' => $user->getTimezone()->getName(),\n ])\n ->values()\n ->all();\n }\n\n public function getValidRecipientUsers(AutomatedReport $report, bool $includeJiminny = false): array\n {\n if ($report->isAskJiminnyReport()) {\n $recipients = $this->resolveAskJiminnyRecipients($report);\n } else {\n $recipients = $this->getRecipientUsers($report);\n if ($includeJiminny) {\n $recipients = array_merge($recipients, $this->getJiminnyRecipientUsers($report));\n }\n }\n\n $emails = [];\n\n return array_values(array_filter(\n $recipients,\n static function ($recipient) use (&$emails) {\n if (empty($recipient['email']) || in_array($recipient['email'], $emails, true)) {\n return false;\n }\n $emails[] = $recipient['email'];\n\n return true;\n }\n ));\n }\n\n private function resolveAskJiminnyRecipients(AutomatedReport $report): array\n {\n $recipients = [];\n\n $creator = $report->getCreator();\n if ($creator !== null) {\n $recipients[] = [\n 'email' => $creator->getEmailAddress(),\n 'name' => $creator->getName(),\n 'timezone' => $creator->getTimezone()->getName(),\n ];\n }\n\n return array_merge(\n $recipients,\n $this->buildRecipientUsers($report->getRecipients()),\n $this->getGroupRecipientUsers($report),\n );\n }\n\n private function getGroupRecipientUsers(AutomatedReport $report): array\n {\n $users = [];\n foreach ($report->getGroups() as $groupId) {\n $group = $this->groupRepository->find($groupId);\n if ($group === null) {\n continue;\n }\n foreach ($group->getMembers() as $member) {\n $users[] = [\n 'email' => $member->getEmailAddress(),\n 'name' => $member->getName(),\n 'timezone' => $member->getTimezone()->getName(),\n ];\n }\n }\n\n return $users;\n }\n\n public function getReportTypeName(AutomatedReportResult $report): string\n {\n $type = $report->getReport()->getType();\n\n $getType = $this->transformReportType($type);\n\n return $getType['name'];\n }\n\n public function getReportPeriodName(AutomatedReportResult $report): string\n {\n $from = $report->getFromDate();\n $to = $report->getToDate();\n $frequency = $report->getReport()->getFrequency();\n\n if ($from === null || $to === null) {\n if (! $report->getReport()->isAskJiminnyReport()) {\n $invalidPeriod = $from === null ? 'from' : 'to';\n\n throw new ApplicationException('Report period is invalid: ' . $invalidPeriod);\n }\n\n $period = $this->calculateFromAndToDatePeriod($frequency);\n $from = $period['fromDate'];\n $to = $period['toDate'];\n }\n\n return $this->formatReportPeriodName($frequency, $from, $to);\n }\n\n private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string\n {\n $fromYear = $from->format('Y');\n $toYear = $to->format('Y');\n $differentYears = $fromYear !== $toYear;\n\n switch ($frequency) {\n case self::FREQUENCY_DAILY:\n return $from->format('j M Y');\n\n case self::FREQUENCY_QUARTERLY:\n // 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ\n $startMonth = $from->format('M');\n $endMonth = $to->copy()->subMonth();\n $endMonthName = $endMonth->format('M');\n $endMonthYear = $endMonth->format('Y');\n\n if ($differentYears) {\n return \"{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}\";\n }\n\n return \"{$startMonth} - {$endMonthName} {$toYear}\";\n\n case self::FREQUENCY_MONTHLY:\n // 'May 2025' - monthly reports are always within the same year\n return $from->format('M Y');\n\n case self::FREQUENCY_WEEKLY:\n // '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ\n $startDay = $from->format('j');\n $endDay = $to->format('j');\n $startMonth = $from->format('M');\n $endMonth = $to->format('M');\n\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n if ($startMonth !== $endMonth) {\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n return \"{$startDay} - {$endDay} {$endMonth} {$toYear}\";\n\n case self::FREQUENCY_ONE_OFF:\n // '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ\n $startDay = $from->format('j');\n $startMonth = $from->format('M');\n $endDay = $to->format('j');\n $endMonth = $to->format('M');\n\n // If same month and year, use a format like '2-31 May 2025'\n if ($startMonth === $endMonth && ! $differentYears) {\n return \"{$startDay} - {$endDay} {$startMonth} {$toYear}\";\n }\n\n // If different years, include both years\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n // Same year but different months\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n\n default:\n // Default format for unknown frequencies\n return $from->format('j M Y') . ' - ' . $to->format('j M Y');\n }\n }\n\n public function getReportTeamsName(AutomatedReportResult $report): string\n {\n $groups = $report->getGroups();\n\n if (empty($groups)) {\n return 'All';\n }\n\n // Get group names from repository\n $groupNames = [];\n foreach ($groups as $groupId) {\n $group = $this->groupRepository->find($groupId);\n if ($group) {\n $groupNames[] = $group->getName();\n }\n }\n\n if (count($groupNames) === 1) {\n // Single team format\n $teamsName = $groupNames[0];\n } else {\n // Multiple teams format\n $teamsName = implode(', ', $groupNames);\n }\n\n return $teamsName;\n }\n\n public function getReportFileName(AutomatedReportResult $report): string\n {\n $customName = $report->getReport()->getCustomName();\n $periodName = $this->getReportPeriodName($report);\n $filenameSuffix = $this->getFilenameSuffix($report);\n\n if ($customName) {\n if ($filenameSuffix) {\n $customName .= \" {$filenameSuffix}\";\n }\n\n return $this->sanitizeFileName(\"{$customName} - {$periodName}\");\n }\n\n $baseName = $this->getReportTypeName($report);\n\n if ($filenameSuffix) {\n $baseName .= \" {$filenameSuffix}\";\n }\n\n return $this->sanitizeFileName(\"{$baseName} - {$periodName} - {$this->getReportTeamsName($report)}\");\n }\n\n public function getReportFileNameWithExtension(AutomatedReportResult $result): string\n {\n $extension = $this->getMediaTypeMetadata($result)['extension'];\n\n return $this->getReportFileName($result) . '.' . $extension;\n }\n\n public function sanitizeFileName(string $fileName): string\n {\n return str_replace(['/', '\\\\'], '-', $fileName);\n }\n\n public function isUserRecipientOfReport(User $user, AutomatedReport $report): bool\n {\n $recipients = array_map('intval', $report->getRecipients()['users'] ?? []);\n\n return in_array($user->getId(), $recipients);\n }\n\n public function transformReportResults(Collection $automatedReportResults): array\n {\n $data = [];\n foreach ($automatedReportResults as $automatedReportResult) {\n /** @var AutomatedReportResult $automatedReportResult */\n\n $report = $automatedReportResult->getReport();\n\n $createdBy = $report->getCreator();\n $creator = [\n 'id' => $createdBy?->getUuid(),\n 'name' => $createdBy?->getName(),\n 'email' => $createdBy?->getEmailAddress(),\n 'photoUrl' => $createdBy?->getPhotoUrl(),\n ];\n\n $recipients = $this->buildRecipients($report);\n\n $data[] = [\n 'id' => $automatedReportResult->getUuid(),\n 'name' => $automatedReportResult->getName(),\n 'frequency' => $this->transformFrequency($report->getFrequency()),\n 'recipients' => [\n ...array_values($this->transformGroups(team: $report->getTeam(), groupsIds: $report->getGroups())),\n ...array_values($this->transformRecipients($report->getRecipients())),\n ],\n 'report_type' => $this->transformReportType($report->getType()),\n 'media_type' => $automatedReportResult->getMediaType(),\n 'downloadUrl' => $this->generateReportResultDownloadUrl($automatedReportResult),\n 'viewUrl' => $this->generateReportResultViewUrl($automatedReportResult),\n 'generated_at' => $automatedReportResult->getGeneratedAt()?->toIso8601String(),\n 'creator' => $creator,\n ];\n }\n\n return $data;\n }\n\n public function hasCallTypeConference(AutomatedReport $report): bool\n {\n return in_array(self::CALL_TYPE_CONFERENCE['id'], $report->getCallTypes(), true);\n }\n\n public function hasCallTypeDialer(AutomatedReport $report): bool\n {\n return in_array(self::CALL_TYPE_DIALER['id'], $report->getCallTypes(), true);\n }\n\n // transformers\n private function transformTeam(Team $team): array\n {\n if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {\n return [];\n }\n\n return [\n 'id' => $team->getUuid(),\n 'name' => $team->getName(),\n ];\n }\n\n private function transformReportFullView(AutomatedReport $report): array\n {\n $base = $this->transformReportBase($report);\n\n return $report->getType() === self::TYPE_ASK_JIMINNY\n ? $base + $this->transformAskJiminnyFields($report)\n : $base + $this->transformStandardReportFields($report);\n }\n\n private function transformReportBase(AutomatedReport $report): array\n {\n return [\n 'id' => $report->getUuid(),\n 'organization' => $this->transformOrganization(team: $report->getTeam()),\n 'report_type' => $this->transformReportType($report->getType()),\n 'frequency' => $this->transformFrequency($report->getFrequency()),\n ];\n }\n\n private function transformStandardReportFields(AutomatedReport $report): array\n {\n $team = $report->getTeam();\n\n return [\n 'report_enabled' => $report->getStatus(),\n 'start_date_period' => $report->getFrom()?->format('Y-m-d H:i:s'),\n 'end_date_period' => $report->getTo()?->format('Y-m-d H:i:s'),\n 'deal_value_min' => $report->getDealValueMin(),\n 'deal_value_max' => $report->getDealValueMax(),\n 'call_types' => $this->transformCallType($report->getCallTypes()),\n 'media_types' => $this->transformMediaTypes($report),\n 'call_duration_min' => $this->transformDurationToMinutes($report->getCallDurationMin()),\n 'call_duration_max' => $this->transformDurationToMinutes($report->getCallDurationMax()),\n 'teams' => $this->transformGroups(team: $team, groupsIds: $report->getGroups()),\n 'deal_at_call_stages' => $this->transformStages(team: $team, stagesIds: $report->getDealAtCallStages()),\n 'current_deal_stages' => $this->transformStages(team: $team, stagesIds: $report->getCurrentDealStages()),\n 'recipients' => $this->transformRecipients($report->getRecipients()),\n 'created_by' => $this->transformCreator($report->getCreator()),\n 'additional_prompt_input' => $report->getAdditionalPromptInput(),\n 'custom_name' => $report->getCustomName(),\n 'created_at' => $report->getCreatedAt()->format('Y-m-d H:i:s'),\n 'updated_at' => $report->getUpdatedAt()->format('Y-m-d H:i:s'),\n 'deleted_at' => $report->getDeletedAt()?->format('Y-m-d H:i:s'),\n ];\n }\n\n private function transformAskJiminnyFields(AutomatedReport $report): array\n {\n $team = $report->getTeam();\n $creatorId = $report->getAttribute('created_by');\n $explicitUserIds = array_values(array_filter(\n $report->getRecipients()['users'] ?? [],\n static fn ($id) => $id !== $creatorId\n ));\n\n return [\n 'report_name' => $report->getCustomName(),\n 'enabled' => $report->getStatus(),\n 'share_teams' => $this->transformGroups(team: $team, groupsIds: $report->getGroups()),\n 'share_users' => $this->transformRecipients(['users' => $explicitUserIds]),\n 'saved_search' => $this->transformSafeSearch($report->getSavedSearch()),\n 'ask_jiminny_prompt' => $this->transformAskJiminnyPrompt($report->getAskAnythingPrompt()),\n 'expires_on' => $report->getExpiresAt()?->format('Y-m-d'),\n ];\n }\n\n private function transformOrganization(?Team $team): array\n {\n return [\n 'id' => $team?->getUuid(),\n 'name' => $team?->getName(),\n ];\n }\n\n private function transformReportType(string $type): array\n {\n foreach (self::ALL_TYPES as $typeItem) {\n if ($typeItem['id'] === $type) {\n return $typeItem;\n }\n }\n\n return [];\n }\n\n private function transformCallType(array $types): array\n {\n $result = [];\n $callTypes = [self::CALL_TYPE_CONFERENCE, self::CALL_TYPE_DIALER];\n\n foreach ($types as $type) {\n foreach ($callTypes as $callTypeItem) {\n if ($callTypeItem['id'] === $type) {\n $result[] = $callTypeItem;\n\n break;\n }\n }\n }\n\n return $result;\n }\n\n private function transformMediaTypes(AutomatedReport $report): array\n {\n $values = [];\n\n foreach ($report->getMediaTypes() as $mediaType) {\n if (! in_array($mediaType, self::MEDIA_TYPES, true)) {\n continue;\n }\n\n $values[] = match ($mediaType) {\n self::MEDIA_TYPE_PDF => self::MEDIA_TYPE_OBJECT_PDF,\n self::MEDIA_TYPE_PODCAST => self::MEDIA_TYPE_OBJECT_PODCAST,\n };\n }\n\n return $values;\n }\n\n private function transformFrequency(string $frequency): array\n {\n foreach (self::ALL_FREQUENCIES as $frequencyItem) {\n if ($frequencyItem['id'] === $frequency) {\n return $frequencyItem;\n }\n }\n\n return [];\n }\n\n public function transformDurationToMinutes(?int $duration): ?int\n {\n if (! $duration) {\n return null;\n }\n\n return (int) ($duration / 60);\n }\n\n private function transformGroups(?Team $team, array $groupsIds): array\n {\n if (empty($groupsIds) || ! $team) {\n return [];\n }\n\n $data = [];\n foreach ($groupsIds as $groupId) {\n $group = $team->groups()->where('id', $groupId)->first();\n\n if ($group) {\n $data[] = [\n 'id' => $group->getUuid(),\n 'name' => $group->getName(),\n 'photoUrl' => $group->getPhotoUrl(),\n ];\n }\n }\n\n return $data;\n }\n\n private function transformStages(?Team $team, array $stagesIds): array\n {\n if (empty($stagesIds) || ! $team) {\n return [];\n }\n\n $data = [];\n foreach ($stagesIds as $stageId) {\n $stage = $team->stages()->where('id', $stageId)->first();\n\n if ($stage) {\n $data[] = [\n 'id' => $stage->getUuid(),\n 'name' => $stage->getName(),\n ];\n }\n }\n\n return $data;\n }\n\n private function transformRecipients(array $recipients): array\n {\n $users = [];\n foreach ($recipients['users'] ?? [] as $userId) {\n $users[] = $this->transformUser($userId);\n }\n\n return $users;\n }\n\n private function transformCreator(?User $user): ?array\n {\n if ($user === null) {\n return null;\n }\n\n return $this->transformUser($user->getId());\n }\n\n private function transformAskJiminnyPrompt(?AskAnythingPrompt $prompt): ?array\n {\n if ($prompt === null) {\n return null;\n }\n\n return [\n 'id' => $prompt->getUuid(),\n 'name' => $prompt->getTitle(),\n ];\n }\n\n private function transformSafeSearch(?Search $search): ?array\n {\n if ($search === null) {\n return null;\n }\n\n return [\n 'id' => $search->getUuid(),\n 'name' => $search->getName(),\n ];\n }\n\n private function transformUser(int $userId): array\n {\n /* @var ?User $user */\n $user = $this->userRepository->find($userId);\n\n return [\n 'id' => $user?->getUuid(),\n 'name' => $user?->getName(),\n 'email' => $user?->getEmailAddress(),\n 'photoUrl' => $user?->getPhotoUrl(),\n ];\n }\n\n public function create(array $data): array\n {\n $validatedData = $this->validateAndTransformData($data);\n $validatedData['created_by'] = auth()->id();\n\n $automatedReport = $this->automatedReportsRepository->create($validatedData);\n\n $this->generateOneOffReport($automatedReport);\n\n return $this->transformReportFullView($automatedReport);\n }\n\n public function update(string $uuid, array $data): array\n {\n $validatedData = $this->validateAndTransformData($data);\n $report = $this->automatedReportsRepository->findByUuid($uuid);\n\n if (! $report) {\n throw new InvalidArgumentException('Report not found');\n }\n\n $oldCustomName = $report->getCustomName();\n\n $automatedReport = $this->automatedReportsRepository->update($report, $validatedData);\n\n if ($oldCustomName !== $automatedReport->getCustomName()) {\n $this->updateResultNames($automatedReport);\n }\n\n $this->generateOneOffReport($automatedReport);\n\n return $this->transformReportFullView($automatedReport);\n }\n\n /**\n * Create an Ask Jiminny report.\n */\n public function createAskJiminnyReport(array $data, User $creator): array\n {\n $validatedData = $this->validateAskJiminnyReportData($data, $creator);\n $validatedData['created_by'] = $creator->getId();\n\n $automatedReport = $this->automatedReportsRepository->create($validatedData);\n\n return $this->transformReportFullView($automatedReport);\n }\n\n /**\n * Update an Ask Jiminny report.\n */\n public function updateAskJiminnyReport(AutomatedReport $report, array $data, User $user): array\n {\n if (! $report->isAskJiminnyReport()) {\n throw new InvalidArgumentException('Report is not an Ask Jiminny report');\n }\n\n $validatedData = $this->validateAskJiminnyReportData($data, $user);\n\n $oldCustomName = $report->getCustomName();\n\n $automatedReport = $this->automatedReportsRepository->update($report, $validatedData);\n\n if ($oldCustomName !== $automatedReport->getCustomName()) {\n $this->updateResultNames($automatedReport);\n }\n\n return $this->transformReportFullView($automatedReport);\n }\n\n public function updateAskJiminnyReportStatus(AutomatedReport $report, bool $status): array\n {\n $this->automatedReportsRepository->update($report, ['status' => $status]);\n\n return $this->transformReportFullView($report->fresh());\n }\n\n /**\n * Validate and transform data for Ask Jiminny reports.\n */\n private function validateAskJiminnyReportData(array $data, User $user): array\n {\n // Validate name\n $name = trim($data['report_name'] ?? '');\n if (empty($name)) {\n throw new InvalidArgumentException('Report name is required');\n }\n if (mb_strlen($name) > 50) {\n throw new InvalidArgumentException('Report name must be 50 characters or less');\n }\n\n // Validate frequency (only daily, weekly, monthly for Ask Jiminny)\n $frequency = $data['frequency'] ?? null;\n $askJiminnyFrequencies = [self::FREQUENCY_DAILY, self::FREQUENCY_WEEKLY, self::FREQUENCY_MONTHLY];\n if (! in_array($frequency, $askJiminnyFrequencies, true)) {\n throw new InvalidArgumentException('Frequency must be daily, weekly, or monthly');\n }\n\n // Validate expiration date\n $expiresAt = $data['expires_on'] ?? null;\n if (empty($expiresAt)) {\n throw new InvalidArgumentException('Expiration date is required');\n }\n\n try {\n $expiresAtDate = Carbon::parse($expiresAt);\n } catch (InvalidFormatException $e) {\n throw new InvalidArgumentException('Expiration date format is invalid');\n }\n $maxExpiration = Carbon::now()->addYear()->endOfDay();\n if ($expiresAtDate->gt($maxExpiration)) {\n throw new InvalidArgumentException('Expiration date cannot be more than 1 year from now');\n }\n if ($expiresAtDate->isPast()) {\n throw new InvalidArgumentException('Expiration date cannot be in the past');\n }\n\n // Validate saved search\n $activitySearchId = $data['saved_search'] ?? null;\n if (empty($activitySearchId)) {\n throw new InvalidArgumentException('Saved search is required');\n }\n $savedSearch = $this->activitySearchRepository->findByUuidAndUser($activitySearchId, $user);\n if (! $savedSearch) {\n throw new InvalidArgumentException('Saved search not found or does not belong to you');\n }\n\n // Validate saved prompt\n $askAnythingPromptId = $data['ask_jiminny_prompt'] ?? null;\n if (empty($askAnythingPromptId)) {\n throw new InvalidArgumentException('Ask Jiminny prompt is required');\n }\n $prompt = $this->askAnythingRepository->getPromptByUuid($askAnythingPromptId);\n if (! $prompt) {\n throw new InvalidArgumentException('Ask Jiminny prompt not found');\n }\n\n // Validate status\n $status = $data['enabled'] ?? false;\n\n $recipientUserIds = [$user->getId()];\n\n if (! empty($data['share_users'])) {\n $sharedUserIds = $this->validateAndGetUserIdsByTeam(\n $user->team,\n (array) $data['share_users']\n );\n $recipientUserIds = array_merge($recipientUserIds, $sharedUserIds);\n }\n\n $sharedGroupIds = [];\n if (! empty($data['share_teams'])) {\n $sharedGroupIds = $this->validateAndGetGroupIds($user->team, (array) $data['share_teams']);\n }\n\n $recipientUserIds = array_values(array_unique($recipientUserIds));\n\n return [\n 'team_id' => $user->getTeamId(),\n 'type' => self::TYPE_ASK_JIMINNY,\n 'status' => (bool) $status,\n 'frequency' => $frequency,\n 'custom_name' => $name,\n 'activity_search_id' => $savedSearch->getId(),\n 'ask_anything_prompt_id' => $prompt->getId(),\n 'expires_at' => $expiresAtDate->toDateString(),\n 'media_types' => [self::MEDIA_TYPE_PDF],\n 'call_types' => [],\n 'recipients' => ['users' => $recipientUserIds],\n 'groups' => $sharedGroupIds,\n ];\n }\n\n public static function getAskJiminnyFrequencies(): array\n {\n return array_map(static function ($frequency) {\n return $frequency['id'];\n }, self::ASK_JIMINNY_FREQUENCIES);\n }\n\n public function getAskJiminnyReportFilters(User $user): array\n {\n $savedSearches = $this->activitySearchRepository->findByUserOrderedByName($user)\n ->map(fn (Search $search) => [\n 'id' => $search->getUuid(),\n 'name' => $search->getName(),\n ])\n ->values()->all();\n\n $prompts = collect(\n $this->askAnythingPromptService->get($user, AskAnythingPromptTarget::on_demand)\n )->map(fn (AskAnythingPromptDto $prompt) => [\n 'id' => $prompt->id,\n 'name' => $prompt->title,\n ])->values()->all();\n\n return [\n [\n 'id' => 'prompt',\n 'label' => 'Prompt',\n 'options' => $prompts,\n ],\n [\n 'id' => 'saved_search',\n 'label' => 'Saved Search',\n 'options' => $savedSearches,\n ],\n ];\n }\n\n public function getAskJiminnyReportFormData(User $user, ?AutomatedReport $report = null): array\n {\n $team = $user->getTeam();\n $userTimezone = $user->getTimezone();\n\n $savedSearches = $this->activitySearchRepository->findByUserOrderedByName($user)\n ->map(fn (Search $search) => [\n 'id' => $search->getUuid(),\n 'name' => $search->getName(),\n ])\n ->values()->all();\n\n $prompts = collect(\n $this->askAnythingPromptService->get($user, AskAnythingPromptTarget::on_demand)\n )->map(fn (AskAnythingPromptDto $prompt) => [\n 'id' => $prompt->id,\n 'name' => $prompt->title,\n ])->values()->all();\n\n $teamGroups = $this->groupRepository->getAllByTeam($team)->map(fn ($group) => [\n 'id' => $group->getUuid(),\n 'name' => $group->getName(),\n ])->values()->all();\n\n $shareUsers = $this->recipientsService->getRecipientsFieldData(team: $team)['options'] ?? [];\n\n $sharedTeamsValue = [];\n $sharedUsersValue = [];\n if ($report) {\n $sharedTeamsValue = $this->transformGroups($team, $report->getGroups());\n\n $recipientUserIds = $report->getRecipients()['users'] ?? [];\n $creatorId = $report->getAttribute('created_by');\n $sharedUserIds = array_values(array_filter(\n $recipientUserIds,\n static fn ($id) => $id !== $creatorId\n ));\n $sharedUsersValue = collect($sharedUserIds)\n ->map(fn ($id) => $this->userRepository->find((int) $id))\n ->filter()\n ->map(fn (User $u) => [\n 'id' => $u->getUuid(),\n 'name' => $u->getName(),\n ])\n ->values()\n ->all();\n }\n\n return [\n 'fields' => [\n [\n 'id' => 'enabled',\n 'inputType' => InputTypeEnum::TOGGLE,\n 'label' => '',\n 'value' => $report?->getStatus() ?? false,\n ],\n [\n 'id' => 'report_name',\n 'inputType' => InputTypeEnum::TEXT,\n 'label' => 'Name',\n 'placeholder' => 'Enter name',\n 'required' => true,\n 'validation' => ['maxLength' => 50],\n 'value' => $report?->getCustomName() ?? '',\n ],\n [\n 'id' => 'frequency',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'label' => 'Frequency',\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => self::ASK_JIMINNY_FREQUENCIES,\n 'value' => $report ? $this->transformFrequency($report->getFrequency()) : null,\n ],\n [\n 'id' => 'expires_on',\n 'inputType' => InputTypeEnum::DATE,\n 'label' => 'Expires on',\n 'required' => true,\n 'placeholder' => 'Select',\n 'validation' => [\n 'minDate' => now($userTimezone)->toDateString(),\n 'maxDate' => now($userTimezone)->addYear()->toDateString(),\n ],\n 'value' => $report?->getExpiresAt()?->toDateString(),\n ],\n [\n 'id' => 'share_teams',\n 'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,\n 'label' => 'Team',\n 'required' => false,\n 'placeholder' => 'Select',\n 'options' => $teamGroups,\n 'value' => $sharedTeamsValue,\n ],\n [\n 'id' => 'share_users',\n 'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,\n 'label' => 'Team member',\n 'required' => false,\n 'placeholder' => 'Select',\n 'groupLabelKey' => 'label',\n 'groupValuesKey' => 'users',\n 'optionLabelKey' => 'name',\n 'optionValueKey' => 'id',\n 'options' => $shareUsers,\n 'value' => $sharedUsersValue,\n ],\n [\n 'id' => 'saved_search',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'label' => 'Saved search',\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => $savedSearches,\n 'value' => $report && $report->getSavedSearch() ? [\n 'id' => $report->getSavedSearch()->getUuid(),\n 'name' => $report->getSavedSearch()->getName(),\n ] : null,\n ],\n [\n 'id' => 'ask_jiminny_prompt',\n 'inputType' => InputTypeEnum::DROPDOWN,\n 'label' => 'Ask Jiminny prompt',\n 'required' => true,\n 'placeholder' => 'Select',\n 'options' => $prompts,\n 'value' => $report && $report->getAskAnythingPrompt() ? [\n 'id' => $report->getAskAnythingPrompt()->getUuid(),\n 'name' => $report->getAskAnythingPrompt()->getTitle(),\n ] : null,\n ],\n ],\n ];\n }\n\n private function updateResultNames(AutomatedReport $automatedReport): void\n {\n $results = $this->automatedReportsRepository->getResultsByReport($automatedReport);\n\n foreach ($results as $result) {\n $result->update(['name' => $this->getReportFileName($result)]);\n }\n }\n\n public function updateStatus(string $uuid, array $data): array\n {\n $automatedReport = $this->automatedReportsRepository->findByUuid($uuid);\n\n if (! $automatedReport) {\n throw new ModelNotFoundException('Report not found');\n }\n\n $status = $this->validateReportStatus($data['report_enabled'] ?? null);\n $automatedReport->update([\n 'status' => $status,\n ]);\n\n $this->generateOneOffReport($automatedReport);\n\n return $this->transformReportFullView($automatedReport);\n }\n\n private function generateOneOffReport(AutomatedReport $automatedReport): void\n {\n // the scheduler handles all the other frequency types\n if ($automatedReport->getStatus() === false || $automatedReport->getFrequency() !== self::FREQUENCY_ONE_OFF) {\n return;\n }\n\n $this->dispatcher->dispatch(new RequestGenerateReportJob($automatedReport->getUuid()));\n }\n\n public function getReport(string $uuid): AutomatedReport\n {\n $automatedReport = $this->automatedReportsRepository->findByUuid($uuid);\n\n if (! $automatedReport) {\n throw new ModelNotFoundException('Report not found');\n }\n\n return $automatedReport;\n }\n\n public function get(string $uuid): array\n {\n $automatedReport = $this->getReport($uuid);\n\n return $this->transformReportFullView($automatedReport);\n }\n\n public function list(string $sortColumn = 'created_at', string $sortDirection = 'desc'): array\n {\n $results = [];\n $collection = $this->automatedReportsRepository->getAllStandardReports($sortColumn, $sortDirection);\n\n /** @var AutomatedReport $report */\n foreach ($collection as $report) {\n $results[] = $this->transformReportFullView($report);\n }\n\n return ['data' => $results];\n }\n\n public function listAskJiminnyReports(\n User $user,\n string $sortColumn = 'created_at',\n string $sortDirection = 'desc'\n ): array {\n $results = [];\n $collection = $this->automatedReportsRepository->getAskJiminnyReportsByUser($user, $sortColumn, $sortDirection);\n\n /** @var AutomatedReport $report */\n foreach ($collection as $report) {\n $results[] = $this->transformReportFullView($report);\n }\n\n return ['data' => $results];\n }\n\n public function delete(string $uuid): void\n {\n $automatedReport = $this->automatedReportsRepository->findByUuid($uuid);\n\n if (! $automatedReport) {\n throw new ModelNotFoundException('Report not found');\n }\n\n $automatedReport->delete();\n }\n\n public function createReportResult(AutomatedReport $automatedReport, array $data = []): AutomatedReportResult\n {\n return $this->automatedReportsRepository->createResult(\n array_merge(\n [\n 'report_id' => $automatedReport->getId(),\n 'status' => AutomatedReportResult::STATUS_DEFAULT,\n ],\n $data\n )\n );\n }\n\n public function getOrCreateReportResult(AutomatedReport $automatedReport, array $data = []): AutomatedReportResult\n {\n $existing = $this->automatedReportsRepository->findLatestDefaultOrFailedResult($automatedReport);\n\n if ($existing !== null) {\n $existing->update(['status' => AutomatedReportResult::STATUS_DEFAULT]);\n\n return $existing;\n }\n\n return $this->createReportResult($automatedReport, $data);\n }\n\n public function getReportResult(string $resultUuid): AutomatedReportResult\n {\n $report = $this->automatedReportsRepository->findResultByUuid($resultUuid);\n\n if (! $report) {\n throw new ModelNotFoundException('Report Result not found');\n }\n\n return $report;\n }\n\n public function findChildResult(AutomatedReportResult $result, string $type): ?AutomatedReportResult\n {\n return $this->automatedReportsRepository->findChildResult($result, $type);\n }\n\n // prophet API calls\n /**\n * @throws ApplicationException\n */\n public function getGenerateReportPayload(AutomatedReport $automatedReport, string $reportResultUuid): array\n {\n $period = $this->calculateFromAndToDate($automatedReport);\n $fromDate = $period['fromDate'];\n $toDate = $period['toDate'];\n\n return [\n 'team_id' => $automatedReport->getTeamId(),\n 'request_id' => $reportResultUuid,\n 'report_type' => $automatedReport->getType(),\n 'media_types' => $automatedReport->getMediaTypes(),\n 'from_date' => $fromDate->startOfDay()->format(DateTimeInterface::RFC3339),\n 'to_date' => $toDate->endOfDay()->format(DateTimeInterface::RFC3339),\n 'group_ids' => $automatedReport->getGroups(),\n 'call_deal_stage' => $automatedReport->getDealAtCallStages(),\n 'current_deal_stage' => $automatedReport->getCurrentDealStages(),\n 'deal_min_value' => $automatedReport->getDealValueMin(),\n 'deal_max_value' => $automatedReport->getDealValueMax(),\n 'call_types' => $automatedReport->getCallTypes(),\n 'call_duration_min_seconds' => $automatedReport->getCallDurationMin(),\n 'call_duration_max_seconds' => $automatedReport->getCallDurationMax(),\n 'special_requirements' => $automatedReport->getAdditionalPromptInput(),\n 'callback_url' => $this->getCallbackUrl(),\n 'report_period' => $this->formatReportPeriodName(\n $automatedReport->getFrequency(),\n $fromDate,\n $toDate,\n ),\n 'playbook_categories' => $automatedReport->getPlaybookCategories(),\n 'custom_name' => $automatedReport->getCustomName(),\n ];\n }\n\n // $inputPayload - FE payload structure\n public function getActivitiesCountPayload(array $inputPayload): array\n {\n // Use validateAndTransformData to validate and normalize input\n $validatedData = $this->validateAndTransformData($inputPayload);\n $period = $this->calculateFromAndToDatePeriod(\n $validatedData['frequency'],\n Carbon::parse($validatedData['from']),\n Carbon::parse($validatedData['to']),\n );\n $fromDate = $period['fromDate'];\n $toDate = $period['toDate'];\n\n // Create payload similar to getGenerateReportPayload\n return [\n 'team_id' => $validatedData['team_id'],\n 'group_ids' => $validatedData['groups'] ?? [],\n 'report_type' => $validatedData['type'],\n 'from_date' => $fromDate->format(DateTimeInterface::RFC3339),\n 'to_date' => $toDate->format(DateTimeInterface::RFC3339),\n 'call_deal_stage' => $validatedData['deal_at_call_stages'] ?? [],\n 'current_deal_stage' => $validatedData['current_deal_stages'] ?? [],\n 'deal_min_value' => $validatedData['deal_value_min'] ?? null,\n 'deal_max_value' => $validatedData['deal_value_max'] ?? null,\n 'call_types' => $validatedData['call_types'],\n 'call_duration_min_seconds' => $validatedData['call_duration_min'] ?? null,\n 'call_duration_max_seconds' => $validatedData['call_duration_max'] ?? null,\n 'special_requirements' => $validatedData['additional_prompt_input'] ?? null,\n 'playbook_categories' => $validatedData['playbook_categories'] ?? [],\n 'request_id' => null,\n 'callback_url' => null,\n ];\n }\n\n public function shouldSendReport(array $users, ?CarbonInterface $generatedAt = null): bool\n {\n if (empty($users)) {\n return false;\n }\n\n $earliestTz = collect($users)\n ->mapWithKeys(function (array $user) {\n $tz = new DateTimeZone($user['timezone']);\n $nowUtc = new DateTime('now', new DateTimeZone('UTC'));\n $offset = $tz->getOffset($nowUtc);\n\n return [$user['timezone'] => $offset];\n })\n ->sortDesc()\n ->keys()\n ->first();\n\n $now = Carbon::now($earliestTz);\n $isScheduledTime = (int) $now->format('H') === self::SENT_REPORT_AT_HOURS;\n\n if ($isScheduledTime) {\n return true;\n }\n\n return $this->hasPassedScheduledTime($generatedAt, $earliestTz);\n }\n\n public function hasPassedScheduledTime(?CarbonInterface $generatedAt, string $timezone): bool\n {\n if ($generatedAt === null) {\n return false;\n }\n\n $now = Carbon::now($timezone);\n $scheduledTime = $now->copy()->setTime(self::SENT_REPORT_AT_HOURS, 0, 0);\n\n if ($now->hour < self::SENT_REPORT_AT_HOURS) {\n $scheduledTime = $scheduledTime->subDay();\n }\n\n $scheduledTimeUtc = $scheduledTime->copy()->utc();\n $generatedAtUtc = $generatedAt->copy()->utc();\n $nowUtc = $now->copy()->utc();\n\n return $generatedAtUtc->lt($scheduledTimeUtc) && $nowUtc->gt($scheduledTimeUtc);\n }\n\n public function calculateFromAndToDatePeriod(\n string $frequency,\n ?Carbon $fromDate = null,\n ?Carbon $toDate = null\n ): array {\n if ($frequency === self::FREQUENCY_ONE_OFF) {\n return [\n 'fromDate' => $fromDate,\n 'toDate' => $toDate,\n ];\n }\n\n $now = Carbon::now();\n\n return match ($frequency) {\n self::FREQUENCY_DAILY => [\n 'fromDate' => $now->copy()->subDay()->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_WEEKLY => [\n 'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_MONTHLY => [\n 'fromDate' => $now->copy()->subMonths(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_QUARTERLY => [\n 'fromDate' => $now->copy()->subMonths(3)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n default => throw new InvalidArgumentException(\"Unsupported frequency: {$frequency}\"),\n };\n }\n\n private function calculateFromAndToDate(AutomatedReport $automatedReport): array\n {\n return $this->calculateFromAndToDatePeriod(\n $automatedReport->getFrequency(),\n $automatedReport->getFrom(),\n $automatedReport->getTo()\n );\n }\n\n public function getAskJiminnyGenerateReportPayload(\n AutomatedReport $automatedReport,\n AutomatedReportResult $reportResult,\n array $activityIds,\n ): array {\n return [\n 'user_question' => $automatedReport->getAskAnythingPrompt()?->getContent(),\n 'call_ids' => array_map('strval', $activityIds),\n 'team_id' => $automatedReport->getTeamId(),\n 'request_id' => $reportResult->getUuid(),\n 'callback_url' => $this->getCallbackUrl(),\n 'report_period' => $this->getReportPeriodName($reportResult),\n 'report_name' => $automatedReport->getCustomName(),\n ];\n }\n\n private function getCallbackUrl(): string\n {\n return $this->webhookService->route('jiminny.webhook.reports.ready');\n }\n\n /**\n * Validate and transform payload data for automated reports\n *\n * @param array $data\n *\n * @throws InvalidArgumentException\n *\n * @return array\n */\n private function validateAndTransformData(array $data): array\n {\n // Validate organization (team) and check feature\n $team = $this->validateOrganization($data['organization'] ?? null);\n\n $status = $this->validateReportStatus($data['report_enabled'] ?? null);\n $type = $this->validateReportType($data['report_type'] ?? null);\n $frequency = $this->validateFrequency($data['frequency'] ?? null);\n $additionalPromptInput = $this->validateAdditionalPromptInput(\n $data['additional_prompt_input'] ?? null\n );\n $customReportName = $this->validateCustomReportName($data['custom_name'] ?? null);\n\n // Prepare data for the database\n $reportData = [\n 'team_id' => $team->getId(),\n 'type' => $type,\n 'status' => $status,\n 'frequency' => $frequency,\n 'additional_prompt_input' => $additionalPromptInput,\n 'custom_name' => $customReportName,\n ];\n\n // Validate deal values\n $reportData = $this->validateDealValues($data, $reportData);\n\n // Validate date range\n $reportData = $this->validateDateRange($data, $reportData, $frequency);\n\n // Validate call durations\n $reportData = $this->validateCallDurations($data, $reportData);\n\n // Validate call types\n $reportData = $this->validateCallTypes($data, $reportData);\n\n // Validate media types\n $reportData = $this->validateMediaTypes($data, $reportData);\n\n // Validate groups\n if (isset($data['teams'])) {\n $reportData['groups'] = $this->validateAndGetGroupIds($team, $data['teams']);\n }\n\n // Validate deal stages\n $reportData = $this->validateDealStages($data, $reportData, $team, $type);\n\n // Validate playbook categories\n $reportData = $this->validatePlaybookCategories($data, $reportData, $team);\n\n // Validate recipients\n $reportData['recipients'] = [\n 'users' => $this->validateAndGetUserIdsByTeam($team, $data['recipients'] ?? []),\n ];\n\n if (isset($data['jiminny_recipients'])) {\n // Validate Jiminny recipients\n $reportData['jiminny_recipients'] = [\n 'users' => $this->validateAndGetJiminnyUserIds((array) $data['jiminny_recipients']),\n ];\n }\n\n return $reportData;\n }\n\n private function validateDealValues(array $data, array $reportData): array\n {\n if (isset($data['min_deal_value'])) {\n $reportData['deal_value_min'] = (int) $data['min_deal_value'];\n\n if ($reportData['deal_value_min'] > 4294967295 || $reportData['deal_value_min'] < 0) {\n throw new InvalidArgumentException('Min deal value should be between 0 and 4294967295');\n }\n }\n\n if (isset($data['max_deal_value'])) {\n $reportData['deal_value_max'] = (int) $data['max_deal_value'];\n\n if ($reportData['deal_value_max'] > 4294967295 || $reportData['deal_value_max'] < 0) {\n throw new InvalidArgumentException('Max deal value should be between 0 and 4294967295');\n }\n }\n\n if (isset($data['min_deal_value'], $data['max_deal_value'])\n && $data['min_deal_value'] > $data['max_deal_value']\n ) {\n throw new InvalidArgumentException('Min deal value cannot be greater than max deal value');\n }\n\n return $reportData;\n }\n\n private function validateDateRange(array $data, array $reportData, string $frequency): array\n {\n // Set date range only for one_off frequency\n if ($frequency === 'one_off') {\n if (isset($data['start_date_period'])) {\n $reportData['from'] = $this->parseDate($data['start_date_period']);\n }\n\n if (isset($data['end_date_period'])) {\n $reportData['to'] = $this->parseDate($data['end_date_period']);\n }\n\n if (empty($reportData['from']) || empty($reportData['to'])) {\n throw new InvalidArgumentException(\n 'Start date and end date are required for one_off frequency'\n );\n }\n } else {\n $reportData['from'] = null;\n $reportData['to'] = null;\n }\n\n return $reportData;\n }\n\n private function validateCallDurations(array $data, array $reportData): array\n {\n // Convert call durations from minutes to seconds\n if (isset($data['min_call_duration'])) {\n $reportData['call_duration_min'] = (int) $data['min_call_duration'] * 60;\n\n if ($reportData['call_duration_min'] > 4294967295 || $reportData['call_duration_min'] < 0) {\n throw new InvalidArgumentException('Min call duration should be between 0 and 4294967295');\n }\n }\n\n if (isset($data['max_call_duration'])) {\n $reportData['call_duration_max'] = (int) $data['max_call_duration'] * 60;\n\n if ($reportData['call_duration_max'] > 4294967295 || $reportData['call_duration_max'] < 0) {\n throw new InvalidArgumentException('Max call duration should be between 0 and 4294967295');\n }\n }\n\n return $reportData;\n }\n\n private function validateCallTypes(array $data, array $reportData): array\n {\n // Set call types\n $reportData['call_types'] = $data['call_type'] ?? [];\n if (empty($reportData['call_types'])) {\n $reportData['call_types'] = self::getCallTypes();\n }\n\n foreach ($reportData['call_types'] as $callType) {\n if (! in_array($callType, self::getCallTypes(), true)) {\n throw new InvalidArgumentException(sprintf('Call type %s is invalid', $callType));\n }\n }\n\n return $reportData;\n }\n\n private function validateMediaTypes(array $data, array $reportData): array\n {\n // Set media types from input data\n $reportData['media_types'] = $data['media_types'] ?? [];\n\n if (empty($reportData['media_types'])) {\n throw new InvalidArgumentException('Media types are required');\n }\n\n foreach ($reportData['media_types'] as $mediaType) {\n if (! in_array($mediaType, self::MEDIA_TYPES, true)) {\n throw new InvalidArgumentException(sprintf('Media type %s is invalid', $mediaType));\n }\n }\n\n return $reportData;\n }\n\n private function validateDealStages(array $data, array $reportData, Team $team, string $reportType): array\n {\n // Validate and set deal stages\n if (isset($data['deal_stage_at_call'])) {\n $reportData['deal_at_call_stages'] =\n $this->validateAndGetDealStageIds($team, $data['deal_stage_at_call'], 'Deal stage at call');\n }\n\n if (isset($data['current_deal_stage'])) {\n $reportData['current_deal_stages'] =\n $this->validateAndGetDealStageIds($team, $data['current_deal_stage'], 'Current deal stage');\n }\n\n // Ensure current_deal_stage is not provided for loss_analysis report type\n if ($reportType === self::TYPE_LOSS_ANALYSIS && ! empty($data['current_deal_stage'])) {\n throw new InvalidArgumentException('Current deal stage is not applicable for Loss Analysis reports');\n }\n\n return $reportData;\n }\n\n // transform uuid to id\n private function validatePlaybookCategories(array $data, array $reportData, Team $team): array\n {\n $key = 'playbook_categories';\n\n if (isset($data[$key])) {\n $payloadIds = $data[$key];\n $ids = [];\n\n foreach ($payloadIds as $uuid) {\n $uuid = (string) $uuid;\n\n try {\n $playbookCategory = $this->playbookCategoryRepository->findByUuid($uuid);\n } catch (Throwable $throwable) {\n Log::error(__METHOD__ . ' ' . $throwable->getMessage());\n\n throw new InvalidArgumentException(sprintf('Playbook category %s not found', $uuid));\n }\n\n if (! $playbookCategory) {\n throw new InvalidArgumentException(sprintf('Playbook category %s not found', $uuid));\n }\n\n if (! $playbookCategory->hasPlaybook()) {\n throw new InvalidArgumentException(sprintf('Playbook category %s has no playbook', $uuid));\n }\n\n if ($playbookCategory->getPlaybook()->getTeamId() !== $team->getId()) {\n throw new InvalidArgumentException(\n sprintf('Playbook category %s not found for team %s', $uuid, $team->getUuid())\n );\n }\n\n $ids[] = $playbookCategory->getId();\n }\n\n $reportData[$key] = $ids;\n }\n\n return $reportData;\n }\n\n private function validateReportStatus($status): bool\n {\n if (! in_array($status, [true, false], true)) {\n throw new InvalidArgumentException('Report status is invalid');\n }\n\n return $status;\n }\n\n private function validateReportType($type): string\n {\n if (! in_array($type, self::getTypes(), true)) {\n throw new InvalidArgumentException(sprintf('Report type is invalid: %s', $type));\n }\n\n return $type;\n }\n\n private function validateFrequency($frequency): string\n {\n if (! in_array($frequency, self::getFrequencies(), true)) {\n throw new InvalidArgumentException('Frequency is invalid');\n }\n\n return $frequency;\n }\n\n private function validateAdditionalPromptInput(?string $additionalPromptInput): ?string\n {\n if ($additionalPromptInput && strlen($additionalPromptInput) > 5000) {\n throw new InvalidArgumentException('Additional Prompt Input should be less than 5000 characters');\n }\n\n return $additionalPromptInput;\n }\n\n private function validateCustomReportName(?string $customReportName): ?string\n {\n if ($customReportName === null || $customReportName === '') {\n return null;\n }\n\n if (strlen($customReportName) > 70) {\n throw new InvalidArgumentException('Custom report name should be less than 70 characters');\n }\n\n return $customReportName;\n }\n\n private function validateOrganization(?string $organizationUuid): Team\n {\n if (! $organizationUuid) {\n throw new InvalidArgumentException('Organization is required');\n }\n\n $team = $this->teamRepository->idOrUuid($organizationUuid);\n\n if (! $team) {\n throw new InvalidArgumentException('Organization not found');\n }\n\n if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {\n throw new InvalidArgumentException('Organization does not have the Automated Reports feature');\n }\n\n return $team;\n }\n\n private function validateAndGetGroupIds(Team $team, array $teamUuids): array\n {\n $groupIds = [];\n\n foreach ($teamUuids as $uuid) {\n $group = $this->groupRepository->findByUuid($uuid);\n\n if ($group === null || $group->getTeamId() !== $team->getId()) {\n throw new InvalidArgumentException(\n sprintf('Group %s not found for team %s', $uuid, $team->getUuid())\n );\n }\n\n $groupIds[] = $group->getId();\n\n }\n\n return $groupIds;\n }\n\n private function validateAndGetDealStageIds(Team $team, array $stageUuids, string $propertyLabel): array\n {\n $stageIds = [];\n\n foreach ($stageUuids as $uuid) {\n $stage = $this->stageRepository->findByUuid($uuid);\n\n if ($stage === null || $stage->getTeamId() !== $team->getId()) {\n throw new InvalidArgumentException(\n sprintf('Stage %s not found for team %s for %s', $uuid, $team->getUuid(), $propertyLabel)\n );\n }\n\n $stageIds[] = $stage->getId();\n }\n\n return $stageIds;\n }\n\n private function validateAndGetUserIds(array $userUuids, callable $teamCheck): array\n {\n if (empty($userUuids)) {\n return [];\n }\n\n $userIds = [];\n\n foreach ($userUuids as $uuid) {\n $user = $this->userRepository->findByUuid($uuid);\n\n if (! $user || ! $user->isStatusActive()) {\n throw new InvalidArgumentException(\n sprintf('User %s not found or is not active', $uuid)\n );\n }\n\n if (! $teamCheck($user)) {\n throw new InvalidArgumentException(\n sprintf('User %s does not belong to the allowed team(s)', $uuid)\n );\n }\n\n $userIds[] = $user->getId();\n }\n\n return $userIds;\n }\n\n private function validateAndGetUserIdsByTeam(Team $team, array $userUuids): array\n {\n return $this->validateAndGetUserIds($userUuids, fn ($user) => $user->getTeamId() === $team->getId());\n }\n\n private function validateAndGetJiminnyUserIds(array $userUuids): array\n {\n $allowedTeamIds = config('kiosk.teamIds', []);\n\n return $this->validateAndGetUserIds($userUuids, fn ($user) => in_array($user->getTeamId(), $allowedTeamIds, true));\n }\n\n private function parseDate(string $dateString): string\n {\n return date('Y-m-d H:i:s', strtotime($dateString));\n }\n\n private function generateReportResultViewUrl(AutomatedReportResult $result): string\n {\n $mediaResource = $this->getReportMediaRouteResource($result);\n\n return route('ai-reports.' . $mediaResource . '.view', ['uuid' => $result->getUuid()]);\n }\n\n private function generateReportResultDownloadUrl(AutomatedReportResult $result): string\n {\n $mediaResource = $this->getReportMediaRouteResource($result);\n\n return route('ai-reports.' . $mediaResource . '.download', ['uuid' => $result->getUuid()]);\n }\n\n private function getReportMediaRouteResource(AutomatedReportResult $result): string\n {\n if ($result->getMediaType() === self::MEDIA_TYPE_PDF) {\n return self::PDF_KEY;\n } elseif ($result->getMediaType() === self::MEDIA_TYPE_PODCAST) {\n return self::AUDIO_KEY;\n }\n\n throw new \\InvalidArgumentException('Unknown media type.');\n }\n\n public function getMediaPath(AutomatedReportResult $result): ?string\n {\n $url = match ($result->getMediaType()) {\n self::MEDIA_TYPE_PDF => $result->getPdfUrl(),\n self::MEDIA_TYPE_PODCAST => $result->getPodcastAudioUrl(),\n default => null,\n };\n\n if ($url === null) {\n return null;\n }\n\n $path = parse_url(trim($url, '\"\\''), PHP_URL_PATH);\n\n return $path ?: null;\n }\n\n public function getFilenameSuffix(AutomatedReportResult $result): ?string\n {\n return match ($result->getMediaType()) {\n self::MEDIA_TYPE_PODCAST => 'Podcast',\n default => null,\n };\n }\n\n public function getMailSubjectSuffix(AutomatedReportResult $result): string\n {\n return match ($result->getMediaType()) {\n self::MEDIA_TYPE_PDF => 'report',\n self::MEDIA_TYPE_PODCAST => 'podcast',\n default => '',\n };\n }\n\n public function getMediaTypeMetadata(AutomatedReportResult $result): array\n {\n return match ($result->getMediaType()) {\n self::MEDIA_TYPE_PODCAST => ['extension' => 'mp3', 'mime' => 'audio/mpeg'],\n self::MEDIA_TYPE_PDF => ['extension' => 'pdf', 'mime' => 'application/pdf'],\n default => ['extension' => null, 'mime' => null],\n };\n }\n\n public function deleteS3Files(AutomatedReportResult $result): void\n {\n $teamUuid = $result->getReport()->getTeam()->getUuid();\n $reportUuid = $result->getUuid();\n\n // delete all files for a report uuid no mather of pdf, podcast, or both\n // in case of both - the podcast files are linked to the pdf (parent) uuid\n // pdf and podcast date times should be close\n $path = sprintf('%s/%s/%s', $teamUuid, self::S3_DIR, $reportUuid);\n\n foreach (self::FILE_EXTENSIONS_VARIANTS as $extension) {\n $file = $path . '.' . $extension;\n\n if (Storage::exists($file)) {\n Storage::delete($file);\n Log::info('[Reports] Deleted S3 file', [\n 'path' => $file,\n ]);\n }\n }\n\n foreach (self::FILE_PODCAST_EXTENSIONS_VARIANTS as $extension) {\n $file = $path . '_podcast.' . $extension;\n\n if (Storage::exists($file)) {\n Storage::delete($file);\n Log::info('[Reports] Deleted Podcast S3 file', [\n 'path' => $file,\n ]);\n }\n }\n }\n\n /**\n *\n * @param int|null $teamId Optional team ID to filter results\n *\n * @return Collection<int, int> Collection of team IDs\n */\n public function getTeamIdsWithReportsResults(?int $teamId = null): Collection\n {\n return $this->automatedReportsRepository->getTeamIdsWithReportsResults($teamId);\n }\n\n /**\n * Core delete logic for report results using a query\n *\n * @param Builder $query\n * @param array $logContext\n *\n * @return int\n */\n private function deleteReportResultsByQuery(Builder $query, array $logContext = []): int\n {\n $deletedCount = 0;\n\n if ($query->exists()) {\n Log::info(\n 'Run delete report results',\n array_merge(\n $logContext,\n [\n 'service' => 'AutomatedReportsService',\n ]\n )\n );\n\n $query->chunkById(50, function ($results) use (&$deletedCount, $logContext) {\n foreach ($results as $result) {\n $this->deleteReportResult($result);\n $deletedCount++;\n\n Log::info(\n 'Deleted a report result',\n array_merge(\n $logContext,\n [\n 'result_id' => $result->getId(),\n 'report_id' => $result->getReportId(),\n ]\n )\n );\n }\n });\n }\n\n return $deletedCount;\n }\n\n /**\n * Delete report results for a team by retention period\n *\n * @param Team $team\n * @param CarbonImmutable $retentionDate\n *\n * @return int Number of deleted report results\n */\n public function deleteReportsResultsInRetentionPeriod(Team $team, CarbonImmutable $retentionDate): int\n {\n $reportIds = $this->automatedReportsRepository->getReportIdsByTeam($team);\n\n if ($reportIds->isEmpty()) {\n return 0;\n }\n\n $query = $this->automatedReportsRepository\n ->getReportResultsQueryForRetention($team, $retentionDate);\n\n return $this->deleteReportResultsByQuery($query, [\n 'team_id' => $team->getId(),\n 'retention_date' => $retentionDate->toDateTimeString(),\n ]);\n }\n\n /**\n * Delete ALL report results for a specific automated report\n *\n * @param string $uuid\n *\n * @return int\n */\n public function deleteReportResults(string $uuid): int\n {\n $report = $this->getReport($uuid);\n\n $query = $this->automatedReportsRepository->getResultsByReportQuery($report);\n\n return $this->deleteReportResultsByQuery($query, [\n 'report_uuid' => $uuid,\n 'report_id' => $report->getId(),\n ]);\n }\n\n public function deleteReportResult(AutomatedReportResult $result): void\n {\n $this->deleteS3Files($result);\n\n $result->delete();\n }\n\n /**\n * Get all reports for a specific team\n *\n * @param Team $team\n *\n * @return \\Illuminate\\Database\\Eloquent\\Collection\n */\n public function getTeamReports(Team $team): \\Illuminate\\Database\\Eloquent\\Collection\n {\n return $this->automatedReportsRepository->getReportsByTeam($team);\n }\n\n /**\n * Get all report results for a specific report\n *\n * @param AutomatedReport $report\n *\n * @return \\Illuminate\\Database\\Eloquent\\Collection\n */\n public function getReportResults(AutomatedReport $report): \\Illuminate\\Database\\Eloquent\\Collection\n {\n return $this->automatedReportsRepository->getResultsByReport($report);\n }\n\n public function deleteAllReportResults(AutomatedReport $report): void\n {\n $results = $this->getReportResults($report);\n\n /** @var AutomatedReportResult $result */\n foreach ($results as $result) {\n Log::info('Deleting result', [\n 'report' => $report->getId(),\n 'result' => $result->getId(),\n ]);\n\n $this->deleteReportResult($result);\n }\n }\n\n public function deleteAllData(Team $team): void\n {\n Log::info('Deleting automated report and results for team', [\n 'team' => $team->getId(),\n ]);\n\n $reports = $this->getTeamReports($team);\n\n /** @var AutomatedReport $report */\n foreach ($reports as $report) {\n Log::info('Deleting report', [\n 'team' => $team->getId(),\n 'report' => $report->getId(),\n ]);\n\n $this->deleteAllReportResults($report);\n\n $report->delete();\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"bounds":{"left":0.70146275,"top":0.123703115,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"bounds":{"left":0.7101064,"top":0.123703115,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"bounds":{"left":0.72107714,"top":0.123703115,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"bounds":{"left":0.7297208,"top":0.123703115,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"bounds":{"left":0.73836434,"top":0.123703115,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"bounds":{"left":0.7493351,"top":0.123703115,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"bounds":{"left":0.7603058,"top":0.123703115,"width":0.024268618,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Cancel Running Statements","depth":4,"bounds":{"left":0.7869016,"top":0.123703115,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Playground","depth":4,"bounds":{"left":0.79787236,"top":0.123703115,"width":0.029587766,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"jiminny","depth":4,"bounds":{"left":0.9587766,"top":0.123703115,"width":0.02825798,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"18","depth":4,"bounds":{"left":0.9311835,"top":0.14844373,"width":0.009640957,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"14","depth":4,"bounds":{"left":0.9428192,"top":0.14844373,"width":0.009640957,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.9544548,"top":0.14844373,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"4","depth":4,"bounds":{"left":0.9644282,"top":0.14844373,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.14684756,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.14684756,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"SELECT a.id, a.uuid, a.actual_start_time, o.id, o.uuid FROM opportunities o\nJOIN activities a ON o.id = a.opportunity_id\nWHERE a.crm_configuration_id = 39\nAND a.actual_start_time > '2025-10-13'\nAND a.type IN ('conference', 'softphone-inbound', 'softphone-outbound')\n;\n\nSELECT * FROM activities\nWHERE crm_configuration_id = 39 and user_id = 143\nand actual_start_time >= '2025-10-13'\nAND type IN ('conference', 'softphone-inbound', 'softphone-outbound')\n;\n\nSELECT * FROM opportunities WHERE account_id IN (178);\nselect * from activities where id IN (620137, 620187, 620188, 620189, 620230);\n\n# HS\nSELECT * FROM opportunities WHERE id IN (238);\nselect * from activities where id IN (477,2076);\n\nselect * from users;\n\nSELECT COUNT(*) FROM users;\nSELECT COUNT(*) FROM activities;\nSELECT COUNT(*) FROM opportunities;\n\nUPDATE activities\nSET\n actual_start_time = '2025-12-19 09:00:00',\n actual_end_time = '2025-12-19 10:30:00',\n scheduled_start_time = '2025-12-19 09:00:00',\n scheduled_end_time = '2025-12-19 10:30:00'\nWHERE id IN (407509,407375);\n\nselect * from partners;\n\nSELECT id, uuid, type, actual_start_time, user_id, crm_configuration_id\nFROM activities\nWHERE user_id = 143\nAND actual_start_time >= '2025-10-13 00:00:00'\nAND actual_start_time <= '2026-01-13 23:59:59'\nORDER BY actual_start_time DESC;\n\nSELECT * FROM activities WHERE uuid_to_bin('78eda160-3086-435f-88a5-bb0c71b6008d') = uuid;\nSELECT * FROM crm_layouts where crm_configuration_id = 39;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 282;\n# lead_id\n# account_id 177\n# contact_id 3969\n# opportunity_id\n# stage_id 203\n\nSELECT * FROM opportunities WHERE opportunities.crm_configuration_id = id = 282;\n\nSELECT * FROM activities where crm_configuration_id = 39 AND type = 'conference'\nAND user_id = 143 and actual_start_time >= '2025-10-13';\n\nSELECT * FROM activities a\n# JOIN opportunities o ON a.opportunity_id = o.id\nWHERE a.crm_configuration_id = 39 AND a.type = 'conference'\nand status = 'completed' and recording_state = 'recorded'\nand a.actual_start_time >= '2025-10-13'\nAND a.user_id = 143\n;\n\nselect * from leads\nwhere crm_configuration_id = 39; # 112 -> ac. 178, 109 => op. 1707\n\nSELECT * FROM activities WHERE id IN (356013,616188,616202,616310,407509,407375,356001,356008);\nSELECT * FROM activities WHERE id IN (356013,616188,616202,616310);\nSELECT * FROM activities WHERE id IN (407509,407375); # leads: 112, 109 | status - 198\nSELECT * FROM activities WHERE id IN (356001, 356008); # contacts:\n\nSELECT * FROM opportunities WHERE id IN (1707);\nSELECT * FROM stages where id IN (204, 198);\nSELECT * FROM opportunities WHERE account_id IN (178);\nSELECT * FROM opportunities WHERE crm_configuration_id = 39 AND created_at > '2025-01-01';\nSELECT * FROM contacts WHERE account_id IN (178); # 4118 Musaibe, 4448 Ceco Personal\n\nSELECT * FROM activities where crm_configuration_id = 39\nAND opportunity_id IS NULL\nAND is_internal = false\nand status = 'completed' and recording_state = 'recorded'\nAND actual_start_time >= '2025-10-13'\nAND (lead_id IS NOT NULL OR contact_id IS NOT NULL OR account_id IS NOT NULL)\n# AND lead_id IN (112, 109)\n;\n\nSELECT * FROM crm_profiles WHERE user_id = 143;\n\nselect * from inboxes; # 212\nselect * from users where id = 143; # 143\nselect * from inbox_email_batches where inbox_id = 212\nand updated_at >= '2026-01-28 00:00:00' order by id desc;\nselect * from inbox_emails where inbox_id = 212\nand batch_id = 95885 order by id desc;\nselect * from email_messages where origin_user_id = 143;\nselect * from activities where user_id = 143 and updated_at >= '2026-01-28 00:00:00';\nselect * from participants where activity_id = 620247;\n\nselect * from crm_profiles where user_id = 143;\n\nSELECT * FROM activities WHERE uuid_to_bin('458cf915-b914-4000-b083-5687b32b2956') = uuid; # 356001\nselect * from transcription where activity_id = 356001; # 6943\nselect * from ai_prompts where transcription_id = 6943;\nSELECT * FROM activity_summary_logs where activity_id = 356001;\n\nSELECT * FROM social_accounts WHERE sociable_id = 143;\n\n# ************************************************************************************\nSELECT * FROM activities WHERE uuid_to_bin('0164a4fb-cb95-454e-9edd-4d804e4999bd') = uuid;\n# 422515 softphone tr. 8100\n\nSELECT * FROM activities WHERE uuid_to_bin('7520add8-8d87-41a5-98e5-fc4edf96f21e') = uuid;\n# 407509 conference tr. 7670 crmId: 00UD1000002J9aTMAS\n\nselect * from ai_prompts where transcription_id IN (8100, 7670);\nselect * from activity_summary_logs where activity_id = 407509;\n\nselect * from sidekick_settings;\nselect * from default_activity_types;\n\nSELECT * FROM contacts WHERE crm_configuration_id = 39 and email = 'm.kogoj@gmx.at';\nSELECT * FROM leads WHERE crm_configuration_id = 39 and email = 'm.kogoj@gmx.at';\n\nSELECT * FROM activity_searches where user_id = 143;\nSELECT * FROM groups where team_id = 1;\n\nselect * from teams where id = 1;\nselect * from groups where team_id = 1; # 1150 - 7e75f8025c22\nselect id, name, group_id, status, deleted_at, email\nfrom users where team_id = 1 order by group_id desc ;\n\nselect * from activity_searches where id in (1977, 1978, 1979);\nselect * from activity_search_filters where activity_search_id IN (1977, 1978, 1979);\nselect * from activity_search_filters where filter = 'group_id' and value = '443f26b8-8512-437e-a9f9-7e75f8025c22'; # 10268, 10272, 10277\nselect * from nudges where activity_search_id IN (1977, 1978, 1979); # 877, 878, 879\n\nINSERT INTO `activity_search_filters`\n(`activity_search_id`, `filter`, `value`) VALUES\n(1977, 'group_id', '443f26b8-8512-437e-a9f9-7e75f8025c22'),\n(1978, 'group_id', '443f26b8-8512-437e-a9f9-7e75f8025c22'),\n(1979, 'group_id', '443f26b8-8512-437e-a9f9-7e75f8025c22')\n;\n\nselect * from crm_configurations where id = 39;\n\n\nselect sa.* from users u JOIN social_accounts sa on u.id = sa.sociable_id\nwhere u.team_id = 1;\nSELECT * FROM social_accounts WHERE sociable_id = 1635;\nSELECT * FROM users WHERE id = 1635;\n\nselect * from teams where id = 1;\nselect * from users where team_id = 1;\nselect * from team_features where team_id = 1;\nselect * from features;\n\nSELECT * FROM activity_searches where id = 1982; # 1981\nSELECT * FROM activity_search_filters WHERE activity_search_id = 1982;\n\nSELECT * FROM automated_reports where id = 68;\nUPDATE automated_reports set playbook_categories = NULL where id = 68;\nSELECT * FROM automated_report_results where id = 275;\n\nSELECT * FROM automated_reports order by id desc;\nSELECT * FROM automated_report_results order by id desc;\nselect * from activity_searches where user_id = 143;\nselect * from ask_anything_prompts;\n\nSELECT * FROM groups WHERE id = 1439;\nSELECT * FROM users WHERE group_id = 1439;\n\nselect * from permissions; # 158\nselect * from roles;\nselect * from permission_role\n\nselect * from teams where id = 1;\nselect * from groups g JOIN playbooks p on g.playbook_id = p.id where g.team_id = 1;\nselect * from groups where id = 28;\nselect * from playbooks where team_id = 1;\nselect * from playbooks where id = 179;\nselect * from playbook_categories where id = 1391;\nselect * from users where id = 143;\nselect * from crm_profiles where user_id = 143;\nselect * from activities where crm_configuration_id = 39 and type = 'conference'\nand crm_provider_id IS NOT NULL ORDER by id desc;\nselect * from activities where id = 422003; # 00UO400000pB6fpMAC\n\nSELECT ar.id, ar.uuid, ar.media_type, ar.status, a.type\nFROM automated_report_results ar\nJOIN automated_reports a ON a.id = ar.report_id\nWHERE a.type = 'ask_jiminny'\nLIMIT 10;\n\nSELECT `automated_report_results`.* FROM `automated_report_results`\nINNER JOIN `automated_reports`\n ON `automated_report_results`.`report_id` = `automated_reports`.`id`\nWHERE `automated_report_results`.`generated_at` IS NOT NULL\n AND `automated_reports`.`team_id` = 1\n AND JSON_CONTAINS(`automated_reports`.`recipients`, 1635, '$.\"users\"')\n;\n\n\nselect * from teams where id = 3143;\nselect * from crm_configurations where id = 500;\nselect * from users where name = 'Integration Account'; # 1695\nSELECT * FROM social_accounts WHERE sociable_id = 1695;\n\nselect * from activities where crm_configuration_id = 39\nand recording_state = 'recorded' and duration > 60\nand status = 'completed' and actual_start_time >= '2025-12-01';\n\nSELECT * FROM activities WHERE uuid_to_bin('458cf915-b914-4000-b083-5687b32b2956') = uuid;\n\nselect * from leads;","depth":4,"value":"SELECT a.id, a.uuid, a.actual_start_time, o.id, o.uuid FROM opportunities o\nJOIN activities a ON o.id = a.opportunity_id\nWHERE a.crm_configuration_id = 39\nAND a.actual_start_time > '2025-10-13'\nAND a.type IN ('conference', 'softphone-inbound', 'softphone-outbound')\n;\n\nSELECT * FROM activities\nWHERE crm_configuration_id = 39 and user_id = 143\nand actual_start_time >= '2025-10-13'\nAND type IN ('conference', 'softphone-inbound', 'softphone-outbound')\n;\n\nSELECT * FROM opportunities WHERE account_id IN (178);\nselect * from activities where id IN (620137, 620187, 620188, 620189, 620230);\n\n# HS\nSELECT * FROM opportunities WHERE id IN (238);\nselect * from activities where id IN (477,2076);\n\nselect * from users;\n\nSELECT COUNT(*) FROM users;\nSELECT COUNT(*) FROM activities;\nSELECT COUNT(*) FROM opportunities;\n\nUPDATE activities\nSET\n actual_start_time = '2025-12-19 09:00:00',\n actual_end_time = '2025-12-19 10:30:00',\n scheduled_start_time = '2025-12-19 09:00:00',\n scheduled_end_time = '2025-12-19 10:30:00'\nWHERE id IN (407509,407375);\n\nselect * from partners;\n\nSELECT id, uuid, type, actual_start_time, user_id, crm_configuration_id\nFROM activities\nWHERE user_id = 143\nAND actual_start_time >= '2025-10-13 00:00:00'\nAND actual_start_time <= '2026-01-13 23:59:59'\nORDER BY actual_start_time DESC;\n\nSELECT * FROM activities WHERE uuid_to_bin('78eda160-3086-435f-88a5-bb0c71b6008d') = uuid;\nSELECT * FROM crm_layouts where crm_configuration_id = 39;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 282;\n# lead_id\n# account_id 177\n# contact_id 3969\n# opportunity_id\n# stage_id 203\n\nSELECT * FROM opportunities WHERE opportunities.crm_configuration_id = id = 282;\n\nSELECT * FROM activities where crm_configuration_id = 39 AND type = 'conference'\nAND user_id = 143 and actual_start_time >= '2025-10-13';\n\nSELECT * FROM activities a\n# JOIN opportunities o ON a.opportunity_id = o.id\nWHERE a.crm_configuration_id = 39 AND a.type = 'conference'\nand status = 'completed' and recording_state = 'recorded'\nand a.actual_start_time >= '2025-10-13'\nAND a.user_id = 143\n;\n\nselect * from leads\nwhere crm_configuration_id = 39; # 112 -> ac. 178, 109 => op. 1707\n\nSELECT * FROM activities WHERE id IN (356013,616188,616202,616310,407509,407375,356001,356008);\nSELECT * FROM activities WHERE id IN (356013,616188,616202,616310);\nSELECT * FROM activities WHERE id IN (407509,407375); # leads: 112, 109 | status - 198\nSELECT * FROM activities WHERE id IN (356001, 356008); # contacts:\n\nSELECT * FROM opportunities WHERE id IN (1707);\nSELECT * FROM stages where id IN (204, 198);\nSELECT * FROM opportunities WHERE account_id IN (178);\nSELECT * FROM opportunities WHERE crm_configuration_id = 39 AND created_at > '2025-01-01';\nSELECT * FROM contacts WHERE account_id IN (178); # 4118 Musaibe, 4448 Ceco Personal\n\nSELECT * FROM activities where crm_configuration_id = 39\nAND opportunity_id IS NULL\nAND is_internal = false\nand status = 'completed' and recording_state = 'recorded'\nAND actual_start_time >= '2025-10-13'\nAND (lead_id IS NOT NULL OR contact_id IS NOT NULL OR account_id IS NOT NULL)\n# AND lead_id IN (112, 109)\n;\n\nSELECT * FROM crm_profiles WHERE user_id = 143;\n\nselect * from inboxes; # 212\nselect * from users where id = 143; # 143\nselect * from inbox_email_batches where inbox_id = 212\nand updated_at >= '2026-01-28 00:00:00' order by id desc;\nselect * from inbox_emails where inbox_id = 212\nand batch_id = 95885 order by id desc;\nselect * from email_messages where origin_user_id = 143;\nselect * from activities where user_id = 143 and updated_at >= '2026-01-28 00:00:00';\nselect * from participants where activity_id = 620247;\n\nselect * from crm_profiles where user_id = 143;\n\nSELECT * FROM activities WHERE uuid_to_bin('458cf915-b914-4000-b083-5687b32b2956') = uuid; # 356001\nselect * from transcription where activity_id = 356001; # 6943\nselect * from ai_prompts where transcription_id = 6943;\nSELECT * FROM activity_summary_logs where activity_id = 356001;\n\nSELECT * FROM social_accounts WHERE sociable_id = 143;\n\n# ************************************************************************************\nSELECT * FROM activities WHERE uuid_to_bin('0164a4fb-cb95-454e-9edd-4d804e4999bd') = uuid;\n# 422515 softphone tr. 8100\n\nSELECT * FROM activities WHERE uuid_to_bin('7520add8-8d87-41a5-98e5-fc4edf96f21e') = uuid;\n# 407509 conference tr. 7670 crmId: 00UD1000002J9aTMAS\n\nselect * from ai_prompts where transcription_id IN (8100, 7670);\nselect * from activity_summary_logs where activity_id = 407509;\n\nselect * from sidekick_settings;\nselect * from default_activity_types;\n\nSELECT * FROM contacts WHERE crm_configuration_id = 39 and email = 'm.kogoj@gmx.at';\nSELECT * FROM leads WHERE crm_configuration_id = 39 and email = 'm.kogoj@gmx.at';\n\nSELECT * FROM activity_searches where user_id = 143;\nSELECT * FROM groups where team_id = 1;\n\nselect * from teams where id = 1;\nselect * from groups where team_id = 1; # 1150 - 7e75f8025c22\nselect id, name, group_id, status, deleted_at, email\nfrom users where team_id = 1 order by group_id desc ;\n\nselect * from activity_searches where id in (1977, 1978, 1979);\nselect * from activity_search_filters where activity_search_id IN (1977, 1978, 1979);\nselect * from activity_search_filters where filter = 'group_id' and value = '443f26b8-8512-437e-a9f9-7e75f8025c22'; # 10268, 10272, 10277\nselect * from nudges where activity_search_id IN (1977, 1978, 1979); # 877, 878, 879\n\nINSERT INTO `activity_search_filters`\n(`activity_search_id`, `filter`, `value`) VALUES\n(1977, 'group_id', '443f26b8-8512-437e-a9f9-7e75f8025c22'),\n(1978, 'group_id', '443f26b8-8512-437e-a9f9-7e75f8025c22'),\n(1979, 'group_id', '443f26b8-8512-437e-a9f9-7e75f8025c22')\n;\n\nselect * from crm_configurations where id = 39;\n\n\nselect sa.* from users u JOIN social_accounts sa on u.id = sa.sociable_id\nwhere u.team_id = 1;\nSELECT * FROM social_accounts WHERE sociable_id = 1635;\nSELECT * FROM users WHERE id = 1635;\n\nselect * from teams where id = 1;\nselect * from users where team_id = 1;\nselect * from team_features where team_id = 1;\nselect * from features;\n\nSELECT * FROM activity_searches where id = 1982; # 1981\nSELECT * FROM activity_search_filters WHERE activity_search_id = 1982;\n\nSELECT * FROM automated_reports where id = 68;\nUPDATE automated_reports set playbook_categories = NULL where id = 68;\nSELECT * FROM automated_report_results where id = 275;\n\nSELECT * FROM automated_reports order by id desc;\nSELECT * FROM automated_report_results order by id desc;\nselect * from activity_searches where user_id = 143;\nselect * from ask_anything_prompts;\n\nSELECT * FROM groups WHERE id = 1439;\nSELECT * FROM users WHERE group_id = 1439;\n\nselect * from permissions; # 158\nselect * from roles;\nselect * from permission_role\n\nselect * from teams where id = 1;\nselect * from groups g JOIN playbooks p on g.playbook_id = p.id where g.team_id = 1;\nselect * from groups where id = 28;\nselect * from playbooks where team_id = 1;\nselect * from playbooks where id = 179;\nselect * from playbook_categories where id = 1391;\nselect * from users where id = 143;\nselect * from crm_profiles where user_id = 143;\nselect * from activities where crm_configuration_id = 39 and type = 'conference'\nand crm_provider_id IS NOT NULL ORDER by id desc;\nselect * from activities where id = 422003; # 00UO400000pB6fpMAC\n\nSELECT ar.id, ar.uuid, ar.media_type, ar.status, a.type\nFROM automated_report_results ar\nJOIN automated_reports a ON a.id = ar.report_id\nWHERE a.type = 'ask_jiminny'\nLIMIT 10;\n\nSELECT `automated_report_results`.* FROM `automated_report_results`\nINNER JOIN `automated_reports`\n ON `automated_report_results`.`report_id` = `automated_reports`.`id`\nWHERE `automated_report_results`.`generated_at` IS NOT NULL\n AND `automated_reports`.`team_id` = 1\n AND JSON_CONTAINS(`automated_reports`.`recipients`, 1635, '$.\"users\"')\n;\n\n\nselect * from teams where id = 3143;\nselect * from crm_configurations where id = 500;\nselect * from users where name = 'Integration Account'; # 1695\nSELECT * FROM social_accounts WHERE sociable_id = 1695;\n\nselect * from activities where crm_configuration_id = 39\nand recording_state = 'recorded' and duration > 60\nand status = 'completed' and actual_start_time >= '2025-12-01';\n\nSELECT * FROM activities WHERE uuid_to_bin('458cf915-b914-4000-b083-5687b32b2956') = uuid;\n\nselect * from leads;","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-8553716549540953071
|
1126710648141684156
|
click
|
accessibility
|
NULL
|
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
AutomatedReportsRepositoryTest
Run 'AutomatedReportsRepositoryTest'
Debug 'AutomatedReportsRepositoryTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
1
103
3
34
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Kiosk\AutomatedReports;
use Carbon\CarbonImmutable;
use Carbon\CarbonInterface;
use Carbon\Exceptions\InvalidFormatException;
use DateTime;
use DateTimeInterface;
use DateTimeZone;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Jiminny\Component\ActivitySearch\FilterDefinition\InputTypeEnum;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Component\AskAnything\Dtos\AskAnythingPromptDto;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Repositories\UserRepository;
use Jiminny\Exceptions\ApplicationException;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\ModelNotFoundException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateReportJob;
use Jiminny\Models\Activity\Search;
use Jiminny\Models\AskAnything\AskAnythingPrompt;
use Jiminny\Models\AskAnything\AskAnythingPromptTarget;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AskAnythingRepository;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\StageRepository;
use Throwable;
class AutomatedReportsService
{
public const string TYPE_LOSS_ANALYSIS = 'loss_analysis';
public const string TYPE_ASK_JIMINNY = 'ask_jiminny';
/**
* Standard report types (used by kiosk for existing automated reports).
*/
// @TODO this will add filter, however if we need to control feature by FF we need conditional logic
public const array TYPES = [
['id' => 'exec_summary', 'name' => 'Exec Summary'],
['id' => 'coaching_profiles', 'name' => 'Coaching Profiles'],
['id' => 'product_feedback', 'name' => 'Product Feedback'],
['id' => self::TYPE_LOSS_ANALYSIS, 'name' => 'Loss Analysis'],
// ['id' => 'questions', 'name' => 'Questions'],
// ['id' => 'statistical_quant', 'name' => 'Statistical Quantitative'],
];
public const array ALL_TYPES = [
...self::TYPES,
['id' => self::TYPE_ASK_JIMINNY, 'name' => 'Ask Jiminny'],
];
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
/**
* Frequencies for standard (non-Ask Jiminny) reports.
*/
public const array FREQUENCIES = [
['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],
['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],
['id' => self::FREQUENCY_QUARTERLY, 'name' => 'Quarterly'],
['id' => self::FREQUENCY_ONE_OFF, 'name' => 'One-off'],
];
/**
* Frequencies for Ask Jiminny reports.
*/
public const array ASK_JIMINNY_FREQUENCIES = [
['id' => self::FREQUENCY_DAILY, 'name' => 'Daily'],
['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],
['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],
];
public const string MEDIA_TYPE_PDF = 'pdf';
public const string MEDIA_TYPE_PODCAST = 'podcast';
public const array MEDIA_TYPES = [self::MEDIA_TYPE_PDF, self::MEDIA_TYPE_PODCAST];
public const array MEDIA_TYPE_OBJECT_PDF = ['id' => self::MEDIA_TYPE_PDF, 'name' => 'PDF'];
public const array MEDIA_TYPE_OBJECT_PODCAST = ['id' => self::MEDIA_TYPE_PODCAST, 'name' => 'Podcast'];
public const array MEDIA_TYPE_OBJECTS = [self::MEDIA_TYPE_OBJECT_PDF, self::MEDIA_TYPE_OBJECT_PODCAST];
public const array CALL_TYPE_CONFERENCE = ['id' => 'conference', 'name' => 'Conference'];
public const array CALL_TYPE_DIALER = ['id' => 'dialer', 'name' => 'Dialer'];
public const int SENT_REPORT_AT_HOURS = 5;
public const string PDF_KEY = 'pdf';
public const string AUDIO_KEY = 'audio';
private const array ALL_FREQUENCIES = [
['id' => self::FREQUENCY_DAILY, 'name' => 'Daily'],
['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],
['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],
['id' => self::FREQUENCY_QUARTERLY, 'name' => 'Quarterly'],
['id' => self::FREQUENCY_ONE_OFF, 'name' => 'One-off'],
];
private const string S3_DIR = 'reports';
private const array FILE_EXTENSIONS_VARIANTS = ['html', 'MD', 'pdf'];
private const array FILE_PODCAST_EXTENSIONS_VARIANTS = ['json', 'mp3', 'ssml'];
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly GroupRepository $groupRepository,
private readonly UserRepository $userRepository,
private readonly StageRepository $stageRepository,
private readonly DealStagesService $dealStagesService,
private readonly RecipientsService $recipientsService,
private readonly AutomatedReportsRepository $automatedReportsRepository,
private readonly Webhook $webhookService,
private readonly BusDispatcher $dispatcher,
private readonly ActivityTypeService $activityTypeService,
private readonly PlaybookCategoryRepository $playbookCategoryRepository,
private readonly AskAnythingPromptService $askAnythingPromptService,
private readonly SearchRepository $activitySearchRepository,
private readonly AskAnythingRepository $askAnythingRepository,
) {
}
public static function getTypes(): array
{
$types = self::TYPES;
return array_map(static function ($type) {
return $type['id'];
}, $types);
}
public static function getCallTypes(): array
{
return array_map(static function ($callType) {
return $callType['id'];
}, [self::CALL_TYPE_CONFERENCE, self::CALL_TYPE_DIALER]);
}
public static function getFrequencies(): array
{
return array_map(static function ($frequency) {
return $frequency['id'];
}, self::FREQUENCIES);
}
// front-facing structure
public function getReportEnabledFieldData(bool $value = false): array
{
return [
'id' => 'report_enabled',
'label' => '',
'inputType' => InputTypeEnum::TOGGLE,
'value' => $value,
];
}
// Organizations = Teams
public function getOrganizationFieldData(?string $value = null, bool $shortVersion = false): array
{
$options = $this->getTeams();
if ($shortVersion) {
return [
'id' => 'organization',
'label' => 'Organization',
'options' => $options,
];
}
return [
'id' => 'organization',
'label' => 'Organization',
'inputType' => InputTypeEnum::DROPDOWN,
'required' => true,
'placeholder' => 'Select',
'options' => $options,
'value' => $value,
'dependencies' => [
'teams',
'deal_stage_at_call',
'current_deal_stage',
'recipients',
ActivityTypeService::PLAYBOOK_CATEGORIES_KEY,
],
'dependsOn' => [],
];
}
// Teams = Groups
public function getTeamFieldData(array $options = [], array $value = [], bool $shortVersion = false): array
{
if ($shortVersion) {
return [
'id' => 'teams',
'label' => 'Team',
'options' => $options,
];
}
return [
'id' => 'teams',
'label' => 'Team',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'required' => false,
'placeholder' => 'Select',
'options' => $options,
'value' => $value, // value should be an array of objects {id, name}
'dependencies' => [ActivityTypeService::PLAYBOOK_CATEGORIES_KEY],
'dependsOn' => [],
];
}
public function getReportTypeFieldData(?string $value = null, bool $shortVersion = false, ?Team $team = null): array
{
$types = [];
if ($team instanceof Team) {
if ($team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {
$types = self::TYPES;
}
if ($team->hasFeature(FeatureEnum::ASK_JIMINNY_REPORTS)) {
$types[] = ['id' => self::TYPE_ASK_JIMINNY, 'name' => 'Ask Jiminny'];
}
} else {
$types = self::TYPES;
}
if ($shortVersion) {
return [
'id' => 'report_type',
'label' => 'Report Type',
'options' => $types,
];
}
return [
'id' => 'report_type',
'label' => 'Report Type',
'inputType' => InputTypeEnum::DROPDOWN,
'required' => true,
'placeholder' => 'Select',
'options' => $types,
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getFrequencyFieldData(?string $value = null): array
{
return [
'id' => 'frequency',
'label' => 'Frequency',
'inputType' => InputTypeEnum::DROPDOWN,
'required' => true,
'placeholder' => 'Select',
'options' => self::FREQUENCIES,
'value' => $value,
'dependencies' => ['period'],
'dependsOn' => [],
];
}
public function getPeriodFieldData(?string $valueStartDate = null, ?string $valueEndDate = null): array
{
return [
'id' => 'period',
'label' => 'Select one-off period',
'inputType' => InputTypeEnum::DATE_RANGE,
'required' => true,
'placeholder' => 'Select',
'value' => ['startDate' => $valueStartDate, 'endDate' => $valueEndDate],
'queryParams' => [
'startDate' => 'start_date_period',
'endDate' => 'end_date_period',
],
'dependencies' => [],
'dependsOn' => ['frequency'],
];
}
public function getActivityTypesFieldData(?Team $team = null, array $value = [], array $teamsFilter = []): array
{
return $this->activityTypeService->getActivityTypeFieldData(team: $team, value: $value, groupIds: $teamsFilter);
}
public function getDealStageAtCallFieldData(?Team $team = null, array $value = []): array
{
return $this->dealStagesService->getDealStageAtCallFieldData(team: $team, value: $value);
}
public function getCurrentDealStageFieldData(?Team $team = null, array $value = []): array
{
return $this->dealStagesService->getCurrentDealStageFieldData(team: $team, value: $value);
}
public function getDealValueFieldData(?int $valueMin = null, ?int $valueMax = null): array
{
return [
'id' => 'deal_value',
'label' => 'Deal Value',
'inputType' => InputTypeEnum::INTEGER_RANGE,
'required' => false,
'value' => ['min' => $valueMin, 'max' => $valueMax],
'queryParams' => [
'min' => 'min_deal_value',
'max' => 'max_deal_value',
],
'dependencies' => [],
'dependsOn' => [],
];
}
public function getCallTypeFieldData(bool $conferenceOn = false, bool $dialerOn = false): array
{
$value = [];
$conferenceOn && $value[] = self::CALL_TYPE_CONFERENCE;
$dialerOn && $value[] = self::CALL_TYPE_DIALER;
return [
'id' => 'call_type',
'label' => 'Call Type',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'required' => true,
'options' => [
self::CALL_TYPE_CONFERENCE,
self::CALL_TYPE_DIALER,
],
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getMediaTypeFieldData(?AutomatedReport $report = null): array
{
$value = [];
if ($report) {
$value = $this->transformMediaTypes($report);
}
return [
'id' => 'media_types',
'label' => 'Export as',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'required' => true,
'options' => self::MEDIA_TYPE_OBJECTS,
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getCallDurationFieldData(?int $valueMin = null, ?int $valueMax = null): array
{
return [
'id' => 'call_duration',
'label' => 'Call Duration',
'inputType' => InputTypeEnum::INTEGER_RANGE,
'required' => false,
'value' => ['min' => $valueMin, 'max' => $valueMax],
'queryParams' => [
'min' => 'min_call_duration',
'max' => 'max_call_duration',
],
'dependencies' => [],
'dependsOn' => [],
];
}
public function getRecipientsFieldData(?Team $team = null, array $value = []): array
{
return $this->recipientsService->getRecipientsFieldData(team: $team, value: $value);
}
public function getJiminnyRecipientsFieldData(array $value = []): array
{
return $this->recipientsService->getJiminnyRecipientsFieldData($value);
}
public function getAdditionalPromptInputFieldData(?string $value = null): array
{
return [
'id' => 'additional_prompt_input',
'label' => 'Special requirements',
'inputType' => InputTypeEnum::TEXTAREA,
'required' => false,
'placeholder' => 'What should be the focus of the report?',
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getCustomReportNameFieldData(?string $value = null): array
{
return [
'id' => 'custom_name',
'label' => 'Custom report name',
'inputType' => InputTypeEnum::TEXT,
'required' => false,
'placeholder' => 'Enter custom name',
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
// data providers
public function getTeams(): array
{
$teams = $this->teamRepository->getTeamsForKiosk(status: Team::STATUS_ACTIVE);
$teamData = [];
foreach ($teams as $team) {
if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {
continue;
}
$teamData[] = $this->transformTeam($team);
}
return $teamData;
}
public function getTeamGroups(string $teamUuid): array
{
$data = [];
$team = $this->getTeam($teamUuid);
if ($team !== null) {
$groups = $team->groups()->get();
foreach ($groups as $group) {
$data[] = [
'id' => $group->getUuid(),
'name' => $group->getName(),
];
}
}
return $data;
}
public function getTeamsGroupsOptions(array $filterTeamUuids = []): array
{
$data = [];
$teams = $this->getTeams();
foreach ($teams as $team) {
if (! empty($filterTeamUuids) && ! in_array($team['id'], $filterTeamUuids, true)) {
continue;
}
$data[] = [
'label' => $team['name'],
'groups' => $this->getTeamGroups($team['id']),
];
}
return $data;
}
public function getTeam(string $teamUuid): ?Team
{
return $this->teamRepository->idOrUuid($teamUuid);
}
public function getTeamById(int $teamId): ?Team
{
return $this->teamRepository->find($teamId);
}
public function getGroupsUuids(AutomatedReport $report): array
{
$uuids = [];
$reportGroups = $report->getGroups();
foreach ($reportGroups as $groupId) {
if ($group = $this->groupRepository->find($groupId)) {
$uuids[] = $group->getUuid();
}
}
return $uuids;
}
public function getPlaybookCategoriesUuids(AutomatedReport $report): array
{
$uuids = [];
$playbookCategories = $report->getPlaybookCategories();
foreach ($playbookCategories as $id) {
if ($category = $this->playbookCategoryRepository->find($id)) {
$uuids[] = $category->getUuid();
}
}
return $uuids;
}
public function getDealAtCallStagesUuids(AutomatedReport $report): array
{
$uuids = [];
$reportStages = $report->getDealAtCallStages();
foreach ($reportStages as $id) {
if ($stage = $this->stageRepository->find($id)) {
$uuids[] = $stage->getUuid();
}
}
return $uuids;
}
public function getCurrentDealStagesUuids(AutomatedReport $report): array
{
$uuids = [];
$reportStages = $report->getCurrentDealStages();
foreach ($reportStages as $id) {
if ($stage = $this->stageRepository->find($id)) {
$uuids[] = $stage->getUuid();
}
}
return $uuids;
}
public function getUsersUuids(AutomatedReport $report): array
{
return $this->extractUserUuids($report->getRecipients());
}
public function getJiminnyUsersUuids(AutomatedReport $report): array
{
return $this->extractUserUuids($report->getJiminnyRecipients());
}
/**
* @param array<string, mixed> $recipients
*/
private function extractUserUuids(array $recipients): array
{
$userIds = $recipients['users'] ?? [];
return collect($userIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->map(fn (UserContract $user) => $user->getUuid())
->values()
->all();
}
// get mail data
public function getRecipientUsers(AutomatedReport $report): array
{
return $this->buildRecipientUsers($report->getRecipients());
}
/**
* @return array<UserContract>
*/
public function getRecipientUserObjects(AutomatedReport $report): array
{
$userIds = $report->getRecipients()['users'] ?? [];
return collect($userIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->values()
->all();
}
private function getJiminnyRecipientUsers(AutomatedReport $report): array
{
return $this->buildRecipientUsers($report->getJiminnyRecipients());
}
/**
* @param array<string, mixed> $recipients
*/
private function buildRecipientUsers(array $recipients): array
{
$userIds = $recipients['users'] ?? [];
return collect($userIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->map(fn (UserContract $user) => [
'email' => $user->getEmailAddress(),
'name' => $user->getName(),
'timezone' => $user->getTimezone()->getName(),
])
->values()
->all();
}
public function getValidRecipientUsers(AutomatedReport $report, bool $includeJiminny = false): array
{
if ($report->isAskJiminnyReport()) {
$recipients = $this->resolveAskJiminnyRecipients($report);
} else {
$recipients = $this->getRecipientUsers($report);
if ($includeJiminny) {
$recipients = array_merge($recipients, $this->getJiminnyRecipientUsers($report));
}
}
$emails = [];
return array_values(array_filter(
$recipients,
static function ($recipient) use (&$emails) {
if (empty($recipient['email']) || in_array($recipient['email'], $emails, true)) {
return false;
}
$emails[] = $recipient['email'];
return true;
}
));
}
private function resolveAskJiminnyRecipients(AutomatedReport $report): array
{
$recipients = [];
$creator = $report->getCreator();
if ($creator !== null) {
$recipients[] = [
'email' => $creator->getEmailAddress(),
'name' => $creator->getName(),
'timezone' => $creator->getTimezone()->getName(),
];
}
return array_merge(
$recipients,
$this->buildRecipientUsers($report->getRecipients()),
$this->getGroupRecipientUsers($report),
);
}
private function getGroupRecipientUsers(AutomatedReport $report): array
{
$users = [];
foreach ($report->getGroups() as $groupId) {
$group = $this->groupRepository->find($groupId);
if ($group === null) {
continue;
}
foreach ($group->getMembers() as $member) {
$users[] = [
'email' => $member->getEmailAddress(),
'name' => $member->getName(),
'timezone' => $member->getTimezone()->getName(),
];
}
}
return $users;
}
public function getReportTypeName(AutomatedReportResult $report): string
{
$type = $report->getReport()->getType();
$getType = $this->transformReportType($type);
return $getType['name'];
}
public function getReportPeriodName(AutomatedReportResult $report): string
{
$from = $report->getFromDate();
$to = $report->getToDate();
$frequency = $report->getReport()->getFrequency();
if ($from === null || $to === null) {
if (! $report->getReport()->isAskJiminnyReport()) {
$invalidPeriod = $from === null ? 'from' : 'to';
throw new ApplicationException('Report period is invalid: ' . $invalidPeriod);
}
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
}
return $this->formatReportPeriodName($frequency, $from, $to);
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function getReportTeamsName(AutomatedReportResult $report): string
{
$groups = $report->getGroups();
if (empty($groups)) {
return 'All';
}
// Get group names from repository
$groupNames = [];
foreach ($groups as $groupId) {
$group = $this->groupRepository->find($groupId);
if ($group) {
$groupNames[] = $group->getName();
}
}
if (count($groupNames) === 1) {
// Single team format
$teamsName = $groupNames[0];
} else {
// Multiple teams format
$teamsName = implode(', ', $groupNames);
}
return $teamsName;
}
public function getReportFileName(AutomatedReportResult $report): string
{
$customName = $report->getReport()->getCustomName();
$periodName = $this->getReportPeriodName($report);
$filenameSuffix = $this->getFilenameSuffix($report);
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
return $this->sanitizeFileName("{$customName} - {$periodName}");
}
$baseName = $this->getReportTypeName($report);
if ($filenameSuffix) {
$baseName .= " {$filenameSuffix}";
}
return $this->sanitizeFileName("{$baseName} - {$periodName} - {$this->getReportTeamsName($report)}");
}
public function getReportFileNameWithExtension(AutomatedReportResult $result): string
{
$extension = $this->getMediaTypeMetadata($result)['extension'];
return $this->getReportFileName($result) . '.' . $extension;
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
public function isUserRecipientOfReport(User $user, AutomatedReport $report): bool
{
$recipients = array_map('intval', $report->getRecipients()['users'] ?? []);
return in_array($user->getId(), $recipients);
}
public function transformReportResults(Collection $automatedReportResults): array
{
$data = [];
foreach ($automatedReportResults as $automatedReportResult) {
/** @var AutomatedReportResult $automatedReportResult */
$report = $automatedReportResult->getReport();
$createdBy = $report->getCreator();
$creator = [
'id' => $createdBy?->getUuid(),
'name' => $createdBy?->getName(),
'email' => $createdBy?->getEmailAddress(),
'photoUrl' => $createdBy?->getPhotoUrl(),
];
$recipients = $this->buildRecipients($report);
$data[] = [
'id' => $automatedReportResult->getUuid(),
'name' => $automatedReportResult->getName(),
'frequency' => $this->transformFrequency($report->getFrequency()),
'recipients' => [
...array_values($this->transformGroups(team: $report->getTeam(), groupsIds: $report->getGroups())),
...array_values($this->transformRecipients($report->getRecipients())),
],
'report_type' => $this->transformReportType($report->getType()),
'media_type' => $automatedReportResult->getMediaType(),
'downloadUrl' => $this->generateReportResultDownloadUrl($automatedReportResult),
'viewUrl' => $this->generateReportResultViewUrl($automatedReportResult),
'generated_at' => $automatedReportResult->getGeneratedAt()?->toIso8601String(),
'creator' => $creator,
];
}
return $data;
}
public function hasCallTypeConference(AutomatedReport $report): bool
{
return in_array(self::CALL_TYPE_CONFERENCE['id'], $report->getCallTypes(), true);
}
public function hasCallTypeDialer(AutomatedReport $report): bool
{
return in_array(self::CALL_TYPE_DIALER['id'], $report->getCallTypes(), true);
}
// transformers
private function transformTeam(Team $team): array
{
if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {
return [];
}
return [
'id' => $team->getUuid(),
'name' => $team->getName(),
];
}
private function transformReportFullView(AutomatedReport $report): array
{
$base = $this->transformReportBase($report);
return $report->getType() === self::TYPE_ASK_JIMINNY
? $base + $this->transformAskJiminnyFields($report)
: $base + $this->transformStandardReportFields($report);
}
private function transformReportBase(AutomatedReport $report): array
{
return [
'id' => $report->getUuid(),
'organization' => $this->transformOrganization(team: $report->getTeam()),
'report_type' => $this->transformReportType($report->getType()),
'frequency' => $this->transformFrequency($report->getFrequency()),
];
}
private function transformStandardReportFields(AutomatedReport $report): array
{
$team = $report->getTeam();
return [
'report_enabled' => $report->getStatus(),
'start_date_period' => $report->getFrom()?->format('Y-m-d H:i:s'),
'end_date_period' => $report->getTo()?->format('Y-m-d H:i:s'),
'deal_value_min' => $report->getDealValueMin(),
'deal_value_max' => $report->getDealValueMax(),
'call_types' => $this->transformCallType($report->getCallTypes()),
'media_types' => $this->transformMediaTypes($report),
'call_duration_min' => $this->transformDurationToMinutes($report->getCallDurationMin()),
'call_duration_max' => $this->transformDurationToMinutes($report->getCallDurationMax()),
'teams' => $this->transformGroups(team: $team, groupsIds: $report->getGroups()),
'deal_at_call_stages' => $this->transformStages(team: $team, stagesIds: $report->getDealAtCallStages()),
'current_deal_stages' => $this->transformStages(team: $team, stagesIds: $report->getCurrentDealStages()),
'recipients' => $this->transformRecipients($report->getRecipients()),
'created_by' => $this->transformCreator($report->getCreator()),
'additional_prompt_input' => $report->getAdditionalPromptInput(),
'custom_name' => $report->getCustomName(),
'created_at' => $report->getCreatedAt()->format('Y-m-d H:i:s'),
'updated_at' => $report->getUpdatedAt()->format('Y-m-d H:i:s'),
'deleted_at' => $report->getDeletedAt()?->format('Y-m-d H:i:s'),
];
}
private function transformAskJiminnyFields(AutomatedReport $report): array
{
$team = $report->getTeam();
$creatorId = $report->getAttribute('created_by');
$explicitUserIds = array_values(array_filter(
$report->getRecipients()['users'] ?? [],
static fn ($id) => $id !== $creatorId
));
return [
'report_name' => $report->getCustomName(),
'enabled' => $report->getStatus(),
'share_teams' => $this->transformGroups(team: $team, groupsIds: $report->getGroups()),
'share_users' => $this->transformRecipients(['users' => $explicitUserIds]),
'saved_search' => $this->transformSafeSearch($report->getSavedSearch()),
'ask_jiminny_prompt' => $this->transformAskJiminnyPrompt($report->getAskAnythingPrompt()),
'expires_on' => $report->getExpiresAt()?->format('Y-m-d'),
];
}
private function transformOrganization(?Team $team): array
{
return [
'id' => $team?->getUuid(),
'name' => $team?->getName(),
];
}
private function transformReportType(string $type): array
{
foreach (self::ALL_TYPES as $typeItem) {
if ($typeItem['id'] === $type) {
return $typeItem;
}
}
return [];
}
private function transformCallType(array $types): array
{
$result = [];
$callTypes = [self::CALL_TYPE_CONFERENCE, self::CALL_TYPE_DIALER];
foreach ($types as $type) {
foreach ($callTypes as $callTypeItem) {
if ($callTypeItem['id'] === $type) {
$result[] = $callTypeItem;
break;
}
}
}
return $result;
}
private function transformMediaTypes(AutomatedReport $report): array
{
$values = [];
foreach ($report->getMediaTypes() as $mediaType) {
if (! in_array($mediaType, self::MEDIA_TYPES, true)) {
continue;
}
$values[] = match ($mediaType) {
self::MEDIA_TYPE_PDF => self::MEDIA_TYPE_OBJECT_PDF,
self::MEDIA_TYPE_PODCAST => self::MEDIA_TYPE_OBJECT_PODCAST,
};
}
return $values;
}
private function transformFrequency(string $frequency): array
{
foreach (self::ALL_FREQUENCIES as $frequencyItem) {
if ($frequencyItem['id'] === $frequency) {
return $frequencyItem;
}
}
return [];
}
public function transformDurationToMinutes(?int $duration): ?int
{
if (! $duration) {
return null;
}
return (int) ($duration / 60);
}
private function transformGroups(?Team $team, array $groupsIds): array
{
if (empty($groupsIds) || ! $team) {
return [];
}
$data = [];
foreach ($groupsIds as $groupId) {
$group = $team->groups()->where('id', $groupId)->first();
if ($group) {
$data[] = [
'id' => $group->getUuid(),
'name' => $group->getName(),
'photoUrl' => $group->getPhotoUrl(),
];
}
}
return $data;
}
private function transformStages(?Team $team, array $stagesIds): array
{
if (empty($stagesIds) || ! $team) {
return [];
}
$data = [];
foreach ($stagesIds as $stageId) {
$stage = $team->stages()->where('id', $stageId)->first();
if ($stage) {
$data[] = [
'id' => $stage->getUuid(),
'name' => $stage->getName(),
];
}
}
return $data;
}
private function transformRecipients(array $recipients): array
{
$users = [];
foreach ($recipients['users'] ?? [] as $userId) {
$users[] = $this->transformUser($userId);
}
return $users;
}
private function transformCreator(?User $user): ?array
{
if ($user === null) {
return null;
}
return $this->transformUser($user->getId());
}
private function transformAskJiminnyPrompt(?AskAnythingPrompt $prompt): ?array
{
if ($prompt === null) {
return null;
}
return [
'id' => $prompt->getUuid(),
'name' => $prompt->getTitle(),
];
}
private function transformSafeSearch(?Search $search): ?array
{
if ($search === null) {
return null;
}
return [
'id' => $search->getUuid(),
'name' => $search->getName(),
];
}
private function transformUser(int $userId): array
{
/* @var ?User $user */
$user = $this->userRepository->find($userId);
return [
'id' => $user?->getUuid(),
'name' => $user?->getName(),
'email' => $user?->getEmailAddress(),
'photoUrl' => $user?->getPhotoUrl(),
];
}
public function create(array $data): array
{
$validatedData = $this->validateAndTransformData($data);
$validatedData['created_by'] = auth()->id();
$automatedReport = $this->automatedReportsRepository->create($validatedData);
$this->generateOneOffReport($automatedReport);
return $this->transformReportFullView($automatedReport);
}
public function update(string $uuid, array $data): array
{
$validatedData = $this->validateAndTransformData($data);
$report = $this->automatedReportsRepository->findByUuid($uuid);
if (! $report) {
throw new InvalidArgumentException('Report not found');
}
$oldCustomName = $report->getCustomName();
$automatedReport = $this->automatedReportsRepository->update($report, $validatedData);
if ($oldCustomName !== $automatedReport->getCustomName()) {
$this->updateResultNames($automatedReport);
}
$this->generateOneOffReport($automatedReport);
return $this->transformReportFullView($automatedReport);
}
/**
* Create an Ask Jiminny report.
*/
public function createAskJiminnyReport(array $data, User $creator): array
{
$validatedData = $this->validateAskJiminnyReportData($data, $creator);
$validatedData['created_by'] = $creator->getId();
$automatedReport = $this->automatedReportsRepository->create($validatedData);
return $this->transformReportFullView($automatedReport);
}
/**
* Update an Ask Jiminny report.
*/
public function updateAskJiminnyReport(AutomatedReport $report, array $data, User $user): array
{
if (! $report->isAskJiminnyReport()) {
throw new InvalidArgumentException('Report is not an Ask Jiminny report');
}
$validatedData = $this->validateAskJiminnyReportData($data, $user);
$oldCustomName = $report->getCustomName();
$automatedReport = $this->automatedReportsRepository->update($report, $validatedData);
if ($oldCustomName !== $automatedReport->getCustomName()) {
$this->updateResultNames($automatedReport);
}
return $this->transformReportFullView($automatedReport);
}
public function updateAskJiminnyReportStatus(AutomatedReport $report, bool $status): array
{
$this->automatedReportsRepository->update($report, ['status' => $status]);
return $this->transformReportFullView($report->fresh());
}
/**
* Validate and transform data for Ask Jiminny reports.
*/
private function validateAskJiminnyReportData(array $data, User $user): array
{
// Validate name
$name = trim($data['report_name'] ?? '');
if (empty($name)) {
throw new InvalidArgumentException('Report name is required');
}
if (mb_strlen($name) > 50) {
throw new InvalidArgumentException('Report name must be 50 characters or less');
}
// Validate frequency (only daily, weekly, monthly for Ask Jiminny)
$frequency = $data['frequency'] ?? null;
$askJiminnyFrequencies = [self::FREQUENCY_DAILY, self::FREQUENCY_WEEKLY, self::FREQUENCY_MONTHLY];
if (! in_array($frequency, $askJiminnyFrequencies, true)) {
throw new InvalidArgumentException('Frequency must be daily, weekly, or monthly');
}
// Validate expiration date
$expiresAt = $data['expires_on'] ?? null;
if (empty($expiresAt)) {
throw new InvalidArgumentException('Expiration date is required');
}
try {
$expiresAtDate = Carbon::parse($expiresAt);
} catch (InvalidFormatException $e) {
throw new InvalidArgumentException('Expiration date format is invalid');
}
$maxExpiration = Carbon::now()->addYear()->endOfDay();
if ($expiresAtDate->gt($maxExpiration)) {
throw new InvalidArgumentException('Expiration date cannot be more than 1 year from now');
}
if ($expiresAtDate->isPast()) {
throw new InvalidArgumentException('Expiration date cannot be in the past');
}
// Validate saved search
$activitySearchId = $data['saved_search'] ?? null;
if (empty($activitySearchId)) {
throw new InvalidArgumentException('Saved search is required');
}
$savedSearch = $this->activitySearchRepository->findByUuidAndUser($activitySearchId, $user);
if (! $savedSearch) {
throw new InvalidArgumentException('Saved search not found or does not belong to you');
}
// Validate saved prompt
$askAnythingPromptId = $data['ask_jiminny_prompt'] ?? null;
if (empty($askAnythingPromptId)) {
throw new InvalidArgumentException('Ask Jiminny prompt is required');
}
$prompt = $this->askAnythingRepository->getPromptByUuid($askAnythingPromptId);
if (! $prompt) {
throw new InvalidArgumentException('Ask Jiminny prompt not found');
}
// Validate status
$status = $data['enabled'] ?? false;
$recipientUserIds = [$user->getId()];
if (! empty($data['share_users'])) {
$sharedUserIds = $this->validateAndGetUserIdsByTeam(
$user->team,
(array) $data['share_users']
);
$recipientUserIds = array_merge($recipientUserIds, $sharedUserIds);
}
$sharedGroupIds = [];
if (! empty($data['share_teams'])) {
$sharedGroupIds = $this->validateAndGetGroupIds($user->team, (array) $data['share_teams']);
}
$recipientUserIds = array_values(array_unique($recipientUserIds));
return [
'team_id' => $user->getTeamId(),
'type' => self::TYPE_ASK_JIMINNY,
'status' => (bool) $status,
'frequency' => $frequency,
'custom_name' => $name,
'activity_search_id' => $savedSearch->getId(),
'ask_anything_prompt_id' => $prompt->getId(),
'expires_at' => $expiresAtDate->toDateString(),
'media_types' => [self::MEDIA_TYPE_PDF],
'call_types' => [],
'recipients' => ['users' => $recipientUserIds],
'groups' => $sharedGroupIds,
];
}
public static function getAskJiminnyFrequencies(): array
{
return array_map(static function ($frequency) {
return $frequency['id'];
}, self::ASK_JIMINNY_FREQUENCIES);
}
public function getAskJiminnyReportFilters(User $user): array
{
$savedSearches = $this->activitySearchRepository->findByUserOrderedByName($user)
->map(fn (Search $search) => [
'id' => $search->getUuid(),
'name' => $search->getName(),
])
->values()->all();
$prompts = collect(
$this->askAnythingPromptService->get($user, AskAnythingPromptTarget::on_demand)
)->map(fn (AskAnythingPromptDto $prompt) => [
'id' => $prompt->id,
'name' => $prompt->title,
])->values()->all();
return [
[
'id' => 'prompt',
'label' => 'Prompt',
'options' => $prompts,
],
[
'id' => 'saved_search',
'label' => 'Saved Search',
'options' => $savedSearches,
],
];
}
public function getAskJiminnyReportFormData(User $user, ?AutomatedReport $report = null): array
{
$team = $user->getTeam();
$userTimezone = $user->getTimezone();
$savedSearches = $this->activitySearchRepository->findByUserOrderedByName($user)
->map(fn (Search $search) => [
'id' => $search->getUuid(),
'name' => $search->getName(),
])
->values()->all();
$prompts = collect(
$this->askAnythingPromptService->get($user, AskAnythingPromptTarget::on_demand)
)->map(fn (AskAnythingPromptDto $prompt) => [
'id' => $prompt->id,
'name' => $prompt->title,
])->values()->all();
$teamGroups = $this->groupRepository->getAllByTeam($team)->map(fn ($group) => [
'id' => $group->getUuid(),
'name' => $group->getName(),
])->values()->all();
$shareUsers = $this->recipientsService->getRecipientsFieldData(team: $team)['options'] ?? [];
$sharedTeamsValue = [];
$sharedUsersValue = [];
if ($report) {
$sharedTeamsValue = $this->transformGroups($team, $report->getGroups());
$recipientUserIds = $report->getRecipients()['users'] ?? [];
$creatorId = $report->getAttribute('created_by');
$sharedUserIds = array_values(array_filter(
$recipientUserIds,
static fn ($id) => $id !== $creatorId
));
$sharedUsersValue = collect($sharedUserIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->map(fn (User $u) => [
'id' => $u->getUuid(),
'name' => $u->getName(),
])
->values()
->all();
}
return [
'fields' => [
[
'id' => 'enabled',
'inputType' => InputTypeEnum::TOGGLE,
'label' => '',
'value' => $report?->getStatus() ?? false,
],
[
'id' => 'report_name',
'inputType' => InputTypeEnum::TEXT,
'label' => 'Name',
'placeholder' => 'Enter name',
'required' => true,
'validation' => ['maxLength' => 50],
'value' => $report?->getCustomName() ?? '',
],
[
'id' => 'frequency',
'inputType' => InputTypeEnum::DROPDOWN,
'label' => 'Frequency',
'required' => true,
'placeholder' => 'Select',
'options' => self::ASK_JIMINNY_FREQUENCIES,
'value' => $report ? $this->transformFrequency($report->getFrequency()) : null,
],
[
'id' => 'expires_on',
'inputType' => InputTypeEnum::DATE,
'label' => 'Expires on',
'required' => true,
'placeholder' => 'Select',
'validation' => [
'minDate' => now($userTimezone)->toDateString(),
'maxDate' => now($userTimezone)->addYear()->toDateString(),
],
'value' => $report?->getExpiresAt()?->toDateString(),
],
[
'id' => 'share_teams',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'label' => 'Team',
...
|
66554
|
|
68584
|
1557
|
2
|
2026-04-22T06:21:55.194798+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-22/1776 /Users/lukas/.screenpipe/data/data/2026-04-22/1776838915194_m2.jpg...
|
Slack
|
platform-inner-team (Channel) - Jiminny Inc - Slac platform-inner-team (Channel) - Jiminny Inc - Slack...
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Switch workspaces… (Jiminny Inc) Has new messages
Switch workspaces… (Jiminny Inc) Has new messages
Home
Home
DMs
DMs
Activity
Activity
Files
Files
Later
Later
More…
More
Unreads
Threads
Huddles
Drafts & sent
Directories
jiminny-x-integration-app
platform-inner-team
ai-chapter
alerts
backend
c-learning-people
confusion-clinic
curiosity_lab
engineering
frontend
general
infra-changes
jiminny-bg
people-with-copilot-licences
people-with-zoom-phone-licences
platform-team
platform-tickets
product_launches
random
releases
sofia-office
support
thank-yous
the_people_of_jiminny
Aneliya Angelova
,
Nikolay Yankov
,
Steliyan Georgiev
Aneliya Angelova
Mario Georgiev
Nikolay Yankov
Todor Stamatov
Gabriela Dureva
Petko Kashinski
Vasil Vasilev
Nikolay Nikolov
Galya Dimitrova
Stefka Stoyanova
Stoyan Tomov
Stoyan Tanev
Nikolay Ivanov
Ves
Jira Cloud
Toast
Messages
Messages
Channel Overview
Channel Overview
More
Add and Edit Channel Tabs
Canvas
List
Folder
Jump to date
Stefka Stoyanova
Yesterday at 7:18:09 PM
7:18 PM
Нещо взе да се случва с Хъбспот на Прод, май трябва да се погледне
https://jiminny.sentry.io/issues/7007366572/?environment=production&environment=produ[…]etric-issue-contributing-issues-issue-stream&statsPeriod=24h
https://jiminny.sentry.io/issues/7007366572/?environment=production&environment=produ[…]etric-issue-contributing-issues-issue-stream&statsPeriod=24h
SevenShores\Hubspot\Exceptions\BadRequest
SevenShores\Hubspot\Exceptions\BadRequest
/app/Services/Crm/Hubspot/Pagination/HubspotPaginationService.php in Jiminny\Services\Crm\Hubspot\Pagination\HubspotPaginationService::executeSearchRequest
Client error: `POST
https://api.hubapi.com/crm/v3/objects/contact/search
` resulted in a `429 Too Many Requests` response:
{"status":"error","message":"You have reached your secondly limit.","errorType":"RATE_LIMIT","correlationId":"019db0d4-8 (truncated...
Events:
6290
State:
Ongoing
First Seen:
2025-11-08
Resolve
Resolve
Archive...
|
[{"role":"AXPopUpButton","text [{"role":"AXPopUpButton","text":"Switch workspaces… (Jiminny Inc) Has new messages","depth":14,"bounds":{"left":0.0056515955,"top":0.058260176,"width":0.011968086,"height":0.028731046},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Home","depth":14,"bounds":{"left":0.0029920214,"top":0.10055866,"width":0.017287234,"height":0.054269753},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXStaticText","text":"Home","depth":16,"bounds":{"left":0.0066489363,"top":0.13806863,"width":0.009973404,"height":0.0103751},"role_description":"text"},{"role":"AXRadioButton","text":"DMs","depth":14,"bounds":{"left":0.0029920214,"top":0.15482841,"width":0.017287234,"height":0.054269753},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"DMs","depth":16,"bounds":{"left":0.0076462766,"top":0.19233839,"width":0.007978723,"height":0.0103751},"role_description":"text"},{"role":"AXRadioButton","text":"Activity","depth":14,"bounds":{"left":0.0029920214,"top":0.20909816,"width":0.017287234,"height":0.054269753},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Activity","depth":16,"bounds":{"left":0.004986702,"top":0.24660814,"width":0.012965426,"height":0.0103751},"role_description":"text"},{"role":"AXRadioButton","text":"Files","depth":14,"bounds":{"left":0.0029920214,"top":0.26336792,"width":0.017287234,"height":0.054269753},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Files","depth":16,"bounds":{"left":0.0076462766,"top":0.3008779,"width":0.0076462766,"height":0.0103751},"role_description":"text"},{"role":"AXRadioButton","text":"Later","depth":14,"bounds":{"left":0.0029920214,"top":0.31763768,"width":0.017287234,"height":0.054269753},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Later","depth":16,"bounds":{"left":0.00731383,"top":0.35514766,"width":0.008643617,"height":0.0103751},"role_description":"text"},{"role":"AXRadioButton","text":"More…","depth":14,"bounds":{"left":0.0029920214,"top":0.3719074,"width":0.017287234,"height":0.054269753},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More","depth":16,"bounds":{"left":0.006981383,"top":0.4094174,"width":0.008976064,"height":0.0103751},"role_description":"text"},{"role":"AXStaticText","text":"Unreads","depth":21,"bounds":{"left":0.036901597,"top":0.10055866,"width":0.018284574,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Threads","depth":21,"bounds":{"left":0.036901597,"top":0.12290503,"width":0.01761968,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Huddles","depth":21,"bounds":{"left":0.036901597,"top":0.1452514,"width":0.017952127,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Drafts & sent","depth":21,"bounds":{"left":0.036901597,"top":0.16759777,"width":0.028922873,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Directories","depth":21,"bounds":{"left":0.036901597,"top":0.18994413,"width":0.023936171,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"jiminny-x-integration-app","depth":23,"bounds":{"left":0.042220745,"top":0.28411812,"width":0.043882977,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"platform-inner-team","depth":23,"bounds":{"left":0.042220745,"top":0.3064645,"width":0.044215426,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"ai-chapter","depth":23,"bounds":{"left":0.042220745,"top":0.35913807,"width":0.022273935,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"alerts","depth":23,"bounds":{"left":0.042220745,"top":0.38148445,"width":0.011968086,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"backend","depth":23,"bounds":{"left":0.042220745,"top":0.4038308,"width":0.018284574,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"c-learning-people","depth":23,"bounds":{"left":0.042220745,"top":0.42617717,"width":0.038231384,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"confusion-clinic","depth":23,"bounds":{"left":0.042220745,"top":0.44852355,"width":0.034242023,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"curiosity_lab","depth":23,"bounds":{"left":0.042220745,"top":0.4708699,"width":0.027593086,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"engineering","depth":23,"bounds":{"left":0.042220745,"top":0.49321628,"width":0.025598405,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"frontend","depth":23,"bounds":{"left":0.042220745,"top":0.51556265,"width":0.018949468,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"general","depth":23,"bounds":{"left":0.042220745,"top":0.53790903,"width":0.015957447,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"infra-changes","depth":23,"bounds":{"left":0.042220745,"top":0.5602554,"width":0.029587766,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"jiminny-bg","depth":23,"bounds":{"left":0.042220745,"top":0.5826017,"width":0.022938829,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"people-with-copilot-licences","depth":23,"bounds":{"left":0.042220745,"top":0.6049481,"width":0.045212764,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"people-with-zoom-phone-licences","depth":23,"bounds":{"left":0.042220745,"top":0.6272945,"width":0.045877658,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"platform-team","depth":23,"bounds":{"left":0.042220745,"top":0.64964086,"width":0.03125,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"platform-tickets","depth":23,"bounds":{"left":0.042220745,"top":0.67198724,"width":0.034906916,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"product_launches","depth":23,"bounds":{"left":0.042220745,"top":0.6943336,"width":0.03856383,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"random","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.01662234,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"releases","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.01761968,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"sofia-office","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.024268618,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"support","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.016954787,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"thank-yous","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.024268618,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"the_people_of_jiminny","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.04488032,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Aneliya Angelova","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.03756649,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":",","depth":23,"bounds":{"left":0.07945479,"top":0.7086991,"width":0.0063164895,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Nikolay Yankov","depth":23,"bounds":{"left":0.08211436,"top":0.7086991,"width":0.014295213,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":",","depth":23,"bounds":{"left":0.09607713,"top":0.7086991,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Steliyan Georgiev","depth":23,"bounds":{"left":0.09607713,"top":0.7086991,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Aneliya Angelova","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.03756649,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Mario Georgiev","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.033909574,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Nikolay Yankov","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.032912236,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Todor Stamatov","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.034242023,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Gabriela Dureva","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.03523936,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Petko Kashinski","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.034242023,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Vasil Vasilev","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.026263298,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Nikolay Nikolov","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.034242023,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Galya Dimitrova","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.034906916,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Stefka Stoyanova","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.03756649,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Stoyan Tomov","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.030585106,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Stoyan Tanev","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.028922873,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Nikolay Ivanov","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.031914894,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Ves","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.0076462766,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Jira Cloud","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.021609042,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Toast","depth":23,"bounds":{"left":0.042220745,"top":0.7086991,"width":0.011635638,"height":0.0007980846},"role_description":"text"},{"role":"AXRadioButton","text":"Messages","depth":18,"bounds":{"left":0.10206117,"top":0.09177973,"width":0.030585106,"height":0.030327214},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXStaticText","text":"Messages","depth":20,"bounds":{"left":0.111369684,"top":0.10055866,"width":0.01861702,"height":0.012769354},"role_description":"text"},{"role":"AXRadioButton","text":"Channel Overview","depth":18,"bounds":{"left":0.13397606,"top":0.09177973,"width":0.047539894,"height":0.030327214},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Channel Overview","depth":20,"bounds":{"left":0.14328457,"top":0.10055866,"width":0.03557181,"height":0.012769354},"role_description":"text"},{"role":"AXRadioButton","text":"More","depth":19,"bounds":{"left":0.18284574,"top":0.09177973,"width":0.020279255,"height":0.030327214},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"Add and Edit Channel Tabs","depth":18,"bounds":{"left":0.20279256,"top":0.09177973,"width":0.010970744,"height":0.030327214},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Canvas","depth":18,"bounds":{"left":0.096409574,"top":0.0518755,"width":0.015625,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"List","depth":18,"bounds":{"left":0.096409574,"top":0.0518755,"width":0.0076462766,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Folder","depth":18,"bounds":{"left":0.096409574,"top":0.0518755,"width":0.013962766,"height":0.0007980846},"role_description":"text"},{"role":"AXPopUpButton","text":"Jump to date","depth":24,"bounds":{"left":0.14660904,"top":0.12689546,"width":0.032579787,"height":0.022346368},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Stefka Stoyanova","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXLink","text":"Yesterday at 7:18:09 PM","depth":25,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"7:18 PM","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"Нещо взе да се случва с Хъбспот на Прод, май трябва да се погледне","depth":26,"role_description":"text"},{"role":"AXLink","text":"https://jiminny.sentry.io/issues/7007366572/?environment=production&environment=produ[…]etric-issue-contributing-issues-issue-stream&statsPeriod=24h","depth":26,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"https://jiminny.sentry.io/issues/7007366572/?environment=production&environment=produ[…]etric-issue-contributing-issues-issue-stream&statsPeriod=24h","depth":27,"role_description":"text"},{"role":"AXLink","text":"SevenShores\\Hubspot\\Exceptions\\BadRequest","depth":28,"role_description":"link","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"SevenShores\\Hubspot\\Exceptions\\BadRequest","depth":29,"role_description":"text"},{"role":"AXStaticText","text":"/app/Services/Crm/Hubspot/Pagination/HubspotPaginationService.php in Jiminny\\Services\\Crm\\Hubspot\\Pagination\\HubspotPaginationService::executeSearchRequest","depth":27,"role_description":"text"},{"role":"AXStaticText","text":"Client error: `POST","depth":29,"role_description":"text"},{"role":"AXLink","text":"https://api.hubapi.com/crm/v3/objects/contact/search","depth":29,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"` resulted in a `429 Too Many Requests` response:","depth":29,"role_description":"text"},{"role":"AXStaticText","text":"{\"status\":\"error\",\"message\":\"You have reached your secondly limit.\",\"errorType\":\"RATE_LIMIT\",\"correlationId\":\"019db0d4-8 (truncated...","depth":29,"bounds":{"left":0.1263298,"top":0.11572227,"width":0.091090426,"height":0.01915403},"role_description":"text"},{"role":"AXStaticText","text":"Events:","depth":27,"bounds":{"left":0.12333777,"top":0.15642458,"width":0.014960106,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"6290","depth":27,"bounds":{"left":0.13796543,"top":0.15642458,"width":0.010305851,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"bounds":{"left":0.14793883,"top":0.15642458,"width":0.0013297872,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"bounds":{"left":0.14926861,"top":0.15642458,"width":0.0009973404,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"State:","depth":27,"bounds":{"left":0.15026596,"top":0.15642458,"width":0.013297873,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"Ongoing","depth":27,"bounds":{"left":0.16323139,"top":0.15642458,"width":0.01662234,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"bounds":{"left":0.17985372,"top":0.15642458,"width":0.0009973404,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"","depth":27,"bounds":{"left":0.18085106,"top":0.15642458,"width":0.0013297872,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"First Seen:","depth":27,"bounds":{"left":0.1818484,"top":0.15642458,"width":0.022273935,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"2025-11-08","depth":27,"bounds":{"left":0.12333777,"top":0.15642458,"width":0.09242021,"height":0.030327214},"role_description":"text"},{"role":"AXButton","text":"Resolve","depth":27,"bounds":{"left":0.12333777,"top":0.19792499,"width":0.020944148,"height":0.022346368},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Resolve","depth":29,"bounds":{"left":0.1263298,"top":0.2019154,"width":0.014960106,"height":0.012769354},"role_description":"text"},{"role":"AXButton","text":"Archive","depth":27,"bounds":{"left":0.14660904,"top":0.19792499,"width":0.020944148,"height":0.022346368},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-8553712025725762882
|
-4055235709209928062
|
visual_change
|
hybrid
|
NULL
|
Switch workspaces… (Jiminny Inc) Has new messages
Switch workspaces… (Jiminny Inc) Has new messages
Home
Home
DMs
DMs
Activity
Activity
Files
Files
Later
Later
More…
More
Unreads
Threads
Huddles
Drafts & sent
Directories
jiminny-x-integration-app
platform-inner-team
ai-chapter
alerts
backend
c-learning-people
confusion-clinic
curiosity_lab
engineering
frontend
general
infra-changes
jiminny-bg
people-with-copilot-licences
people-with-zoom-phone-licences
platform-team
platform-tickets
product_launches
random
releases
sofia-office
support
thank-yous
the_people_of_jiminny
Aneliya Angelova
,
Nikolay Yankov
,
Steliyan Georgiev
Aneliya Angelova
Mario Georgiev
Nikolay Yankov
Todor Stamatov
Gabriela Dureva
Petko Kashinski
Vasil Vasilev
Nikolay Nikolov
Galya Dimitrova
Stefka Stoyanova
Stoyan Tomov
Stoyan Tanev
Nikolay Ivanov
Ves
Jira Cloud
Toast
Messages
Messages
Channel Overview
Channel Overview
More
Add and Edit Channel Tabs
Canvas
List
Folder
Jump to date
Stefka Stoyanova
Yesterday at 7:18:09 PM
7:18 PM
Нещо взе да се случва с Хъбспот на Прод, май трябва да се погледне
https://jiminny.sentry.io/issues/7007366572/?environment=production&environment=produ[…]etric-issue-contributing-issues-issue-stream&statsPeriod=24h
https://jiminny.sentry.io/issues/7007366572/?environment=production&environment=produ[…]etric-issue-contributing-issues-issue-stream&statsPeriod=24h
SevenShores\Hubspot\Exceptions\BadRequest
SevenShores\Hubspot\Exceptions\BadRequest
/app/Services/Crm/Hubspot/Pagination/HubspotPaginationService.php in Jiminny\Services\Crm\Hubspot\Pagination\HubspotPaginationService::executeSearchRequest
Client error: `POST
https://api.hubapi.com/crm/v3/objects/contact/search
` resulted in a `429 Too Many Requests` response:
{"status":"error","message":"You have reached your secondly limit.","errorType":"RATE_LIMIT","correlationId":"019db0d4-8 (truncated...
Events:
6290
State:
Ongoing
First Seen:
2025-11-08
Resolve
Resolve
Archive
ActivityMoreSlackcalVIewMistonWindowhelp@ Describe what you are looking forJiminny... v— Unreads@ Threads60 Huddles• Drafts & sent8) DirectoriesAb External connections* Starred8 fiminny-x-integrati...platform-inner-team# Channels# ai-chapter# alerts# backend# c-learning-peoplei confusion-clinic# curiosity labu engineering# frontend# generall# infra-changes# jiminny-bgA neonle-with-conilo…people-with-zoom-..# platform-teamst mlatform-hcketc# product_launchesV A PRODA console (PROD]A console 1 [PROD]LDI PROD I> AOA> ₫ OAiA OAI PRODV & STAGING& console STAGINGplattorm-inner-...Q 10MessagesChannel OverviewMore8 (truncated...Events: 6290 State: Ongoing First Seen: 202511-08ArchiveSee moreAdded by Sentry@Nikolay Nikolov да ти кажа подозирам"?lolnresleleimehLukas Kovalik 7:34 PMизглesna много aircalll calis сe мauват.Stefka Stovanova 7:35 PMIхм. все пак упояме пейт лимити на ХъбспотNikolay Nikollov 7:42 PMвъзможно е забъозването от 30 на 5 мин лае лоппинесло за реит лимита на този клиентмакар че не сьм сигурен как - би трябвалосьшото количество АПИ рекуести ланаправим така или иначеLukas Kovalik 7:47 PMIпуска ce rematching най-вероятновследствие на триене на някой CRMobект edited)Message & platform-inner-team+ Aal$hasMatch = $activitI1 Sactivity->geI1 Sactivity->ge11 Sactivity->ge117if (ShasMatch) {Loq: :infoc mess.& console STAGiNgduranus STAGINGIcontact. 1d'→ Extensions)>MScratches.account 1d'••• DPlatform Sprint 2 Q2 - Platform Te4 [JY-20372] Al Reports > Empty paX Jiminny MCP Connector - ProductJiminny Maill(JY-20500] Batch initial sync for SFeed - iminny — Sentry8) JiminnyPipelines - jiminny/appFormalizelT (SRD-6793] Les Mills activity type8 Search results: calendar | Jiminny8 Jiminny8 Jiminny9 liminny* Edit - Engineering - Confluence7 1wy-189091 (Part21 Automated rerSevenShores|Hubspot\Exceptions'CloudWatch | us-east-2w Usage | WindsurfSevenShores\Hubspot\Exceptic XNew TalIssues08DashboardsMonitoreApettingsAll Eventscf346ebo486f0dd54402f54202018287AADOogg910ebOf7666a813719db1dct405d5075ebd023340z20h502002L00212208h508888a04:047472c9ebca931d02bc586ddatfchhc16eb1b:haothdo15d3cc6:OLAAAnOOeh44055C07042747755heh8Z6020170EAdAALAhh7adzchoApr 21, 2026 7:20:47 PM UTCAor 21. 2026 7:20:46 PMUTOApr 21, 2026 7:20:46 PM UTCAor 21. 2026 7.20:45 PMUTeApr 21, 2026 7:20:45 PM UTCAor 21. 2026 7:20:45 PM UTOApr 21, 2026 7:20:45 PM UTCAnr 21. 2026 7.20•44 PMUTCApr 21. 2026 7:20:44 PM UTCAnr 21 2024 7.20•44 PMUITOApr 21. 2026 7:20:44 PM UTCApr 21, 2026 7:20:44 PM UTCApr 21. 2026 7:20:44 PM UTCApr 21, 2026 7:20:44 PM UTCApr 21. 2026 7:20:42 PM UTCApr 21, 2026 7:20:42 PM UTCAor 21. 2026 7:20:42 PM UTCAnr 01 2004 7.20011 DMIITAAor 21. 2026 7:20:41 PMUTOAnr 21 2024 7.20.40 DMLITOAor 21. 2026 7:20.39 PMUTCApr 21, 2026 7:20:37 PM UTCAnr 21. 2026 7.20-77 PMUTOApr 21. 2026 7:20:37 PM UTCAnr 21. 2026 7:20.27 PM UTOApr 21, 2026 7:20:37 PM UTCAnr 21. 2026 7.20-37 PMUTOApr 21. 2026 7:20:36 PM UTCAnr 21 2024 7.20.24 PMLITOAor 21. 2026 7:20:36 PM UTCAnr01 2004 7.20.Z5 DMIITOSevenShores\Hubspot\Exceptions\BadRequest: Client error: ".SevenShores Hubsoot Excentions. BadReauest Client error.SevenShores\Hubspot\Exceptions\BadRequest: Client error:SevenShores Hubsoor Excentions BadRequest Client error.SevenShores\Hubspot\Exceptions\BadRequest: Client error:'.SevenShores Hubsoot Excentions BadRequest: Client error."SevenShores\Hubspot\Exceptions\BadRequest: Client error:'.SevenShores Hubsoot Excentions BadReauest Client error.SevenShores\Hubspot\Exceptions\BadRequest: Client error:SovenShores Huhsnot Eycentions RadPequect. Client error."SevenShores\Hubspot\Exceptions\ BadRequest: Client errorCovenChoros Hubcnot Fycontionc RadPoquoct. Cliont orror."SevenShores\Hubspot\Exceptions\BadRequest: Client errorSovenShores Hubsnot Eycentions RadPequost. Client error. "SevenShores\Hubspot\Exceptions\ BadRequest: Client errorSevenShores\Hubspot\Exceptions\BadRequest: Client error:'.SevenShores\Hubspot\Exceptions\ BadRequest- Client error.SevenShores\Hubspot\Exceptions\BadRequest: Client error: *..SevenShores\Hubspot\Exceptions\ BadRequest: Client error.SavenShorac) Hnhenatl Eycantionel RadPoauoct. Client error."SevenShores\Hubspot\Excentions\ BadReauest- Client error.SevenShores\Hubspot\Exceptions\ BadRequest: Client error: '..SevenShores\ Hubsnot\Eycentions\ RadReauest- Client error.CovonChoroel Lnbenotl Eucontinnel PodDonuoct. Cliant orror."SevenShorec\Hubcnot\Fycentione| RadPeauest- Client error.SovenShores Hubsnot Eycentions RadRequest. Client error. ".CouonChoroel Unbenotl Gyrontinnel PodDoauoct. Cliont orror.SovenShoros) Hubsnot) Eycontioncl RadPoauoct. Cliont orror."SevenShores\ Hubspot\ Exceptions\ BadRequest: Client error: •CovonChoros Huhsnot Fycontions RadPoauoct. Cliont orror."(empty string)emotv strina)(empty string)emorv string)(empty string)emoty strina)(empty string)lemntv string)(empty strina)(empty string)(emptv strina)lomnty ctrinal(empty strina)(empty string)(empty strina)(empty string)lemotv strina(empty string)emotv strina)(empty string)emotv string)(empty string)lemntv string)(empty strina)emntv string)(empty string)lemntv string)(empty string)(emnty strina)(emnty string)lamnty ctring O7ACOO874500971500971500O7AGOO874599productionoroductioneroauctionoroductionproductionoroductionproductionoroductionproductionnroductionoroductionnroductionproductionproductionproductionproductionproductionproductionproductionproductionoroductionproductionnroductionproductionnroductionproductionnroductionproductionnroductiororoductionnroductior(no value)no value)(no value)no value)(no value)no value)(no value)ino value)(no value)(no value)(no value)ina value)(no value)(no value)(no value)(na value)(no value)na value)(no value)(no value)no value)fna value)no value)(no value)(no value)(no value)ino value(no value)(no value)(no value)ma value)a Daily - Platform • in 24 mShowing 1-50 of 11,747 matching eventsJ, Linux 6.1.A Linux 6114. Linux 6.1.1A. Linux 611Linux 6.1.1A. Linux 6.11A Linux 6.1.1A. Linux 611A. Linux 6.1.1A linux 611A Linux 611A tinuy 61-A Linux 6.11*, Linux 6.1.1A Linux 6.114. Linux 6.1.A Linux 6.111Linux 6.1.1A Linux 61114, Linux 6.1.1, Linux 6.1.1A. Linux 6.1.1A. Linux 6114. Linux 6.111. Linux 6.1.14. Linux 6.1.1A. Tinux 611A Linux 6.11A. tinuy 61-A Linux 611A tinuy 61-100% CWed 22 Apr 9:21:55Merged Issues...
|
68582
|
|
69222
|
1587
|
2
|
2026-04-22T07:41:21.158913+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-22/1776 /Users/lukas/.screenpipe/data/data/2026-04-22/1776843681158_m2.jpg...
|
Slack
|
Nikolay Yankov (DM) - Jiminny Inc - 2 new items - Nikolay Yankov (DM) - Jiminny Inc - 2 new items - Slack...
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Switch workspaces… (Jiminny Inc) Has new messages
Switch workspaces… (Jiminny Inc) Has new messages
Home
Home
DMs
DMs
Activity
Activity
Files
Files
Later
Later
More…
More
Unreads
Threads
Huddles
Drafts & sent
Directories
jiminny-x-integration-app
platform-inner-team
ai-chapter
alerts
backend
c-learning-people
confusion-clinic
curiosity_lab
engineering
frontend
general
infra-changes
jiminny-bg
people-with-copilot-licences
people-with-zoom-phone-licences
platform-team
platform-tickets
product_launches
random
releases
sofia-office
support
thank-yous
the_people_of_jiminny
Nikolay Yankov
Nikolay Nikolov
Aneliya Angelova
,
Nikolay Yankov
,
Steliyan Georgiev
Aneliya Angelova
Mario Georgiev
Todor Stamatov
Gabriela Dureva
Petko Kashinski
Vasil Vasilev
Galya Dimitrova
Stefka Stoyanova
Stoyan Tomov
Stoyan Tanev
Nikolay Ivanov
Ves
Toast
Jira Cloud
Messages
Messages
Add canvas
Add canvas
Files
Files
Add and Edit Channel Tabs
Canvas
List
Folder
Jump to date
Jump to date
Nikolay Yankov
Apr 7th at 5:26:21 PM
5:26 PM
вдигнах на front-end coverage-a до 38%
останалото е в PHP
image.png
Toggle file
image.png
Jump to date
Nikolay Yankov
Yesterday at 4:35:36 PM
4:35 PM
Опа
Lukas
Yesterday at 4:38:12 PM
4:38
за този бутон
I'm Interested
да направим един ендппойнт
POST
/api/v1/automated-reports/interest
Yesterday at 4:38:16 PM
4:38
примерно?
React with white_check_mark
React with eyes
React with raised_hands
Add reaction…...
|
[{"role":"AXPopUpButton","text [{"role":"AXPopUpButton","text":"Switch workspaces… (Jiminny Inc) Has new messages","depth":14,"bounds":{"left":0.0056515955,"top":0.058260176,"width":0.011968086,"height":0.028731046},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Home","depth":14,"bounds":{"left":0.0029920214,"top":0.10055866,"width":0.017287234,"height":0.054269753},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXStaticText","text":"Home","depth":16,"bounds":{"left":0.0066489363,"top":0.13806863,"width":0.009973404,"height":0.0103751},"role_description":"text"},{"role":"AXRadioButton","text":"DMs","depth":14,"bounds":{"left":0.0029920214,"top":0.15482841,"width":0.017287234,"height":0.054269753},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"DMs","depth":16,"bounds":{"left":0.0076462766,"top":0.19233839,"width":0.007978723,"height":0.0103751},"role_description":"text"},{"role":"AXRadioButton","text":"Activity","depth":14,"bounds":{"left":0.0029920214,"top":0.20909816,"width":0.017287234,"height":0.054269753},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Activity","depth":16,"bounds":{"left":0.004986702,"top":0.24660814,"width":0.012965426,"height":0.0103751},"role_description":"text"},{"role":"AXRadioButton","text":"Files","depth":14,"bounds":{"left":0.0029920214,"top":0.26336792,"width":0.017287234,"height":0.054269753},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Files","depth":16,"bounds":{"left":0.0076462766,"top":0.3008779,"width":0.0076462766,"height":0.0103751},"role_description":"text"},{"role":"AXRadioButton","text":"Later","depth":14,"bounds":{"left":0.0029920214,"top":0.31763768,"width":0.017287234,"height":0.054269753},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Later","depth":16,"bounds":{"left":0.00731383,"top":0.35514766,"width":0.008643617,"height":0.0103751},"role_description":"text"},{"role":"AXRadioButton","text":"More…","depth":14,"bounds":{"left":0.0029920214,"top":0.3719074,"width":0.017287234,"height":0.054269753},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More","depth":16,"bounds":{"left":0.006981383,"top":0.4094174,"width":0.008976064,"height":0.0103751},"role_description":"text"},{"role":"AXStaticText","text":"Unreads","depth":21,"role_description":"text"},{"role":"AXStaticText","text":"Threads","depth":21,"role_description":"text"},{"role":"AXStaticText","text":"Huddles","depth":21,"role_description":"text"},{"role":"AXStaticText","text":"Drafts & sent","depth":21,"role_description":"text"},{"role":"AXStaticText","text":"Directories","depth":21,"role_description":"text"},{"role":"AXStaticText","text":"jiminny-x-integration-app","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"platform-inner-team","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ai-chapter","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"alerts","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"backend","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"c-learning-people","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"confusion-clinic","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"curiosity_lab","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"engineering","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"frontend","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"general","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"infra-changes","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"jiminny-bg","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"people-with-copilot-licences","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"people-with-zoom-phone-licences","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"platform-team","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"platform-tickets","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"product_launches","depth":23,"bounds":{"left":0.042220745,"top":0.096568234,"width":0.03856383,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"random","depth":23,"bounds":{"left":0.042220745,"top":0.118914604,"width":0.01662234,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"releases","depth":23,"bounds":{"left":0.042220745,"top":0.14126097,"width":0.01761968,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"sofia-office","depth":23,"bounds":{"left":0.042220745,"top":0.16360734,"width":0.024268618,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"support","depth":23,"bounds":{"left":0.042220745,"top":0.1859537,"width":0.016954787,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"thank-yous","depth":23,"bounds":{"left":0.042220745,"top":0.20830008,"width":0.024268618,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"the_people_of_jiminny","depth":23,"bounds":{"left":0.042220745,"top":0.23064645,"width":0.04488032,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Nikolay Yankov","depth":23,"bounds":{"left":0.042220745,"top":0.28332004,"width":0.032912236,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Nikolay Nikolov","depth":23,"bounds":{"left":0.042220745,"top":0.3056664,"width":0.034242023,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Aneliya Angelova","depth":23,"bounds":{"left":0.042220745,"top":0.32801276,"width":0.03756649,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":",","depth":23,"bounds":{"left":0.07945479,"top":0.32801276,"width":0.0063164895,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Nikolay Yankov","depth":23,"bounds":{"left":0.08211436,"top":0.32801276,"width":0.014295213,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":",","depth":23,"bounds":{"left":0.09607713,"top":0.34557062,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Steliyan Georgiev","depth":23,"bounds":{"left":0.09607713,"top":0.34557062,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Aneliya Angelova","depth":23,"bounds":{"left":0.042220745,"top":0.35035914,"width":0.03756649,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Mario Georgiev","depth":23,"bounds":{"left":0.042220745,"top":0.37270552,"width":0.033909574,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Todor Stamatov","depth":23,"bounds":{"left":0.042220745,"top":0.39505187,"width":0.034242023,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Gabriela Dureva","depth":23,"bounds":{"left":0.042220745,"top":0.41739824,"width":0.03523936,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Petko Kashinski","depth":23,"bounds":{"left":0.042220745,"top":0.43974462,"width":0.034242023,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Vasil Vasilev","depth":23,"bounds":{"left":0.042220745,"top":0.46209097,"width":0.026263298,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Galya Dimitrova","depth":23,"bounds":{"left":0.042220745,"top":0.48443735,"width":0.034906916,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Stefka Stoyanova","depth":23,"bounds":{"left":0.042220745,"top":0.5067837,"width":0.03756649,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Stoyan Tomov","depth":23,"bounds":{"left":0.042220745,"top":0.5291301,"width":0.030585106,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Stoyan Tanev","depth":23,"bounds":{"left":0.042220745,"top":0.5514765,"width":0.028922873,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Nikolay Ivanov","depth":23,"bounds":{"left":0.042220745,"top":0.5738228,"width":0.031914894,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Ves","depth":23,"bounds":{"left":0.042220745,"top":0.5961692,"width":0.0076462766,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Toast","depth":23,"bounds":{"left":0.042220745,"top":0.64884275,"width":0.011968086,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Jira Cloud","depth":23,"bounds":{"left":0.042220745,"top":0.6711891,"width":0.021609042,"height":0.014365523},"role_description":"text"},{"role":"AXRadioButton","text":"Messages","depth":17,"bounds":{"left":0.10206117,"top":0.09177973,"width":0.030585106,"height":0.030327214},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXStaticText","text":"Messages","depth":19,"bounds":{"left":0.111369684,"top":0.10055866,"width":0.01861702,"height":0.012769354},"role_description":"text"},{"role":"AXRadioButton","text":"Add canvas","depth":18,"bounds":{"left":0.13397606,"top":0.09177973,"width":0.033909574,"height":0.030327214},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Add canvas","depth":20,"bounds":{"left":0.14328457,"top":0.10055866,"width":0.021941489,"height":0.012769354},"role_description":"text"},{"role":"AXRadioButton","text":"Files","depth":17,"bounds":{"left":0.16921543,"top":0.09177973,"width":0.020944148,"height":0.030327214},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Files","depth":19,"bounds":{"left":0.17852394,"top":0.10055866,"width":0.008976064,"height":0.012769354},"role_description":"text"},{"role":"AXPopUpButton","text":"Add and Edit Channel Tabs","depth":17,"bounds":{"left":0.19115691,"top":0.09177973,"width":0.010970744,"height":0.030327214},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Canvas","depth":17,"bounds":{"left":0.096409574,"top":0.0518755,"width":0.015625,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"List","depth":17,"bounds":{"left":0.096409574,"top":0.0518755,"width":0.0076462766,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Folder","depth":17,"bounds":{"left":0.096409574,"top":0.0518755,"width":0.013962766,"height":0.0007980846},"role_description":"text"},{"role":"AXPopUpButton","text":"Jump to date","depth":22,"bounds":{"left":0.15026596,"top":0.632083,"width":0.025265958,"height":0.0007980846},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"Jump to date","depth":23,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Nikolay Yankov","depth":24,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":24,"role_description":"text"},{"role":"AXLink","text":"Apr 7th at 5:26:21 PM","depth":24,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"5:26 PM","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"вдигнах на front-end coverage-a до 38%","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"останалото е в PHP","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"image.png","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXButton","text":"Toggle file","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXLink","text":"image.png","depth":27,"role_description":"Unlabelled image","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"Jump to date","depth":23,"bounds":{"left":0.14660904,"top":0.12689546,"width":0.032579787,"height":0.022346368},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Nikolay Yankov","depth":24,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":24,"role_description":"text"},{"role":"AXLink","text":"Yesterday at 4:35:36 PM","depth":24,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"4:35 PM","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Опа","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Lukas","depth":25,"role_description":"text"},{"role":"AXLink","text":"Yesterday at 4:38:12 PM","depth":25,"bounds":{"left":0.107380316,"top":0.11572227,"width":0.007978723,"height":0.009577015},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"4:38","depth":26,"bounds":{"left":0.107380316,"top":0.11572227,"width":0.007978723,"height":0.009577015},"role_description":"text"},{"role":"AXStaticText","text":"за този бутон","depth":25,"bounds":{"left":0.11801862,"top":0.11572227,"width":0.032247342,"height":0.009577015},"role_description":"text"},{"role":"AXStaticText","text":"I'm Interested","depth":26,"bounds":{"left":0.15159574,"top":0.11572227,"width":0.03357713,"height":0.009577015},"role_description":"text"},{"role":"AXStaticText","text":"да направим един ендппойнт","depth":25,"bounds":{"left":0.11801862,"top":0.12849163,"width":0.06815159,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"POST","depth":26,"bounds":{"left":0.11934841,"top":0.14844373,"width":0.009640957,"height":0.011971269},"role_description":"text"},{"role":"AXStaticText","text":"/api/v1/automated-reports/interest","depth":26,"bounds":{"left":0.11934841,"top":0.1660016,"width":0.08144947,"height":0.011971269},"role_description":"text"},{"role":"AXLink","text":"Yesterday at 4:38:16 PM","depth":25,"bounds":{"left":0.107380316,"top":0.18994413,"width":0.007978723,"height":0.011971269},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"4:38","depth":26,"bounds":{"left":0.107380316,"top":0.18994413,"width":0.007978723,"height":0.011971269},"role_description":"text"},{"role":"AXStaticText","text":"примерно?","depth":25,"bounds":{"left":0.11801862,"top":0.18754987,"width":0.025265958,"height":0.014365523},"role_description":"text"},{"role":"AXCheckBox","text":"React with white_check_mark","depth":26,"bounds":{"left":0.13730054,"top":0.16280925,"width":0.010638298,"height":0.025538707},"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"React with eyes","depth":26,"bounds":{"left":0.14793883,"top":0.16280925,"width":0.010638298,"height":0.025538707},"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"React with raised_hands","depth":26,"bounds":{"left":0.15857713,"top":0.16280925,"width":0.010638298,"height":0.025538707},"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Add reaction…","depth":26,"bounds":{"left":0.16921543,"top":0.16280925,"width":0.010638298,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-8553623108235644432
|
-6395746309002258136
|
click
|
hybrid
|
NULL
|
Switch workspaces… (Jiminny Inc) Has new messages
Switch workspaces… (Jiminny Inc) Has new messages
Home
Home
DMs
DMs
Activity
Activity
Files
Files
Later
Later
More…
More
Unreads
Threads
Huddles
Drafts & sent
Directories
jiminny-x-integration-app
platform-inner-team
ai-chapter
alerts
backend
c-learning-people
confusion-clinic
curiosity_lab
engineering
frontend
general
infra-changes
jiminny-bg
people-with-copilot-licences
people-with-zoom-phone-licences
platform-team
platform-tickets
product_launches
random
releases
sofia-office
support
thank-yous
the_people_of_jiminny
Nikolay Yankov
Nikolay Nikolov
Aneliya Angelova
,
Nikolay Yankov
,
Steliyan Georgiev
Aneliya Angelova
Mario Georgiev
Todor Stamatov
Gabriela Dureva
Petko Kashinski
Vasil Vasilev
Galya Dimitrova
Stefka Stoyanova
Stoyan Tomov
Stoyan Tanev
Nikolay Ivanov
Ves
Toast
Jira Cloud
Messages
Messages
Add canvas
Add canvas
Files
Files
Add and Edit Channel Tabs
Canvas
List
Folder
Jump to date
Jump to date
Nikolay Yankov
Apr 7th at 5:26:21 PM
5:26 PM
вдигнах на front-end coverage-a до 38%
останалото е в PHP
image.png
Toggle file
image.png
Jump to date
Nikolay Yankov
Yesterday at 4:35:36 PM
4:35 PM
Опа
Lukas
Yesterday at 4:38:12 PM
4:38
за този бутон
I'm Interested
да направим един ендппойнт
POST
/api/v1/automated-reports/interest
Yesterday at 4:38:16 PM
4:38
примерно?
React with white_check_mark
React with eyes
React with raised_hands
Add reaction…
ActivityMoreSlackcalVIewJiminny ...# product_launches# randoma releases# soha-office# support# thank-yous# the people of jimi..• Direct messagesNikolay YankovNikolav Nikolov3 Aneliya Angelova. ...f Aneliva AngelovaMario GeorgievSe: Todor StamatovGabriela DurevalPetko Kashinskia Vasil VasileyGalya Dimitrova8 Stefka StovanovaAa Stovan Tomov*% Stoyan TanevNikolav Ivanov::: AonsToastSi Jira GloudMistonWindowhelp@ Describe what you are looking forNikolay YankovMessagesAdd canvasUr Filesда направим/api/v1/automated-reports/interestпримерно?Lukas Kovallik 4:40 PMna unu lautomated-renorts/track-interestняма значение да си е POSTглелам че си направил branchJy-203 /2-atreports-promotion-pagesнали така, там ла комитнаNikolay Yankov 4:54 PMда, даваи тамNikolay Yankoy 10:06 AMУукаш, има някакви коментари от Claudelвиж ги ако мислиш, че трябва да се фикснеhttos://github.com/liminnv/a0o/ou|//11894Nikolav Yankov 10:40 AMLukas. я виж каква промяна направих по ВЕокей ли е?httos://github.com/iminnv/aoo/oul/11998/chaaa Promotion pagesel sied malko+ Aa €FV faVsco.js#11894 on JY-18909-automated-reports-asProject v(C) Service.ohn• _ Fields© Field.phpAskJlminnykeponActviyserMce.ongx> → OpportunityMatcher> → OpportunitySyncStrategy› ProspectSearchStrategy• M ServiceTraits©) AutomatedReportsServiceTest.phpC UserPilotActivityListener.phpC) AcuvityLogged.phpsendkeportmallJob.oho© RequestGenerateAskJiminnyReportJobTest.php© Client.php© DecorateActivity.phpT DeleteObjectsTrait.php© FieldDefinitions.php© PayloadBuilder.phpC Profile.php© QueryBuilder.php© QueryHandler.php© Querylterator.php© QueryResults.php© Service.php© SyncBatchRedisService.phpC) RequestGenerateReportJob.php© AutomatedReportResult.phpphp api.phpphp api_ v2.phpphp web.php(C) Controllers/UserAutomatedReportsController.phpC) AutomatedReport.phpQ- DATE_FILTER_KEYS‹P Cc W .*P.T:class AskJ1minnykeporcaculv1cyservicewlraitsC BaseClient.phpC, BaseService.oho© CachedCrmServiceDecorator.php© CountryCodeResolver.php© CrmActivityProviderIntegratedEvenC) CrmActivitvService.ono© CrmConfigurationSettingsService.pC) CrmObiectsResolver.oho© DefaultProspectSearchStrategy.phjC) EmailHelner.oho@ FindsProspectinterface.php© LayoutManager.php© MatchDomainByEmailInterface.php© OpportunityActivityMatcher.php• OpportunitySyncStrategyInterface.© OpportunitySyncStrategyResolver.© ProspectCache.php© ProspectSearchScope.php© ProspectSearchStrategyFactory.ph© ProspectSearchStrategyInterface.p© ProviderRegistry.php@ RecordSelector.php(* ResolveCompanvNameByEmailTrai© TimePeriodIterator.php> C Import›D Internalv D Kioskv AutomatedReoortsC) ActivitviivoeService.oho© AskJiminnyReportActivityServicrC) AutomatedRenortsCallbackSery© AutomatedReportsService.php© DealStagesService.phppublic function -_construct(private readonly ActivitySearch SactivitySearch,private readonly ElasticActivityRepository $elasticRepository.private readonly LoggerInterface $logger,) 1..3* Fetch activity IDs for a saved search, passing its filters as-is to Criteria.* Date filters stored on the saved search are excluded; if no other filters exist,* no date constraint is applied - matching the behaviour of getContextForAskAnythingBuFilter.* Qreturn stringl] Activity IDspublic tunction getactivityldsrorsavedsearchsearch ssavedsearchUser suserastrind trequencv = nulu): arrav {srequestParams = Sthis->ou1ldRequestParamsFromSearchssavedSearch. Suser):if ($frequency |== null) {sdateRanae = Sthis->calculatelateRangeForFrequencySfrequency. Suser):if EdateRande lez nuubSrequestParamslActivitvActualDate: PARAM START DATE = SdateRande("start date!1»SrequestParamslActivitvActualDate:PARAM END DATE = SdateRanae('end datel1»Conitonia = Cnitonia..cnonteSromPoauoctdarray_merge(SrequestParams, [Ilimiti =s colf.•nScAlIT TOp ACTTVTTTES COuNT I'page' => 1'sequence number' => 1,Suser->getTimezoneOotalterser = sths-›acrzvlrysearch-›qerunuemanorageruterser scriterza, suser"V2P1ACkM Issues • In 4 m100% L2Wed 22 Apr 10:41:23= laravel.log4 SF [jiminny@localhost]4 HS_local [jiminny@localhost]« console [PROD]© MatchActivityCrmData.php Xfiò crm_configurations [PROD)A console [EU]A console [STAGING]<:ondeclare scrict-cypes=l)namespace Jiminny Jobs crm.use ..-37 usagesclass MatchActivitvCrmData extends Job imolements ShouldOueue. ShouldBelniqueuse InteractsWithQueve:use SerializesModels:nublic int Stries =3nnivato int Sactivitvtd»o usagesprivate ?Configuration $fromConfiguration; usagesprivate bool SremoteSearch;15 usagespublic functionconstouct/Int saccIvicy1a?Confiquration $fromConfiquration = null.bool sremotesearch = talse.•{...}public tunction uniqueldo: strina...public function timeout@: ints...}public function uniqueFor@): intf...?oublic function backoffo: arrav....,* Athrows ContainenfycentionIntenfacoOthrows_No+FoundExcentionIntenfacp* Othrows Excention/ Throwablenublde function handlodActivityRepository $activityRepository,Comdetivi+yConvico CanmdetivitvSonvicoConnection Sconnection): void {...}W Windsurf Teams 91:54 (16 chars) UTF-8 Po 4 spaces...
|
NULL
|
|
25633
|
550
|
4
|
2026-04-15T12:57:57.910639+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-15/1776 /Users/lukas/.screenpipe/data/data/2026-04-15/1776257877910_m2.jpg...
|
Boosteroid
|
Boosteroid
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
60913148016149/55Castle AgeGame Paused (P)Villager 60913148016149/55Castle AgeGame Paused (P)Villagerkovalfklukas (Britons)2 Rajyapala: 2474/24744 Louis VI: 2472/24725 Magnus Olafsson: 2437/24377 Maximilian of Habsburg: 2333/23338 Almish Yiltawar: 2323/23233 Huascár: 2224/22246 László I: 2133/21331 kovaliklukas: 2062/2062BHE...
|
NULL
|
-8553612243471374550
|
NULL
|
click
|
ocr
|
NULL
|
60913148016149/55Castle AgeGame Paused (P)Villager 60913148016149/55Castle AgeGame Paused (P)Villagerkovalfklukas (Britons)2 Rajyapala: 2474/24744 Louis VI: 2472/24725 Magnus Olafsson: 2437/24377 Maximilian of Habsburg: 2333/23338 Almish Yiltawar: 2323/23233 Huascár: 2224/22246 László I: 2133/21331 kovaliklukas: 2062/2062BHE...
|
NULL
|
|
74272
|
1846
|
16
|
2026-04-23T09:31:38.495893+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-23/1776 /Users/lukas/.screenpipe/data/data/2026-04-23/1776936698495_m2.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEvent.php
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
->track(
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/1
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
1
2
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
try {
foreach ($this->resolveUsers($automatedReport) as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny
Code changed:
Hide
Sync Changes
Hide This Notification
21
1...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25731382,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20157-AJ-report-not-send-notification, menu","depth":5,"bounds":{"left":0.29587767,"top":0.019952115,"width":0.10139628,"height":0.025538707},"help_text":"Git Branch: JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8171542,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AutomatedReportsCommandTest","depth":6,"bounds":{"left":0.8324468,"top":0.019952115,"width":0.0831117,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AutomatedReportsCommandTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AutomatedReportsCommandTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"bounds":{"left":0.35239363,"top":0.15482841,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"bounds":{"left":0.3650266,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"->track(","depth":4,"bounds":{"left":0.37599733,"top":0.15403032,"width":0.043882977,"height":0.015961692},"value":"->track(","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.42885637,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"bounds":{"left":0.43882978,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"bounds":{"left":0.4474734,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"bounds":{"left":0.45611703,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1/1","depth":4,"bounds":{"left":0.46974733,"top":0.15323225,"width":0.025598405,"height":0.017557861},"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"bounds":{"left":0.49534574,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"bounds":{"left":0.50398934,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"bounds":{"left":0.51263297,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"bounds":{"left":0.5212766,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"bounds":{"left":0.7606383,"top":0.15243416,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.73105055,"top":0.18355946,"width":0.00731383,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.74035907,"top":0.18355946,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.7503325,"top":0.18355946,"width":0.00731383,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.7593085,"top":0.1819633,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.76662236,"top":0.1819633,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n try {\n foreach ($this->resolveUsers($automatedReport) as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n try {\n foreach ($this->resolveUsers($automatedReport) as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"bounds":{"left":0.77526593,"top":0.123703115,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"bounds":{"left":0.78390956,"top":0.123703115,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"bounds":{"left":0.79488033,"top":0.123703115,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"bounds":{"left":0.80352396,"top":0.123703115,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"bounds":{"left":0.8121675,"top":0.123703115,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"bounds":{"left":0.8231383,"top":0.123703115,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"bounds":{"left":0.83410907,"top":0.123703115,"width":0.024268618,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Cancel Running Statements","depth":4,"bounds":{"left":0.8607048,"top":0.123703115,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Playground","depth":4,"bounds":{"left":0.87167555,"top":0.123703115,"width":0.029587766,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"jiminny","depth":4,"bounds":{"left":0.9587766,"top":0.123703115,"width":0.02825798,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"21","depth":4,"bounds":{"left":0.9222075,"top":0.14844373,"width":0.009640957,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.9338431,"top":0.14844373,"width":0.00731383,"height":0.015163607},"role_description":"text"}]...
|
-8553380832675719203
|
-3034141351191291066
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
->track(
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/1
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
1
2
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
try {
foreach ($this->resolveUsers($automatedReport) as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny
Code changed:
Hide
Sync Changes
Hide This Notification
21
1...
|
74270
|
|
71623
|
1713
|
6
|
2026-04-22T13:08:27.083661+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-22/1776 /Users/lukas/.screenpipe/data/data/2026-04-22/1776863307083_m2.jpg...
|
PhpStorm
|
faVsco.js – RequestGenerateAskJiminnyReportJob.php
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25797874,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20157-AJ-report-not-send-notification, menu","depth":5,"bounds":{"left":0.29654256,"top":0.019952115,"width":0.10139628,"height":0.025538707},"help_text":"Git Branch: JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-8552673707532730679
|
-8348545427604918848
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
ActivityMorePhpStormViewINavigarecodeLaravelKeractorloolsWindowmelpJiminny ... ~# platform-tickets# product launchesic random# releases*sona-oince# support# thank-yous# the_people_of jimi..•? Direct messagesf Aneliya AngelovaStoyan Tomov3 Aneliya Angelova, ...Nikolay Yankove. Nikolay NikolovMario Georgievea Todor StamatovA Gabriela Dureva PPetko Kashinski V8. Vasil Vasilev XP. Galya Dimitrova& Stefka Stoyanova* Stovan TanevNikolay Ivanov::: AppsToastJira Cloudrvtavsco.s°9 JY-20157-AJ-report-not-send-notificationProiect v© JiminnyDebugCommand.phpC AutomatedkeporconpAneliya AngelovaMessagesAdd canvasur Filesnuos.l iminnyToday ~ t/browse/JY-20157или да го пусна преди товаJY-20157 Send emalll notincation when theirenort is not generatedStatus: In DevL Tvpe: StorvAssignee: Lukas KovalikT Priority: MediumChange Status*+ Al SummariseAneliva Angelova 4:03 PMла говорих - каза, чеможе като част от товасториsend emall nouncaron when the revortis notgeneratedN-20157 Send emaill nonncation when therenort is not generatedStatus: In DeyA Tvne: StorvAssignee: Lukas Kovalik1 Prioritv: MediumAssierChange status*+AI SummariseAdded by Jira CloudMessage Aneliva Angelova+ Aa> D Audio© RequestGenerateAskJiminnyReportJob.php Xv D AutomatedReports© RequestGenerateAskJiminnyR© kequestGeneratekeportJoo.or© SendReportJob.phprepori-not-qenerated.blade.ohcc) senckeponmalljoo.ono© SendReportNotGeneratedMail.(C) AutomatedReportsRepositorv.phpphp apiv2.php• → calendaiC) AutomatedReportResult.phpv D CrmV → DeleteLOG PRESIXX P Cc W .*3/16© DeleteAccountJob.phpclass kequescbeneraceaskJamznnvkeporcJcvC) DeleteContactJob.oho() DeleteCrmEntitvTrait.ohp© DeleteLeadJob.phppublic tunccion handleAUromareokeporrsservice sredortservice© DeleteOpportunityJob.phpAskJiminnvReportAct1v1tvService sactivitvServicelC) VerifvActivitvCrmtask.Job.rProphetclient Sorophetculent)> M HubspotLoqgerinterface Slogger1 Salesforce©AutologDelayedToCrm.phpJobdispatcherinterface Stobd1spatcher(c) Check AndPetrvRemoteMatch.©CreateFollowupActivity.php(C) CreateNotes nhnlVo1d *slodder->infolself.*L0GPEpx Started'.'automatedRenortluid'© MatchActivitiesToNewOpportu© MatchActivityCrmData.php€ NoteObject.phptry t(©) SaveActivity.pnp$automatedReport = SreportService->getReport(© SaveTranscription.php© SetupLayout.phpif(1 Sthic-svalidateRenont/SautomatedRenonF© SyncActivity.php© SyncFieldMetadata.phprecurnr© SyncHubspotObjects.php© SyncLeads.php© SyncObjects.php© SyncOpportunitiesJob.phpc) suncepoortunitv.ono$creator = SautomatedReport->getCreatorO:if ($creator === null) {$logger->warning(self::L06_PREFIX.Ski'automatedReportUuid' => Sthis-›repor© SyncProfileMetadata.phpC) SvncTeamfields.lob.ono© SyncTeamMetadata.phpreturn© UpdateOpportunitvSpecificatic(C) UodateStade.ohoM DealRisks> MMailboy• M MeetinaRot• M MiddlewaressavedSearch = SautomatedReport->qetSavedSearif ($savedSearch === null) {$logger->warning(self::LOG_PREFIX.'automatedRenortluid' => Sthis->renor> D Streaming• MTeam> D Telephonyv Milcorreturn:© ChangeEmailJob.php9 DeactivateUser.Job.php(e) NolatoSchodulodl IcorA ctivitiosSnromnt = SautomatedRenont->aetAckAnvthinaProlif (Snromnt eer null) $& SF [jiminny@localhost]A HS_local [jiminny@localhostA console [STAGING)S76412020-04-22 12.5/.11J Local.LNFU. LULminny Joos Ma1LDox2020-04-LL 12.00.00 LocaL. LNFU.Jiminny Console\ Commands\Command::run12020-04-LL 12.00.00 LocaL. LNFU.[ScheduleBotCommandl Number of activit[2026-04-22 12:58:03] local.INF0: Jiminny\ Console\ Commands\ Command: :rur2026-04-22 12:58:05 Local.INFU: Jminny Console Commands Command::run[2026-04-22 12:58:05] local.INF0: Jiminny\ Console\ Commands\ Command: : rur2026-04-22 12:58:07 Local.NOTICE: Monitoring start12826-84-22 12:58:07JLocol, NocE: Monntorino endi12026-04-22 12:5811511LocoL,INFo: Jaminny console Commands command: : run2026-04-22 12:58:1371Local. INFO: Jiminny\Console\Commands\Command: :run2026-04-22 12:58:217LocaL.INF0: Jiminny Console Commands Command: : runlocal, INF0: [EmailSchedulel STARTING batch process[2026-04-22 12:58:21]2026-04-22 12:58:217Local.INFO: Processing email batch 98408 for inbo›[2026-04-22 12:58:21]local.INFO: [SocialAccountService] Fetching token2026-04-22 12•58-2171ocal.TAS0• Social Accoun+Servicel Token netriever[2026-04-22 12:58:21]local.INFO: [EncryptedTokenManager] Generating aco2026-04-22 12•58-211 1oca1.TNE0• TCrmûwnenResolven Inteanation ownen m[2026-04-22 12:58:21] local.INF0: [SocialAccountService) Fetching token2026-04-22 12•58-211 1oca1TNE0• [SocialAccountServicel Token notnieven[2026-04-22 12:58:21] local.INF0: [EncryptedTokenManager] Generating aco[2026-04-22 12:58:24] local.INF0: Processing an email from inbox batch {[2026-04-22 12:58:24] local.INF0: [SocialAccountService] Fetching token[2026-04-22 12:58:24] local.INF0: [SocialAccountService] Token retrieveo[2026-04-22 12:58:24] local.INF0: [EncryptedTokenManager] Generating aco[2026-04-22 12:58:24] local.INF0: [Crm0wnerResolver] Integration owner m[2026-04-22 12:58:24] local.INF0: [EmailImport\ParticipantsResolver] The[2026-04-22 12:58:24] local.INF0: [EmailImport\ParticipantsValidator] En[2026-04-22 12:58:24] local.INF0: Processing an email from inbox batch ‹[2026-04-22 12:58:24] local.INF0: [SocialAccountServicel Fetching token12826-84-2 12:58:24 LOCoL. INFO:12826-84-22 1:58:24 LocaL.INFO:socralAccountservice oken retrzevei[EncryptedTokenManager] Generating acd2026-04-22 12:58:241 LocaL.INFO:2026-04-22 12:58:241 LocaL.INFO:CrmOwnerPesolver Integration owner m[EmailImport\ParticipantsResolverl The2026-04-22 12:58:241 LocaL.INFO:2026-04-22 12:58:241 LocaL.INFO:2026-04-22 12:58:241 LocaL.INFO:EmaiUmoort ParticivantsValidator EnProcessing an email from inbox batch ‹SocialAccountService Fetchina token2026-04-22 12:58-241 10c07.1N5002026-04-22 12•58-2411EncruntedtokenManader Genenatina acTCenQwnerPesolverl Inteanation owner2026-04-22 12•58-2411local TAEO• [Email Imnont PanticinantsResolven The[2026-04-22 12:58:24] Local.INFO: [EmailImport\ParticipantsValidator] En2026-04-22 12•58-241 1oca1.TNE0• Processing an email from inhox batch &[2026-04-22 12:58:24]local.INF0: [SocialAccountService] Fetching token12024-04-29 12.59•241 1oca1 TAS0• CocialAccoun+Convicol Tolon notnievor[2026-04-22 12:58:24] Local. INFO: [EncryptedTokenManager] Generating acc12004-04-22 12.59.041 10c01 TAS0• [CamÎwnonPocolvenl Intoanation ownenm[2026-04-22 12:58:24] local.INF0: [EmailImport\ParticipantsResolver] The12a04-04-29 12.59.041 1oc01 TAS0• [5mai1Tmnon+|DanticinantcVolidatonlG.[2026-04-22 12:58:24] local.INF0: Processing an email from inbox batch &[2026-04-22 12:58:24] local.INF0: [SocialAccountServicel Fetching token: Al chapter - in 52 m100% C4Wed 22 Apr 16:08:30Report Activity Count+0.should I use Snow->copy() as it is in @AutomatedReportsService.php#L1770-1803 . Alos ereflect changes in unit testsNo - Snow->copy() is only needed for mutable Carbon. AskJiminnyReportActivityService uses CarbonImmutable ( $now = C)arbonImmutable::now(... where every method returns a new instance, so copy would be redundantThe AutomatedReportsService version uses mutable Carbon, hence the explicit ->copy() . The two implementations behaveI youo preter consistency, the cleaner tix is lo switch AutomatedReportsservice:: calculaterromAndtodateper1od Lo carbonimmtable and drop the conv(") calls — but that's a broader refactor outside this bua's sconeAsk anvthina (₴4L)+ « CodeWN Windsurf Toams 62-1UTF.8io 4 space:...
|
71622
|
|
66621
|
1498
|
12
|
2026-04-21T14:51:04.946032+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-21/1776 /Users/lukas/.screenpipe/data/data/2026-04-21/1776783064946_m2.jpg...
|
PhpStorm
|
faVsco.js – AutomatedReportsService.php
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormcodeFV faVsco.js vProletey© Profile.php© Q PhostormcodeFV faVsco.js vProletey© Profile.php© QueryBuilder.php©QueryHandler.php© Querylterator.php© QueryResults.php© Service.php© SyncBatchRedisService.phpD Traits© BaseClient.php© BaseService.phpo vacheaurmservcevecorator.countrycoderesolver.ong© CrmActivityProviderintegratedc) Crmacuivityservice.ong(c) crmeonrigurationsettingsservc) Crmobiectskesolver.phpc) DeraultProspectsearchstrateac) -mallhelper.ohoFindsProspectintertace.ohoC)LavoutManager.ohoC) OnoortunitvSvncStratedvResoC) ProspectSearchScone.oho) ProspectSearchStrategyFactorD ProspectSearchStrategyinterftewethelneelnieC RecordSelector.oho© ResolveCompanyNameByEmai© TimePeriodlterator.php& Import• Kioskv MAutomatedRenortc© ActivityTypeService.php© AskJiminnyReportActivitySi© AutomatedReportsCallbackc) Aulomaredkepor sservice.c) DealStagesService.ohv©RecipientsService.php() ReportSort.ohpE) ReportSortDirection.ohoC) KioskService.ohoD MailMeetinaGeneratorM ©Auth2N Securitvclass AutomatedReportsServicepubLic function transtormkeportresults(Collection sautomatedReportResults): array'photoUrl' => $createdBy?->getPhotoUrl(),$data[] = ['id' => $automatedReportResult->getUvid(),"name' = sautomaceakeporckesult->gethameo"trequency' = schis->cranstormrrequencysreport->qecrrequencvop.'recipients' => sthis->ou1ldRec1p1entsSreport).'report_type' => $this->transformReportType($report-›getType()),'medna type' => SautomatedRedortResult->getMednalvoeo.'downLoadUrl' => Sthis->generateReportResuLtDownLoadUrl(SautomatedReportResult),'viewurl' => Sthis->generateRedortResultViewurlSautomatedReportResult)'generated_at' => $automatedReportResult->getGeneratedAt()?->toIs08601String().return $data;lusageprivate function buildRecipients(AutomatedReport $report): array$recipients = array_values($this->transformRecipients($report->getRecipients()));if (! $report->isAskJiminnyReport()) €recurn sreclplentsreturn [•array_values($this-›transformGroups(team: $report->getTeam(), groupsIds: $report->getGroups())),...Sreciplents.public function hasCallTypeConference(AutomatedReport $report): boolf...}3 usacespublic function hasCallTypeDialer(AutomatedReport $report): boolf...;+nansfarmens1 usageprivate function transformTeam(Team $team): arrayf..}inny // View pull request (today 16:29)© Service.php© TrackProviderInstalledEvent.php© Field.php© AutomatedReportsSendCommand.phpE custom.logA HS_local [jiminny@localhost]© SendReportMailJob.phpC) sendreportNotGenerateamallJob.phpA SF [jiminny@localhost] xA console (PROD]A console [EU]report-not-generated.blade.phgA console [STAGING]C | A102 X3 X34 A Y Q 159161— 162163164166172176_178180181182=184і 186187-191192193194195=196201204de jiminnyseleel * rrun accivity searches where mSELECT * FROM activity_search_filters WHERE activity_search_id =SELECT * FROM automated_reports where id = 68;UPUAItautomated reports set playbook categories = NULL where idSELECT * FROM automated_report_results where id = 275;SELECT * FROM automated_reports order by id deso;SELEcT * FROM automated report results order bv 1d desc:select * from activity_searches where user_id = 143;select * from askSELEC * FROM GrOUOS WHERE 10 = 14591SELECT * FROM users WHERE group_id = 1439;select * from permissions; # 158select * from roles:select * from permission_roleselect * from teams where id = 1;select * from groups g JOIN playbooks p 1..n<->1: on g.playbook_idselect * from groups where id = 28;select * from playbooks where team_id = 1;select * from playbooks where id = 179;select * from playbook_categories where id = 1391;select x Tron users where 10 = 145select * from crm_profiles where user_id = 143;select * from activities where crm_confiquration id = 39 and typeand crm_provider_id IS NOT NULL ORDER by id desc;select * from activities where id = 422003; # 00U0400000pB6fpMACSELECT ar.ig, ar.uig, ar.media type, ar.status, a.typeFROM automated_report_results arJOIN automated reports a ON a.1d = ar.report 1dWHERE a.tyRe = 'ask_jiminny'LIME 10:SELECTautomated revort results * FROM 'automated renort resiautomated_report_results.report_id =automated_reportsautomated_report_results'. 'generated_at' IS NOT NULLautomated nenonts'."team id' = 1AND JSON_CONTAINS( automated_reports'. 'recipients', 1635, '$.*100% L2Tue 21 Apr 17:51:06UAutomatedReportsRepositoryTestvCascadeReview Planhat IntearAutomated ReportsCalendar Multi-DomalbuthcRecipieits CAusonatekepertsservue.phtpincadegrups into recipients move to logic to separated methodThought for 1s>Error while editina AutomatedReportsService.phpThoughts>Read AutomatedRenortsService.oho #|844-888Thoughts•AutomatedReportsService.phpNow add the buildRecipients method. Ill place it next to the other transform/build helpers:m AutomatedReportsService.pnpNow verify no test breakage+0 ..o desker xee epokerories/AuEonavedkeports/eposttoryTestcone 2e tes ta/n.t/Services/Kiosk/AutonatedReports/+1-9lAsk anything (3AL)+ @ Code Claude Qnus 17 MediumWN Windsurf Toams 882-10UTF.8f?4 spaces...
|
NULL
|
-8551545819792306535
|
NULL
|
click
|
ocr
|
NULL
|
PhostormcodeFV faVsco.js vProletey© Profile.php© Q PhostormcodeFV faVsco.js vProletey© Profile.php© QueryBuilder.php©QueryHandler.php© Querylterator.php© QueryResults.php© Service.php© SyncBatchRedisService.phpD Traits© BaseClient.php© BaseService.phpo vacheaurmservcevecorator.countrycoderesolver.ong© CrmActivityProviderintegratedc) Crmacuivityservice.ong(c) crmeonrigurationsettingsservc) Crmobiectskesolver.phpc) DeraultProspectsearchstrateac) -mallhelper.ohoFindsProspectintertace.ohoC)LavoutManager.ohoC) OnoortunitvSvncStratedvResoC) ProspectSearchScone.oho) ProspectSearchStrategyFactorD ProspectSearchStrategyinterftewethelneelnieC RecordSelector.oho© ResolveCompanyNameByEmai© TimePeriodlterator.php& Import• Kioskv MAutomatedRenortc© ActivityTypeService.php© AskJiminnyReportActivitySi© AutomatedReportsCallbackc) Aulomaredkepor sservice.c) DealStagesService.ohv©RecipientsService.php() ReportSort.ohpE) ReportSortDirection.ohoC) KioskService.ohoD MailMeetinaGeneratorM ©Auth2N Securitvclass AutomatedReportsServicepubLic function transtormkeportresults(Collection sautomatedReportResults): array'photoUrl' => $createdBy?->getPhotoUrl(),$data[] = ['id' => $automatedReportResult->getUvid(),"name' = sautomaceakeporckesult->gethameo"trequency' = schis->cranstormrrequencysreport->qecrrequencvop.'recipients' => sthis->ou1ldRec1p1entsSreport).'report_type' => $this->transformReportType($report-›getType()),'medna type' => SautomatedRedortResult->getMednalvoeo.'downLoadUrl' => Sthis->generateReportResuLtDownLoadUrl(SautomatedReportResult),'viewurl' => Sthis->generateRedortResultViewurlSautomatedReportResult)'generated_at' => $automatedReportResult->getGeneratedAt()?->toIs08601String().return $data;lusageprivate function buildRecipients(AutomatedReport $report): array$recipients = array_values($this->transformRecipients($report->getRecipients()));if (! $report->isAskJiminnyReport()) €recurn sreclplentsreturn [•array_values($this-›transformGroups(team: $report->getTeam(), groupsIds: $report->getGroups())),...Sreciplents.public function hasCallTypeConference(AutomatedReport $report): boolf...}3 usacespublic function hasCallTypeDialer(AutomatedReport $report): boolf...;+nansfarmens1 usageprivate function transformTeam(Team $team): arrayf..}inny // View pull request (today 16:29)© Service.php© TrackProviderInstalledEvent.php© Field.php© AutomatedReportsSendCommand.phpE custom.logA HS_local [jiminny@localhost]© SendReportMailJob.phpC) sendreportNotGenerateamallJob.phpA SF [jiminny@localhost] xA console (PROD]A console [EU]report-not-generated.blade.phgA console [STAGING]C | A102 X3 X34 A Y Q 159161— 162163164166172176_178180181182=184і 186187-191192193194195=196201204de jiminnyseleel * rrun accivity searches where mSELECT * FROM activity_search_filters WHERE activity_search_id =SELECT * FROM automated_reports where id = 68;UPUAItautomated reports set playbook categories = NULL where idSELECT * FROM automated_report_results where id = 275;SELECT * FROM automated_reports order by id deso;SELEcT * FROM automated report results order bv 1d desc:select * from activity_searches where user_id = 143;select * from askSELEC * FROM GrOUOS WHERE 10 = 14591SELECT * FROM users WHERE group_id = 1439;select * from permissions; # 158select * from roles:select * from permission_roleselect * from teams where id = 1;select * from groups g JOIN playbooks p 1..n<->1: on g.playbook_idselect * from groups where id = 28;select * from playbooks where team_id = 1;select * from playbooks where id = 179;select * from playbook_categories where id = 1391;select x Tron users where 10 = 145select * from crm_profiles where user_id = 143;select * from activities where crm_confiquration id = 39 and typeand crm_provider_id IS NOT NULL ORDER by id desc;select * from activities where id = 422003; # 00U0400000pB6fpMACSELECT ar.ig, ar.uig, ar.media type, ar.status, a.typeFROM automated_report_results arJOIN automated reports a ON a.1d = ar.report 1dWHERE a.tyRe = 'ask_jiminny'LIME 10:SELECTautomated revort results * FROM 'automated renort resiautomated_report_results.report_id =automated_reportsautomated_report_results'. 'generated_at' IS NOT NULLautomated nenonts'."team id' = 1AND JSON_CONTAINS( automated_reports'. 'recipients', 1635, '$.*100% L2Tue 21 Apr 17:51:06UAutomatedReportsRepositoryTestvCascadeReview Planhat IntearAutomated ReportsCalendar Multi-DomalbuthcRecipieits CAusonatekepertsservue.phtpincadegrups into recipients move to logic to separated methodThought for 1s>Error while editina AutomatedReportsService.phpThoughts>Read AutomatedRenortsService.oho #|844-888Thoughts•AutomatedReportsService.phpNow add the buildRecipients method. Ill place it next to the other transform/build helpers:m AutomatedReportsService.pnpNow verify no test breakage+0 ..o desker xee epokerories/AuEonavedkeports/eposttoryTestcone 2e tes ta/n.t/Services/Kiosk/AutonatedReports/+1-9lAsk anything (3AL)+ @ Code Claude Qnus 17 MediumWN Windsurf Toams 882-10UTF.8f?4 spaces...
|
NULL
|
|
32706
|
663
|
30
|
2026-04-16T07:17:52.694153+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776323872694_m2.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
FirefoxFileEoitViewHistoryBookmarksProfilesToolsWi FirefoxFileEoitViewHistoryBookmarksProfilesToolsWindowHelpaccounts.google.com/v3/signin/identifier?authuser=0&continue=https%3A%2F%2Fmail.goog/e.com%2Fmail&ec=GAIAFw&hl=en&se40hohlion&dsh=S959363990%3A1776323854760180Support Daily • in 4h 43mA100%CThu 16 Apr 10:17:52G Gmail7 Jiminny x Shiji - Reconnecting theZ For you - Confluenceb Lukas Kovalik - Time Offu Product Growth Plattorm Userpilou Userpiloifix(security): composer dependend(&) JiminnyNew Tab• Jiminny+ New TabGSign into continue to Gmail= =nall or [EMAIL] loe maikconPasskey for [EMAIL] for [EMAIL] for [EMAIL] Tor cooce con&? Use another passkeyManace rassworesEnglish (United States) -HelpPrivacyTerms...
|
NULL
|
-8551391062437945401
|
NULL
|
click
|
ocr
|
NULL
|
FirefoxFileEoitViewHistoryBookmarksProfilesToolsWi FirefoxFileEoitViewHistoryBookmarksProfilesToolsWindowHelpaccounts.google.com/v3/signin/identifier?authuser=0&continue=https%3A%2F%2Fmail.goog/e.com%2Fmail&ec=GAIAFw&hl=en&se40hohlion&dsh=S959363990%3A1776323854760180Support Daily • in 4h 43mA100%CThu 16 Apr 10:17:52G Gmail7 Jiminny x Shiji - Reconnecting theZ For you - Confluenceb Lukas Kovalik - Time Offu Product Growth Plattorm Userpilou Userpiloifix(security): composer dependend(&) JiminnyNew Tab• Jiminny+ New TabGSign into continue to Gmail= =nall or [EMAIL] loe maikconPasskey for [EMAIL] for [EMAIL] for [EMAIL] Tor cooce con&? Use another passkeyManace rassworesEnglish (United States) -HelpPrivacyTerms...
|
NULL
|
|
46394
|
979
|
13
|
2026-04-17T10:34:21.028981+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-17/1776 /Users/lukas/.screenpipe/data/data/2026-04-17/1776422061028_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelplihl= Support Daily - in 1h 26 m100% <47-zshAPP (-zsh)|X4-zshDOCKER881DEV (-zsh)APP (-zsh)X3./public/vue-assets/assets/ondemand-CE8XLH98.js:./public/vue-assets/assets/CrmLink-DKYsnHnx.js./public/vue-assets/assets/liquor-tree-COUefof4.js./public/vue-assets/assets/DealRiskList-3DSxnTNQ.js:./public/vue-assets/assets/AskAnything-BQ36E0GS.js:./public/vue-assets/assets/lib-CwM9toD2.js./public/vue-assets/assets/AppFormField-CqCXZwrk.js./public/vue-assets/assets/deal-view-B92qaj32.js:./public/vue-assets/assets/exports-D1lmea40.js../public/vue-assets/assets/playlists-u3UeUUz5.js../public/vue-assets/assets/callScoringTemplates-zeRn40ul.js../public/vue-assets/assets/_copy0bject-USkOnlaQ.js../public/vue-assets/assets/pusher-znYCfz7U.js•/public/vue-assets/assets/onboard-CCnYJmCP.js./public/vue-assets/assets/StatusBadge-CNGFZm8P.js./public/vue-assets/assets/kiosk-D6N5-ivP.js./public/vue-assets/assets/preload-helper-DCvhahzG.js../public/vue-assets/assets/deal-insights-jlkz65MZ.js../public/vue-assets/assets/ListView-DmKqYVi1.js:./public/vue-assets/assets/_plugin-vue_export-helper-DD3s5456.js./public/vue-assets/assets/WelcomeLayout-B6wd32HG.js../public/vue-assets/assets/dashboard-BWoBIPKA.js:./public/vue-assets/assets/emoji-input-CSq87OVy.js../public/vue-assets/assets/AppButton-D3qMdODr.js../public/vue-assets/assets/sentry-icPKypZa.js:./public/vue-assets/assets/OrgSettingsLayout-BZ-JOgiH.js./public/vue-assets/assets/vuex.esm-bundler-DqfufJ2-.js./public/vue-assets/assets/playback-DKsqp_al.js./public/vue-assets/assets/index.module-Bjlhgfd1.js./public/vue-assets/assets/intl-tel-input-BW4mv40Q.js:./public/vue-assets/assets/team-insights-D9P6ojjD.js../public/vue-assets/assets/popper-CQwVcrX4.js../public/vue-assets/assets/PhoneField-CwCIoAYm.js./public/vue-assets/assets/live-DWumv-QD.js../public/vue-assets/assets/video-js-skin.less_vue_type_style_index_0_src_true_lang-BN0485xV.js../public/vue-assets/assets/index-DiEs6xIb.js:./public/vue-assets/assets/logged-in-layout-Cq1ztH50.js• ₴526.88kB27.91kB30.75kB34.39kB39.50kB39.69kB41.91kB43.22kB47.84kB48.28kB55.13kB61.28kB62.98kB63.11kB64.66kB79.60kB82.59kB94.84kB115.71kB117.59 kB120.67kB128.71kB129.28 kB133.44kB164.28kB176.33kB180.40kB198.79kB218.14kB264.94 kВ298.57kB307.13kB343.99kB367.43kB689.63kB825.23 kB1,402.70 kB[plugin builtin:vite-reporter](!) Somechunks are larger than 500 kB after minification. Consider:- Using dynamic import() to code-split the application- Use build.rolldown0ptions.output.codeSplitting to improve chunking: [URL_WITH_CREDENTIALS] ₴8APP...
|
NULL
|
-8551202271736587053
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelplihl= Support Daily - in 1h 26 m100% <47-zshAPP (-zsh)|X4-zshDOCKER881DEV (-zsh)APP (-zsh)X3./public/vue-assets/assets/ondemand-CE8XLH98.js:./public/vue-assets/assets/CrmLink-DKYsnHnx.js./public/vue-assets/assets/liquor-tree-COUefof4.js./public/vue-assets/assets/DealRiskList-3DSxnTNQ.js:./public/vue-assets/assets/AskAnything-BQ36E0GS.js:./public/vue-assets/assets/lib-CwM9toD2.js./public/vue-assets/assets/AppFormField-CqCXZwrk.js./public/vue-assets/assets/deal-view-B92qaj32.js:./public/vue-assets/assets/exports-D1lmea40.js../public/vue-assets/assets/playlists-u3UeUUz5.js../public/vue-assets/assets/callScoringTemplates-zeRn40ul.js../public/vue-assets/assets/_copy0bject-USkOnlaQ.js../public/vue-assets/assets/pusher-znYCfz7U.js•/public/vue-assets/assets/onboard-CCnYJmCP.js./public/vue-assets/assets/StatusBadge-CNGFZm8P.js./public/vue-assets/assets/kiosk-D6N5-ivP.js./public/vue-assets/assets/preload-helper-DCvhahzG.js../public/vue-assets/assets/deal-insights-jlkz65MZ.js../public/vue-assets/assets/ListView-DmKqYVi1.js:./public/vue-assets/assets/_plugin-vue_export-helper-DD3s5456.js./public/vue-assets/assets/WelcomeLayout-B6wd32HG.js../public/vue-assets/assets/dashboard-BWoBIPKA.js:./public/vue-assets/assets/emoji-input-CSq87OVy.js../public/vue-assets/assets/AppButton-D3qMdODr.js../public/vue-assets/assets/sentry-icPKypZa.js:./public/vue-assets/assets/OrgSettingsLayout-BZ-JOgiH.js./public/vue-assets/assets/vuex.esm-bundler-DqfufJ2-.js./public/vue-assets/assets/playback-DKsqp_al.js./public/vue-assets/assets/index.module-Bjlhgfd1.js./public/vue-assets/assets/intl-tel-input-BW4mv40Q.js:./public/vue-assets/assets/team-insights-D9P6ojjD.js../public/vue-assets/assets/popper-CQwVcrX4.js../public/vue-assets/assets/PhoneField-CwCIoAYm.js./public/vue-assets/assets/live-DWumv-QD.js../public/vue-assets/assets/video-js-skin.less_vue_type_style_index_0_src_true_lang-BN0485xV.js../public/vue-assets/assets/index-DiEs6xIb.js:./public/vue-assets/assets/logged-in-layout-Cq1ztH50.js• ₴526.88kB27.91kB30.75kB34.39kB39.50kB39.69kB41.91kB43.22kB47.84kB48.28kB55.13kB61.28kB62.98kB63.11kB64.66kB79.60kB82.59kB94.84kB115.71kB117.59 kB120.67kB128.71kB129.28 kB133.44kB164.28kB176.33kB180.40kB198.79kB218.14kB264.94 kВ298.57kB307.13kB343.99kB367.43kB689.63kB825.23 kB1,402.70 kB[plugin builtin:vite-reporter](!) Somechunks are larger than 500 kB after minification. Consider:- Using dynamic import() to code-split the application- Use build.rolldown0ptions.output.codeSplitting to improve chunking: [URL_WITH_CREDENTIALS] ₴8APP...
|
46392
|
|
16507
|
363
|
93
|
2026-04-14T15:19:19.495758+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-14/1776 /Users/lukas/.screenpipe/data/data/2026-04-14/1776179959495_m2.jpg...
|
Boosteroid
|
Boosteroid
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
51050415020021/25toFeudal Age--reuaai Age Kesearen 51050415020021/25toFeudal Age--reuaai Age Kesearen Compiete-KovankiuKas aavancea to tne reuaaiAge.Game Paused (P)Click where you want to build the building.7 Vikramaditya I: 684/6841 kovaliklukas: 673/6733 Mari Djata I: 657/6576 Prithviraj Chauhan: 642/6428 Ellac the Hun: 640/640Yekuno Amlak: 631/6314 Wen Tianxiang: 608/6085 Danylo Kobiakovych: 549/549...
|
NULL
|
-8551089685035591135
|
NULL
|
visual_change
|
ocr
|
NULL
|
51050415020021/25toFeudal Age--reuaai Age Kesearen 51050415020021/25toFeudal Age--reuaai Age Kesearen Compiete-KovankiuKas aavancea to tne reuaaiAge.Game Paused (P)Click where you want to build the building.7 Vikramaditya I: 684/6841 kovaliklukas: 673/6733 Mari Djata I: 657/6576 Prithviraj Chauhan: 642/6428 Ellac the Hun: 640/640Yekuno Amlak: 631/6314 Wen Tianxiang: 608/6085 Danylo Kobiakovych: 549/549...
|
16506
|
|
29883
|
606
|
90
|
2026-04-15T14:50:14.398813+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-15/1776 /Users/lukas/.screenpipe/data/data/2026-04-15/1776264614398_m1.jpg...
|
Boosteroid
|
Boosteroid
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
+SlackFileEditViewGoEDHomeDMSActivityFilesLater..• +SlackFileEditViewGoEDHomeDMSActivityFilesLater..•More+→Jiminny ...+CHISHICCIITIS# frontend# general# infra-changes# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...Direct messagesAneliya Angelova, ...Stoyan TanevVes. Galya Dimitrova€. Vasil VasilevR. Steliyan GeorgievAdelina Petrova, Ili...P. Adelina PetrovaR. Nikolay Nikolovii: AppsJira CloudToastHistoryWindowHelpSearch Jiminny Inc# releases8 226 0MessagesVIeWSOU@ Files• Bookmarks+Today ~GitHub APP3:28 PM7 new commits pushed to master by nikolay-yankov24b989ee - Enhance SECFIXdocumentation and policiesa3a0a742 - Update SECFIX Slack channelreference in documentation and workflowfiles071c999d - Merge branch 'master' intoimprove-secfix-bot-15-04-2026981e9a1a - Update SECFIX_PROMPT.mdto enhance clarity on upgrade safety andchangelog reviews6e938e53 - Enhance SECFIX workflow withSlack notification optionsShow more( jiminny/app Added by GitHubNewCircleCl APP3:53 PMDeployment Successful!Project: appWhen:04/15/202612:53:30Tag:View JobMessage #releases+Aa...Activity MonitorAll ProcessesProcess NameBoosteroidWindowServerFirefoxFirefoxCP Isolated Web ContentFirefoxFirefoxCP Isolated Web ContentCursorUlViewService (Not Responding)FirefoxCP Isolated Web ContentFirefox GPU HelperFirefox GPU HelperVTDecoderXPCServiceFirefoxCP Isolated Web ContentSlack Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentNotion Calendar Helper (Renderer)Notion Helper (Renderer)claudeClaude Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentiTerm2FirefoxCP Isolated Web ContentCode Helper (Renderer)MEMORY PRESSUREMem...2,05 GB1,21 GB1,00 GB963,1 MB839,8 MB806,2 MB794,5 MB547,6 MB544,3 MB543,8 MB516,2 MB498,7 MB451,9 MB424,8 MB416,9 MB396,6 MB393,6 MB387,8 MB372,5 MB343,2 MB329,9 MB326,2 MB326,0 MB295,6 MB268,7 MB252,3 MB243,4 MB214,5 MBPhysical Memory:Memory Used:Cached Files:Swap Used:100% <478Wed 15 Apr 17:50:14CPUMemoryDiskThreads3923722684282530261124162425252326221521131528282718EnergyPorts60819 8127231251 20012920 047127245250168121187123122124119123118172328722191251281 832124221PID93892407801442974146644203084236713801914673938993548041863352763583143016368984365248173265481148509106051935833482984878561388534016,00 GB14,23 GB <1,72 GB3,13 GBApp Memory:Wired Memory:Compressed:NetworkUserlukas_windowserverlukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukas3,92 GB2,87 GB6,89 GB...
|
NULL
|
-8550698158541296456
|
NULL
|
click
|
ocr
|
NULL
|
+SlackFileEditViewGoEDHomeDMSActivityFilesLater..• +SlackFileEditViewGoEDHomeDMSActivityFilesLater..•More+→Jiminny ...+CHISHICCIITIS# frontend# general# infra-changes# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...Direct messagesAneliya Angelova, ...Stoyan TanevVes. Galya Dimitrova€. Vasil VasilevR. Steliyan GeorgievAdelina Petrova, Ili...P. Adelina PetrovaR. Nikolay Nikolovii: AppsJira CloudToastHistoryWindowHelpSearch Jiminny Inc# releases8 226 0MessagesVIeWSOU@ Files• Bookmarks+Today ~GitHub APP3:28 PM7 new commits pushed to master by nikolay-yankov24b989ee - Enhance SECFIXdocumentation and policiesa3a0a742 - Update SECFIX Slack channelreference in documentation and workflowfiles071c999d - Merge branch 'master' intoimprove-secfix-bot-15-04-2026981e9a1a - Update SECFIX_PROMPT.mdto enhance clarity on upgrade safety andchangelog reviews6e938e53 - Enhance SECFIX workflow withSlack notification optionsShow more( jiminny/app Added by GitHubNewCircleCl APP3:53 PMDeployment Successful!Project: appWhen:04/15/202612:53:30Tag:View JobMessage #releases+Aa...Activity MonitorAll ProcessesProcess NameBoosteroidWindowServerFirefoxFirefoxCP Isolated Web ContentFirefoxFirefoxCP Isolated Web ContentCursorUlViewService (Not Responding)FirefoxCP Isolated Web ContentFirefox GPU HelperFirefox GPU HelperVTDecoderXPCServiceFirefoxCP Isolated Web ContentSlack Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentNotion Calendar Helper (Renderer)Notion Helper (Renderer)claudeClaude Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentiTerm2FirefoxCP Isolated Web ContentCode Helper (Renderer)MEMORY PRESSUREMem...2,05 GB1,21 GB1,00 GB963,1 MB839,8 MB806,2 MB794,5 MB547,6 MB544,3 MB543,8 MB516,2 MB498,7 MB451,9 MB424,8 MB416,9 MB396,6 MB393,6 MB387,8 MB372,5 MB343,2 MB329,9 MB326,2 MB326,0 MB295,6 MB268,7 MB252,3 MB243,4 MB214,5 MBPhysical Memory:Memory Used:Cached Files:Swap Used:100% <478Wed 15 Apr 17:50:14CPUMemoryDiskThreads3923722684282530261124162425252326221521131528282718EnergyPorts60819 8127231251 20012920 047127245250168121187123122124119123118172328722191251281 832124221PID93892407801442974146644203084236713801914673938993548041863352763583143016368984365248173265481148509106051935833482984878561388534016,00 GB14,23 GB <1,72 GB3,13 GBApp Memory:Wired Memory:Compressed:NetworkUserlukas_windowserverlukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukas3,92 GB2,87 GB6,89 GB...
|
29881
|
|
63303
|
1370
|
25
|
2026-04-21T08:47:05.643206+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-21/1776 /Users/lukas/.screenpipe/data/data/2026-04-21/1776761225643_m2.jpg...
|
PhpStorm
|
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Find in Files
100+ matches in 26+ files
File mask: Find in Files
100+ matches in 26+ files
File mask:
*.php
*.php
Auto
*.php
Filter Search Results
Pin Window
Search History
LeadConverted
New Line
Match case
Words
Regex
Replace History
Replace
New Line
Preserve case
In Project
Module
Directory
Scope
Module
/Users/lukas/jiminny/app/app/Providers
/Users/lukas/jiminny/app/app/Providers
/Users/lukas/jiminny/app/app/Services/Crm/IntegrationApp
/Users/lukas/jiminny/app/app/Services/Crm/Salesforce
/Users/lukas/jiminny/app/app/Events/Activities/Crm
/Users/lukas/jiminny/app/app/Listeners/Playbooks
/Users/lukas/jiminny/app/app/Console/Commands/Crm
/Users/lukas/jiminny/app/app/Services/Crm
/Users/lukas/jiminny/app/app/Listeners/Activities/Coaching/UserPilot
/Users/lukas/jiminny/app/app/Listeners/AutomatedReports/UserPilot
/Users/lukas/jiminny/app/app/Listeners/Crm
/Users/lukas/jiminny/app/app/Http/Controllers
/Users/lukas/jiminny/app/app/Console/Commands/Reports
/Users/lukas/jiminny/app/app/VO/Repository/OnDemandActivitySearch
/Users/lukas/jiminny/app/app/Listeners/Activities/Conferences/UserPilot
/Users/lukas/jiminny/app/app/Http/Controllers/Webhook
/Users/lukas/jiminny/app/resources/views/emails/reports
/Users/lukas/jiminny/app/app/Mail/Reports
/Users/lukas/jiminny/app/app/Repositories
/Users/lukas/jiminny/app/app/Http/Controllers/API/UserAutomatedReports
/Users/lukas/jiminny/app/app/Services/Kiosk/AutomatedReports
/Users/lukas/jiminny/app/app/Component/ActivitySearch/Service
/Users/lukas/jiminny/app/app/Jobs/AutomatedReports
/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Salesforce
/Users/lukas/jiminny/app/routes
/Users/lukas/jiminny/app/app/Console/Commands
/Users/lukas/jiminny/app/database/migrations
/Users/lukas/Library/Application Support/JetBrains/PhpStorm2026.1/consoles/db/325d461a-c90f-430a-99d4-6ddfce0c61d7
/Users/lukas/jiminny/app/app/Http/Controllers/API/V2
/Users/lukas/jiminny/app/app/Jobs/Crm/Hubspot
/Users/lukas/jiminny/app/app/Component/DealInsights
/Users/lukas/jiminny/app/app/Policies
/Users/lukas/jiminny/app/app/Services/Crm/Helpers
/Users/lukas/jiminny/app/app/Jobs/Crm
/Users/lukas/jiminny/app/app/Events/Crm
/Users/lukas/jiminny/app/app/Models
/Users/lukas/jiminny/app/app/Listeners/Teams
/Users/lukas/jiminny/app/app/Jobs/Crm/Salesforce
/Users/lukas/jiminny/app/app
/Users/lukas/jiminny/app/app/Services/Crm/Hubspot/Journal
/Users/lukas/jiminny/app/app/Services/Crm/Hubspot
/Users/lukas/jiminny/app/app/Services/Crm/Hubspot/ServiceTraits
/Users/lukas/jiminny/app/app/Services/Crm/Hubspot/OpportunitySyncStrategy
/Users/lukas/jiminny/app/app/Services/Crm/Hubspot/Webhook
/Users/lukas/jiminny/app/storage/logs
/Users/lukas/jiminny/app
/Users/lukas/jiminny/app/app/Services/Internal
/Users/lukas/jiminny/app/app/Listeners/Transcription
/Users/lukas/jiminny/app/app/Repositories/Crm
/Users/lukas/jiminny/app/tests/Unit/Listeners/Teams
/Users/lukas/jiminny/app/app/Models/Crm
/Users/lukas/Library/Application Support/JetBrains/PhpStorm2026.1/consoles/db/91133dfa-8d71-4e12-bfb8-fec7f1afba8f
/Users/lukas/jiminny/app/app/Observers
/Users/lukas/jiminny/app/app/Services/Mail
/Users/lukas/jiminny/app/app/Console/Commands/Activities
/Users/lukas/jiminny/app/app/Console/Commands/Activities/Migrator
/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Hubspot/ServiceTraits
/Users/lukas/jiminny/app/app/Jobs/User
/Users/lukas/jiminny/app/app/Models/Activity
/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Hubspot/Webhook
/Users/lukas/jiminny/app/app/Component/AiAutomation/Listeners/PendingAnalysis
/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Hubspot
/Users/lukas/jiminny/app/app/Component/ActivitySearch/FilterDefinition/DealInsights
/Users/lukas/jiminny/app/app/Services/Crm/DecorateActivity
/Users/lukas/jiminny/app/app/Component/Activity/Event
/Users/lukas/jiminny/app/app/Component/Sidekick
/Users/lukas/jiminny/app/app/Jobs/Activity/PushSummaryToCrm
/Users/lukas/jiminny/app/app/Listeners/Activities/Conferences
/Users/lukas/jiminny/app/app/Listeners/Activities/Bots
/Users/lukas/jiminny/app/app/Services/RecallAI/Webhooks/Handlers
/Users/lukas/jiminny/app/app/Events/Activities/Bots
/Users/lukas/jiminny/app/app/Component/MeetingBot
/Users/lukas/jiminny/app/app/Services/Activity/RingCentral
/Users/lukas/jiminny/app/app/Http/Controllers/Webhook/Hubspot
/Users/lukas/jiminny/app/app/Services/Activity/Gmail
/Users/lukas/jiminny/app/app/Services/Crm/CrmObjects/ServiceTraits
/Users/lukas/jiminny/app/app/Jobs/Mailbox
/Users/lukas/jiminny/app/app/Console
/Users/lukas/jiminny/app/front-end/src/composables
/Users/lukas/jiminny/app/app/Console/Commands/Calendars
/Users/lukas/jiminny/app/app/Http/Controllers/API
/Users/lukas/jiminny/app/app/Http/Controllers/Internal/WebhookReceiver
/Users/lukas/jiminny/app/app/Services/Crm/IntegrationApp/ServiceTraits
/Users/lukas/jiminny/app/app/Component/Queue
/Users/lukas/jiminny/app/app/Console/Commands/Crm/Hubspot
/Users/lukas/jiminny/app/app/Component/Transcription/Job
/Users/lukas/jiminny/app/tests/Unit/Services/Listeners
/Users/lukas/jiminny/app/app/Services/Crm/Listeners
/Users/lukas/jiminny/app/app/Traits
/Users/lukas/jiminny/app/tests/Unit/Jobs/Crm/Hubspot
/Users/lukas/jiminny/app/tests/Unit/Services/Crm
/Users/lukas/jiminny/app/app/Services/Activity
/Users/lukas/jiminny/app/app/Services/Calendar/Command
/Users/lukas/jiminny/app/.idea/queries
/Users/lukas/jiminny/app/vendor/hubspot/api-client/codegen/Crm
/Users/lukas/jiminny/app/vendor/hubspot
/Users/lukas/jiminny/app/app/Services/Crm/Hubspot/Fields
/Users/lukas/jiminny/app/app/Services/Crm/Copper
/Users/lukas/jiminny/app/app/Services/Crm/Bullhorn
/Users/lukas/jiminny/app/app/Notifications/Channels
/Users/lukas/jiminny/app/tests/Unit
/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Hubspot/Journal
/Users/lukas/jiminny/app/app/Interactions/Settings/Teams
/Users/lukas/jiminny/app/app/Exceptions/Crm
/Users/lukas/jiminny/app/app/Exceptions
/Users/lukas/jiminny/app/vendor/hubspot/hubspot-php/src/Endpoints
/Users/lukas/jiminny/app/config
/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Hubspot/OpportunitySyncStrategy
/Users/lukas/jiminny/app/vendor/laravel/framework/src/Illuminate/Redis/Connections
/Users/lukas/jiminny/app/app/Http/Controllers/Settings/Teams
/Users/lukas/jiminny/app/app/Services/Crm/Hubspot/Webhook/Traits
/Users/lukas/jiminny/app/vendor/laravel/framework/src/Illuminate/Broadcasting
/Users/lukas/jiminny/app/app/Component/FeatureFlags
/Users/lukas/jiminny/app/app/Component/Activity
/Users/lukas/jiminny/app/app/Component/ActivitySearch
/Users/lukas/jiminny/app/tests/Unit/Events/Activities/Crm
/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Hubspot/Pagination
/Users/lukas/jiminny/app/app/Console/Commands/Dev
/Users/lukas/jiminny/app/front-end
/Users/lukas/jiminny/app/app/Component/Prophet
/Users/lukas/jiminny/app/tests/Unit/Services/Crm/IntegrationApp
/Users/lukas/jiminny/app/app/Component/AskAnything
/Users/lukas/jiminny/app/app/Component/AskJiminnyAi/OnDemandLevel/Events
/Users/lukas/jiminny/app/app/Component/AskAnything/Events
/Users/lukas/jiminny/app/app/Component/AskJiminnyAi/DealLevel/Traits
/Users/lukas/jiminny/app/app/Component/ProphetAi
/Users/lukas/Library/Application Support/JetBrains/PhpStorm2026.1/consoles/db/d1e2c340-64e9-49c6-aa9a-196201874532
/Users/lukas/jiminny/app/app/Http/Controllers/API/Page
/Users/lukas/jiminny/app/front-end/src/components/ondemand/ActivityList
/Users/lukas/Library/Application Support/JetBrains/PhpStorm2026.1/consoles/db/5b1549d5-9876-4d9e-9ce3-025f12a83283
/Users/lukas/jiminny/app/app/Contracts/Repositories
/Users/lukas/jiminny/app/app/Http/Controllers/Kiosk
/Users/lukas/jiminny/app/app/Component/AiAutomation/Actions
/Users/lukas/jiminny/app/app/Services/Activity/HubSpot
/Users/lukas/jiminny/app/app/Services/Crm/Pipedrive
/Users/lukas/jiminny/app/app/Jobs/Activity/Import
/Users/lukas/jiminny/app/app/Events/Import
/Users/lukas/jiminny/app/app/Events/Activities/Dialers
/Users/lukas/jiminny/app/tests
/Users/lukas/jiminny/app/app/Events/Activities
/Users/lukas/jiminny/app/tests/Unit/Jobs/Activity/PushSummaryToCrm
/Users/lukas/jiminny/app/app/Console/Commands/Analytics
/Users/lukas/jiminny/app/tests/Unit/Services/Kiosk/AutomatedReports
/Users/lukas/jiminny/app/app/Http/Middleware
/Users/lukas/jiminny/app/app/Http/Controllers/Auth
/Users/lukas/jiminny/app/tests/Unit/Jobs/Crm
/Users/lukas/jiminny/app/app/Services/Crm/IntegrationApp/Api
/Users/lukas/jiminny/app/app/Services/Crm/IntegrationApp/Accessors
/Users/lukas/jiminny/app/app/Services/Crm/Close
/Users/lukas/jiminny/app/app/Services
/Users/lukas/jiminny/app/app/Http/Transformers
/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Pipedrive
/Users/lukas/jiminny/app/app/Listeners/Activities/Crm/Summary
/Users/lukas/jiminny/app/app/Services/Activity/AmazonConnect
/Users/lukas/jiminny/app/app/Models/Participant
/Users/lukas/jiminny/app/app/Events/Activities/Connections
/Users/lukas/jiminny/app/app/Listeners/Activities/Crm
/Users/lukas/jiminny/app/app/Services/Calendar
/Users/lukas/jiminny/app/app/Jobs/DealRisks
/Users/lukas/jiminny/app/front-end/src...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"Find in Files","depth":1,"bounds":{"left":0.2992021,"top":0.12609737,"width":0.024601065,"height":0.013567438},"role_description":"text"},{"role":"AXStaticText","text":"100+ matches in 26+ files","depth":1,"bounds":{"left":0.32779256,"top":0.12609737,"width":0.05285904,"height":0.013567438},"role_description":"text"},{"role":"AXCheckBox","text":"File mask:","depth":1,"bounds":{"left":0.5315825,"top":0.12290503,"width":0.029587766,"height":0.019952115},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXComboBox","text":"*.php","depth":1,"bounds":{"left":0.5621675,"top":0.11971269,"width":0.027925532,"height":0.027134877},"value":"*.php","role_description":"combo box","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"*.php","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"Auto","depth":6,"role_description":"text"},{"role":"AXTextField","text":"*.php","depth":2,"bounds":{"left":0.5661569,"top":0.12609737,"width":0.011635638,"height":0.013567438},"value":"*.php","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":1,"bounds":{"left":0.5944149,"top":0.12290503,"width":0.00731383,"height":0.017557861},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Pin Window","depth":1,"bounds":{"left":0.6037234,"top":0.12290503,"width":0.00731383,"height":0.017557861},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":1,"bounds":{"left":0.2962101,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"LeadConverted","depth":2,"bounds":{"left":0.30718085,"top":0.15403032,"width":0.26196808,"height":0.017557861},"value":"LeadConverted","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":1,"bounds":{"left":0.578125,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match case","depth":1,"bounds":{"left":0.5880984,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":1,"bounds":{"left":0.59674203,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":1,"bounds":{"left":0.60538566,"top":0.15403032,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":1,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":2,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":1,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":1,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In Project","depth":2,"bounds":{"left":0.2992021,"top":0.1867518,"width":0.022938829,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Module","depth":2,"bounds":{"left":0.32214096,"top":0.1867518,"width":0.019281914,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Directory","depth":2,"bounds":{"left":0.3414229,"top":0.1867518,"width":0.022606382,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Scope","depth":2,"bounds":{"left":0.36402926,"top":0.1867518,"width":0.017287234,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"Module","depth":1,"bounds":{"left":0.27027926,"top":1.0,"width":0.099734046,"height":0.0},"role_description":"pop up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXComboBox","text":"/Users/lukas/jiminny/app/app/Providers","depth":1,"bounds":{"left":0.27027926,"top":1.0,"width":0.1974734,"height":0.0},"value":"/Users/lukas/jiminny/app/app/Providers","role_description":"combo box","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Providers","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Crm/IntegrationApp","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Crm/Salesforce","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Events/Activities/Crm","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Listeners/Playbooks","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Console/Commands/Crm","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Crm","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Listeners/Activities/Coaching/UserPilot","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Listeners/AutomatedReports/UserPilot","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Listeners/Crm","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Http/Controllers","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Console/Commands/Reports","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/VO/Repository/OnDemandActivitySearch","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Listeners/Activities/Conferences/UserPilot","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Http/Controllers/Webhook","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/resources/views/emails/reports","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Mail/Reports","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Repositories","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Http/Controllers/API/UserAutomatedReports","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Kiosk/AutomatedReports","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Component/ActivitySearch/Service","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Jobs/AutomatedReports","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Salesforce","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/routes","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Console/Commands","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/database/migrations","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/Library/Application Support/JetBrains/PhpStorm2026.1/consoles/db/325d461a-c90f-430a-99d4-6ddfce0c61d7","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Http/Controllers/API/V2","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Jobs/Crm/Hubspot","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Component/DealInsights","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Policies","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Crm/Helpers","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Jobs/Crm","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Events/Crm","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Models","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Listeners/Teams","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Jobs/Crm/Salesforce","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Crm/Hubspot/Journal","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Crm/Hubspot","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Crm/Hubspot/ServiceTraits","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Crm/Hubspot/OpportunitySyncStrategy","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Crm/Hubspot/Webhook","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/storage/logs","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Internal","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Listeners/Transcription","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Repositories/Crm","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/tests/Unit/Listeners/Teams","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Models/Crm","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/Library/Application Support/JetBrains/PhpStorm2026.1/consoles/db/91133dfa-8d71-4e12-bfb8-fec7f1afba8f","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Observers","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Mail","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Console/Commands/Activities","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Console/Commands/Activities/Migrator","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Hubspot/ServiceTraits","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Jobs/User","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Models/Activity","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Hubspot/Webhook","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Component/AiAutomation/Listeners/PendingAnalysis","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Hubspot","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Component/ActivitySearch/FilterDefinition/DealInsights","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Crm/DecorateActivity","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Component/Activity/Event","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Component/Sidekick","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Jobs/Activity/PushSummaryToCrm","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Listeners/Activities/Conferences","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Listeners/Activities/Bots","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/RecallAI/Webhooks/Handlers","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Events/Activities/Bots","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Component/MeetingBot","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Activity/RingCentral","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Http/Controllers/Webhook/Hubspot","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Activity/Gmail","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Crm/CrmObjects/ServiceTraits","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Jobs/Mailbox","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Console","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/front-end/src/composables","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Console/Commands/Calendars","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Http/Controllers/API","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Http/Controllers/Internal/WebhookReceiver","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Crm/IntegrationApp/ServiceTraits","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Component/Queue","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Console/Commands/Crm/Hubspot","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Component/Transcription/Job","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/tests/Unit/Services/Listeners","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Crm/Listeners","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Traits","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/tests/Unit/Jobs/Crm/Hubspot","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/tests/Unit/Services/Crm","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Activity","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Calendar/Command","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/.idea/queries","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/vendor/hubspot/api-client/codegen/Crm","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/vendor/hubspot","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Crm/Hubspot/Fields","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Crm/Copper","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Crm/Bullhorn","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Notifications/Channels","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/tests/Unit","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Hubspot/Journal","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Interactions/Settings/Teams","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Exceptions/Crm","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Exceptions","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/vendor/hubspot/hubspot-php/src/Endpoints","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/config","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Hubspot/OpportunitySyncStrategy","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/vendor/laravel/framework/src/Illuminate/Redis/Connections","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Http/Controllers/Settings/Teams","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Crm/Hubspot/Webhook/Traits","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/vendor/laravel/framework/src/Illuminate/Broadcasting","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Component/FeatureFlags","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Component/Activity","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Component/ActivitySearch","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/tests/Unit/Events/Activities/Crm","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Hubspot/Pagination","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Console/Commands/Dev","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/front-end","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Component/Prophet","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/tests/Unit/Services/Crm/IntegrationApp","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Component/AskAnything","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Component/AskJiminnyAi/OnDemandLevel/Events","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Component/AskAnything/Events","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Component/AskJiminnyAi/DealLevel/Traits","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Component/ProphetAi","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/Library/Application Support/JetBrains/PhpStorm2026.1/consoles/db/d1e2c340-64e9-49c6-aa9a-196201874532","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Http/Controllers/API/Page","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/front-end/src/components/ondemand/ActivityList","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/Library/Application Support/JetBrains/PhpStorm2026.1/consoles/db/5b1549d5-9876-4d9e-9ce3-025f12a83283","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Contracts/Repositories","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Http/Controllers/Kiosk","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Component/AiAutomation/Actions","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Activity/HubSpot","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Crm/Pipedrive","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Jobs/Activity/Import","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Events/Import","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Events/Activities/Dialers","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/tests","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Events/Activities","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/tests/Unit/Jobs/Activity/PushSummaryToCrm","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Console/Commands/Analytics","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/tests/Unit/Services/Kiosk/AutomatedReports","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Http/Middleware","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Http/Controllers/Auth","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/tests/Unit/Jobs/Crm","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Crm/IntegrationApp/Api","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Crm/IntegrationApp/Accessors","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Crm/Close","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Http/Transformers","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Pipedrive","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Listeners/Activities/Crm/Summary","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Activity/AmazonConnect","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Models/Participant","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Events/Activities/Connections","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Listeners/Activities/Crm","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Services/Calendar","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/app/Jobs/DealRisks","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"/Users/lukas/jiminny/app/front-end/src","depth":6,"role_description":"text"}]...
|
-8550417436641721927
|
-5770845894756731577
|
click
|
accessibility
|
NULL
|
Find in Files
100+ matches in 26+ files
File mask: Find in Files
100+ matches in 26+ files
File mask:
*.php
*.php
Auto
*.php
Filter Search Results
Pin Window
Search History
LeadConverted
New Line
Match case
Words
Regex
Replace History
Replace
New Line
Preserve case
In Project
Module
Directory
Scope
Module
/Users/lukas/jiminny/app/app/Providers
/Users/lukas/jiminny/app/app/Providers
/Users/lukas/jiminny/app/app/Services/Crm/IntegrationApp
/Users/lukas/jiminny/app/app/Services/Crm/Salesforce
/Users/lukas/jiminny/app/app/Events/Activities/Crm
/Users/lukas/jiminny/app/app/Listeners/Playbooks
/Users/lukas/jiminny/app/app/Console/Commands/Crm
/Users/lukas/jiminny/app/app/Services/Crm
/Users/lukas/jiminny/app/app/Listeners/Activities/Coaching/UserPilot
/Users/lukas/jiminny/app/app/Listeners/AutomatedReports/UserPilot
/Users/lukas/jiminny/app/app/Listeners/Crm
/Users/lukas/jiminny/app/app/Http/Controllers
/Users/lukas/jiminny/app/app/Console/Commands/Reports
/Users/lukas/jiminny/app/app/VO/Repository/OnDemandActivitySearch
/Users/lukas/jiminny/app/app/Listeners/Activities/Conferences/UserPilot
/Users/lukas/jiminny/app/app/Http/Controllers/Webhook
/Users/lukas/jiminny/app/resources/views/emails/reports
/Users/lukas/jiminny/app/app/Mail/Reports
/Users/lukas/jiminny/app/app/Repositories
/Users/lukas/jiminny/app/app/Http/Controllers/API/UserAutomatedReports
/Users/lukas/jiminny/app/app/Services/Kiosk/AutomatedReports
/Users/lukas/jiminny/app/app/Component/ActivitySearch/Service
/Users/lukas/jiminny/app/app/Jobs/AutomatedReports
/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Salesforce
/Users/lukas/jiminny/app/routes
/Users/lukas/jiminny/app/app/Console/Commands
/Users/lukas/jiminny/app/database/migrations
/Users/lukas/Library/Application Support/JetBrains/PhpStorm2026.1/consoles/db/325d461a-c90f-430a-99d4-6ddfce0c61d7
/Users/lukas/jiminny/app/app/Http/Controllers/API/V2
/Users/lukas/jiminny/app/app/Jobs/Crm/Hubspot
/Users/lukas/jiminny/app/app/Component/DealInsights
/Users/lukas/jiminny/app/app/Policies
/Users/lukas/jiminny/app/app/Services/Crm/Helpers
/Users/lukas/jiminny/app/app/Jobs/Crm
/Users/lukas/jiminny/app/app/Events/Crm
/Users/lukas/jiminny/app/app/Models
/Users/lukas/jiminny/app/app/Listeners/Teams
/Users/lukas/jiminny/app/app/Jobs/Crm/Salesforce
/Users/lukas/jiminny/app/app
/Users/lukas/jiminny/app/app/Services/Crm/Hubspot/Journal
/Users/lukas/jiminny/app/app/Services/Crm/Hubspot
/Users/lukas/jiminny/app/app/Services/Crm/Hubspot/ServiceTraits
/Users/lukas/jiminny/app/app/Services/Crm/Hubspot/OpportunitySyncStrategy
/Users/lukas/jiminny/app/app/Services/Crm/Hubspot/Webhook
/Users/lukas/jiminny/app/storage/logs
/Users/lukas/jiminny/app
/Users/lukas/jiminny/app/app/Services/Internal
/Users/lukas/jiminny/app/app/Listeners/Transcription
/Users/lukas/jiminny/app/app/Repositories/Crm
/Users/lukas/jiminny/app/tests/Unit/Listeners/Teams
/Users/lukas/jiminny/app/app/Models/Crm
/Users/lukas/Library/Application Support/JetBrains/PhpStorm2026.1/consoles/db/91133dfa-8d71-4e12-bfb8-fec7f1afba8f
/Users/lukas/jiminny/app/app/Observers
/Users/lukas/jiminny/app/app/Services/Mail
/Users/lukas/jiminny/app/app/Console/Commands/Activities
/Users/lukas/jiminny/app/app/Console/Commands/Activities/Migrator
/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Hubspot/ServiceTraits
/Users/lukas/jiminny/app/app/Jobs/User
/Users/lukas/jiminny/app/app/Models/Activity
/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Hubspot/Webhook
/Users/lukas/jiminny/app/app/Component/AiAutomation/Listeners/PendingAnalysis
/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Hubspot
/Users/lukas/jiminny/app/app/Component/ActivitySearch/FilterDefinition/DealInsights
/Users/lukas/jiminny/app/app/Services/Crm/DecorateActivity
/Users/lukas/jiminny/app/app/Component/Activity/Event
/Users/lukas/jiminny/app/app/Component/Sidekick
/Users/lukas/jiminny/app/app/Jobs/Activity/PushSummaryToCrm
/Users/lukas/jiminny/app/app/Listeners/Activities/Conferences
/Users/lukas/jiminny/app/app/Listeners/Activities/Bots
/Users/lukas/jiminny/app/app/Services/RecallAI/Webhooks/Handlers
/Users/lukas/jiminny/app/app/Events/Activities/Bots
/Users/lukas/jiminny/app/app/Component/MeetingBot
/Users/lukas/jiminny/app/app/Services/Activity/RingCentral
/Users/lukas/jiminny/app/app/Http/Controllers/Webhook/Hubspot
/Users/lukas/jiminny/app/app/Services/Activity/Gmail
/Users/lukas/jiminny/app/app/Services/Crm/CrmObjects/ServiceTraits
/Users/lukas/jiminny/app/app/Jobs/Mailbox
/Users/lukas/jiminny/app/app/Console
/Users/lukas/jiminny/app/front-end/src/composables
/Users/lukas/jiminny/app/app/Console/Commands/Calendars
/Users/lukas/jiminny/app/app/Http/Controllers/API
/Users/lukas/jiminny/app/app/Http/Controllers/Internal/WebhookReceiver
/Users/lukas/jiminny/app/app/Services/Crm/IntegrationApp/ServiceTraits
/Users/lukas/jiminny/app/app/Component/Queue
/Users/lukas/jiminny/app/app/Console/Commands/Crm/Hubspot
/Users/lukas/jiminny/app/app/Component/Transcription/Job
/Users/lukas/jiminny/app/tests/Unit/Services/Listeners
/Users/lukas/jiminny/app/app/Services/Crm/Listeners
/Users/lukas/jiminny/app/app/Traits
/Users/lukas/jiminny/app/tests/Unit/Jobs/Crm/Hubspot
/Users/lukas/jiminny/app/tests/Unit/Services/Crm
/Users/lukas/jiminny/app/app/Services/Activity
/Users/lukas/jiminny/app/app/Services/Calendar/Command
/Users/lukas/jiminny/app/.idea/queries
/Users/lukas/jiminny/app/vendor/hubspot/api-client/codegen/Crm
/Users/lukas/jiminny/app/vendor/hubspot
/Users/lukas/jiminny/app/app/Services/Crm/Hubspot/Fields
/Users/lukas/jiminny/app/app/Services/Crm/Copper
/Users/lukas/jiminny/app/app/Services/Crm/Bullhorn
/Users/lukas/jiminny/app/app/Notifications/Channels
/Users/lukas/jiminny/app/tests/Unit
/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Hubspot/Journal
/Users/lukas/jiminny/app/app/Interactions/Settings/Teams
/Users/lukas/jiminny/app/app/Exceptions/Crm
/Users/lukas/jiminny/app/app/Exceptions
/Users/lukas/jiminny/app/vendor/hubspot/hubspot-php/src/Endpoints
/Users/lukas/jiminny/app/config
/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Hubspot/OpportunitySyncStrategy
/Users/lukas/jiminny/app/vendor/laravel/framework/src/Illuminate/Redis/Connections
/Users/lukas/jiminny/app/app/Http/Controllers/Settings/Teams
/Users/lukas/jiminny/app/app/Services/Crm/Hubspot/Webhook/Traits
/Users/lukas/jiminny/app/vendor/laravel/framework/src/Illuminate/Broadcasting
/Users/lukas/jiminny/app/app/Component/FeatureFlags
/Users/lukas/jiminny/app/app/Component/Activity
/Users/lukas/jiminny/app/app/Component/ActivitySearch
/Users/lukas/jiminny/app/tests/Unit/Events/Activities/Crm
/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Hubspot/Pagination
/Users/lukas/jiminny/app/app/Console/Commands/Dev
/Users/lukas/jiminny/app/front-end
/Users/lukas/jiminny/app/app/Component/Prophet
/Users/lukas/jiminny/app/tests/Unit/Services/Crm/IntegrationApp
/Users/lukas/jiminny/app/app/Component/AskAnything
/Users/lukas/jiminny/app/app/Component/AskJiminnyAi/OnDemandLevel/Events
/Users/lukas/jiminny/app/app/Component/AskAnything/Events
/Users/lukas/jiminny/app/app/Component/AskJiminnyAi/DealLevel/Traits
/Users/lukas/jiminny/app/app/Component/ProphetAi
/Users/lukas/Library/Application Support/JetBrains/PhpStorm2026.1/consoles/db/d1e2c340-64e9-49c6-aa9a-196201874532
/Users/lukas/jiminny/app/app/Http/Controllers/API/Page
/Users/lukas/jiminny/app/front-end/src/components/ondemand/ActivityList
/Users/lukas/Library/Application Support/JetBrains/PhpStorm2026.1/consoles/db/5b1549d5-9876-4d9e-9ce3-025f12a83283
/Users/lukas/jiminny/app/app/Contracts/Repositories
/Users/lukas/jiminny/app/app/Http/Controllers/Kiosk
/Users/lukas/jiminny/app/app/Component/AiAutomation/Actions
/Users/lukas/jiminny/app/app/Services/Activity/HubSpot
/Users/lukas/jiminny/app/app/Services/Crm/Pipedrive
/Users/lukas/jiminny/app/app/Jobs/Activity/Import
/Users/lukas/jiminny/app/app/Events/Import
/Users/lukas/jiminny/app/app/Events/Activities/Dialers
/Users/lukas/jiminny/app/tests
/Users/lukas/jiminny/app/app/Events/Activities
/Users/lukas/jiminny/app/tests/Unit/Jobs/Activity/PushSummaryToCrm
/Users/lukas/jiminny/app/app/Console/Commands/Analytics
/Users/lukas/jiminny/app/tests/Unit/Services/Kiosk/AutomatedReports
/Users/lukas/jiminny/app/app/Http/Middleware
/Users/lukas/jiminny/app/app/Http/Controllers/Auth
/Users/lukas/jiminny/app/tests/Unit/Jobs/Crm
/Users/lukas/jiminny/app/app/Services/Crm/IntegrationApp/Api
/Users/lukas/jiminny/app/app/Services/Crm/IntegrationApp/Accessors
/Users/lukas/jiminny/app/app/Services/Crm/Close
/Users/lukas/jiminny/app/app/Services
/Users/lukas/jiminny/app/app/Http/Transformers
/Users/lukas/jiminny/app/tests/Unit/Services/Crm/Pipedrive
/Users/lukas/jiminny/app/app/Listeners/Activities/Crm/Summary
/Users/lukas/jiminny/app/app/Services/Activity/AmazonConnect
/Users/lukas/jiminny/app/app/Models/Participant
/Users/lukas/jiminny/app/app/Events/Activities/Connections
/Users/lukas/jiminny/app/app/Listeners/Activities/Crm
/Users/lukas/jiminny/app/app/Services/Calendar
/Users/lukas/jiminny/app/app/Jobs/DealRisks
/Users/lukas/jiminny/app/front-end/src...
|
NULL
|
|
44801
|
945
|
34
|
2026-04-17T09:06:24.918239+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-17/1776 /Users/lukas/.screenpipe/data/data/2026-04-17/1776416784918_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
FirefoxFileEditViewHistoryBookmarksQ.ProfilesTools FirefoxFileEditViewHistoryBookmarksQ.ProfilesToolsWindowHelpmeet.google.com/xpx-omah-rkn+llian Kyuchukov (Presenting, annotating)ShellEdit© D22391310 [CREDIT_CARD] 815 7D16 5117 56118 419 320 221 122. 2225 2126 4<?phpdeclare(strict_types=1):/ля* Class JininnyDebugConnand* Onackase Jininny|Console|Connands1 usage & Nikola Petianski +1 °class Jtatnnrocouoconano exceods coccanoprotected Ssignature • 'Jiminny:debug":Sopportunity • |Jininny|Models|Opportunity:: find( idSopportunity->stage_id = 100:Sopportunáty-›nane = 'Loren Ipsun";fattributeCastCache: 11edateFornat: nullc0p0000 OrareeadispatchesEvents: 11euusctvoutcanfrelationAutoloadCallback: nultErelationAutolosdcontext: nulrusesUniquelds: falseshiddenavisible: 11sfillable: array:189 = "valuet#guarded:array:1lfuseMicroSeconds: falseaforceDeleting: falseapp/Console/Comnands/J1minnyDebugComnand.php:24root@docker_lamp_1:/hone/jiminnye php artisan|rooteoocker,tonp lspnoner trinny php oroasan2180 - /1 app/Models/OpportunIty,php:187"100* // app/Models/Opportunity-php:197гоотеdоскer_(a»p_:/hoee/) zninhy php arcisan09:23:4610:37:12Systen inforsatton as of Frs. Aor 17 07:37:23N4 C0c058.6%. of 7.57G8IPvs address• Ubuntu Pro delivers the nost conprehensivelsuuizencceLueTo see these additional updates run: apt listESM Apps to receive additional future sSee nttps://ubuntu.com/esm or run: sudo pro stretease '24,04,4 LTS'SYRLIONLC1h 01:14.19 287h Troo7h7n12:06 PM|Daily - ProcessingLộ3Support Daily - in 2h 54 m100% C8 • Fri 17 Apr 12:06:24• Q 8• Fri17 Aor 12:06Tx110:12:05https://laodscape.canooicalhttps:://ubuntu.com/proSysten infornation as of Frs Apr 17 07:22:05UI LULLprosesses"Usase of/58.05 ot 7.576810.28.163.228Users loggedIPv4 address3w00 Us03e* Ubuntu Pro delivers the nost conprehensiveopen source secuhttps://ubuntu.com/avs/proExpanded Security Maintenance for Applicationsapplied innediately.To see these additional updates run: apt list-upgradabteCLUIALY UMMBLCONSee https://ubuntu.com/esm or run: sudo pro stNew releaso '24,84,4 LTS' avallable‹Tlian KyuchukovNikolay NikolovVasil VasilevMihail MihayLukas Kovalik...
|
NULL
|
-8550417436478382273
|
NULL
|
visual_change
|
ocr
|
NULL
|
FirefoxFileEditViewHistoryBookmarksQ.ProfilesTools FirefoxFileEditViewHistoryBookmarksQ.ProfilesToolsWindowHelpmeet.google.com/xpx-omah-rkn+llian Kyuchukov (Presenting, annotating)ShellEdit© D22391310 [CREDIT_CARD] 815 7D16 5117 56118 419 320 221 122. 2225 2126 4<?phpdeclare(strict_types=1):/ля* Class JininnyDebugConnand* Onackase Jininny|Console|Connands1 usage & Nikola Petianski +1 °class Jtatnnrocouoconano exceods coccanoprotected Ssignature • 'Jiminny:debug":Sopportunity • |Jininny|Models|Opportunity:: find( idSopportunity->stage_id = 100:Sopportunáty-›nane = 'Loren Ipsun";fattributeCastCache: 11edateFornat: nullc0p0000 OrareeadispatchesEvents: 11euusctvoutcanfrelationAutoloadCallback: nultErelationAutolosdcontext: nulrusesUniquelds: falseshiddenavisible: 11sfillable: array:189 = "valuet#guarded:array:1lfuseMicroSeconds: falseaforceDeleting: falseapp/Console/Comnands/J1minnyDebugComnand.php:24root@docker_lamp_1:/hone/jiminnye php artisan|rooteoocker,tonp lspnoner trinny php oroasan2180 - /1 app/Models/OpportunIty,php:187"100* // app/Models/Opportunity-php:197гоотеdоскer_(a»p_:/hoee/) zninhy php arcisan09:23:4610:37:12Systen inforsatton as of Frs. Aor 17 07:37:23N4 C0c058.6%. of 7.57G8IPvs address• Ubuntu Pro delivers the nost conprehensivelsuuizencceLueTo see these additional updates run: apt listESM Apps to receive additional future sSee nttps://ubuntu.com/esm or run: sudo pro stretease '24,04,4 LTS'SYRLIONLC1h 01:14.19 287h Troo7h7n12:06 PM|Daily - ProcessingLộ3Support Daily - in 2h 54 m100% C8 • Fri 17 Apr 12:06:24• Q 8• Fri17 Aor 12:06Tx110:12:05https://laodscape.canooicalhttps:://ubuntu.com/proSysten infornation as of Frs Apr 17 07:22:05UI LULLprosesses"Usase of/58.05 ot 7.576810.28.163.228Users loggedIPv4 address3w00 Us03e* Ubuntu Pro delivers the nost conprehensiveopen source secuhttps://ubuntu.com/avs/proExpanded Security Maintenance for Applicationsapplied innediately.To see these additional updates run: apt list-upgradabteCLUIALY UMMBLCONSee https://ubuntu.com/esm or run: sudo pro stNew releaso '24,84,4 LTS' avallable‹Tlian KyuchukovNikolay NikolovVasil VasilevMihail MihayLukas Kovalik...
|
44800
|
|
47965
|
1018
|
42
|
2026-04-17T12:13:38.568573+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-17/1776 /Users/lukas/.screenpipe/data/data/2026-04-17/1776428018568_m2.jpg...
|
Firefox
|
Comparing master...JY-20692-fix-integration-app-to Comparing master...JY-20692-fix-integration-app-token-auth-response-change · jiminny/app — Work...
|
True
|
github.com/jiminny/app/compare/JY-20692-fix-integr github.com/jiminny/app/compare/JY-20692-fix-integration-app-token-auth-response-change?expand=1...
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
[SRD-6793] Les Mills activity types not pulling in - Jira
Workers | Datadog
Developers | HubSpot
Developers | HubSpot
Inbox (1,576) - [EMAIL] - Jiminny Mail
Inbox (1,576) - [EMAIL] - Jiminny Mail
120216 is your HubSpot Log In Code - [EMAIL] - Jiminny Mail
120216 is your HubSpot Log In Code - [EMAIL] - Jiminny Mail
CloudWatch | eu-west-1
CloudWatch | eu-west-1
New Tab
New Tab
Configure SSH access to multiple environment - Engineering - Confluence
Configure SSH access to multiple environment - Engineering - Confluence
fix-cache-for-business-processes by ilian-jiminny · Pull Request #11985 · jiminny/app
fix-cache-for-business-processes by ilian-jiminny · Pull Request #11985 · jiminny/app
Auth Proxy
Auth Proxy
Dashboard · Jiminny · Membrane
Dashboard · Jiminny · Membrane
App "Zoho CRM" · Jiminny · Membrane
App "Zoho CRM" · Jiminny · Membrane
Comparing master...JY-20692-fix-integration-app-[API_KEY] · jiminny/app
Comparing master...JY-20692-fix-integration-app-[API_KEY] · jiminny/app
Close tab
JY-20543 add AJ reports User pilot tracking by LakyLak · Pull Request #11932 · jiminny/app
JY-20543 add AJ reports User pilot tracking by LakyLak · Pull Request #11932 · jiminny/app
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Close bookmarks (⌘B)
Bookmarks
Bookmarks
Close sidebar
Search bookmarks
Skip to content
Skip to content
Open menu
Homepage (g then d)
jiminny
jiminny
app
app
Search or jump to…...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.00234375,"top":0.045138888,"width":0.0296875,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"[SRD-6793] Les Mills activity types not pulling in - Jira","depth":4,"bounds":{"left":0.03203125,"top":0.045138888,"width":0.0296875,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Workers | Datadog","depth":4,"bounds":{"left":0.06171875,"top":0.045138888,"width":0.0296875,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Developers | HubSpot","depth":4,"bounds":{"left":0.0,"top":0.08263889,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Developers | HubSpot","depth":5,"bounds":{"left":0.015625,"top":0.09236111,"width":0.04453125,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Inbox (1,576) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"bounds":{"left":0.0,"top":0.11111111,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Inbox (1,576) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"bounds":{"left":0.015625,"top":0.12083333,"width":0.11445312,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"120216 is your HubSpot Log In Code - integration-account@jiminny.com - Jiminny Mail","depth":4,"bounds":{"left":0.0,"top":0.13958333,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"120216 is your HubSpot Log In Code - integration-account@jiminny.com - Jiminny Mail","depth":5,"bounds":{"left":0.015625,"top":0.14930555,"width":0.17734376,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"CloudWatch | eu-west-1","depth":4,"bounds":{"left":0.0,"top":0.16805555,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"CloudWatch | eu-west-1","depth":5,"bounds":{"left":0.015625,"top":0.17777778,"width":0.048828125,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"New Tab","depth":4,"bounds":{"left":0.0,"top":0.19652778,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"New Tab","depth":5,"bounds":{"left":0.015625,"top":0.20625,"width":0.017578125,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Configure SSH access to multiple environment - Engineering - Confluence","depth":4,"bounds":{"left":0.0,"top":0.225,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Configure SSH access to multiple environment - Engineering - Confluence","depth":5,"bounds":{"left":0.015625,"top":0.23472223,"width":0.1515625,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"fix-cache-for-business-processes by ilian-jiminny · Pull Request #11985 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.2534722,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"fix-cache-for-business-processes by ilian-jiminny · Pull Request #11985 · jiminny/app","depth":5,"bounds":{"left":0.015625,"top":0.26319444,"width":0.17421874,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Auth Proxy","depth":4,"bounds":{"left":0.0,"top":0.28194445,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Auth Proxy","depth":5,"bounds":{"left":0.015625,"top":0.29166666,"width":0.021875,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Dashboard · Jiminny · Membrane","depth":4,"bounds":{"left":0.0,"top":0.31041667,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Dashboard · Jiminny · Membrane","depth":5,"bounds":{"left":0.015625,"top":0.3201389,"width":0.06679688,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"App \"Zoho CRM\" · Jiminny · Membrane","depth":4,"bounds":{"left":0.0,"top":0.33888888,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"App \"Zoho CRM\" · Jiminny · Membrane","depth":5,"bounds":{"left":0.015625,"top":0.34861112,"width":0.0796875,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Comparing master...JY-20692-fix-integration-app-token-auth-response-change · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.3673611,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Comparing master...JY-20692-fix-integration-app-token-auth-response-change · jiminny/app","depth":5,"bounds":{"left":0.015625,"top":0.37708333,"width":0.19179687,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.07890625,"top":0.37361112,"width":0.009375,"height":0.016666668},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"JY-20543 add AJ reports User pilot tracking by LakyLak · Pull Request #11932 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.39583334,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20543 add AJ reports User pilot tracking by LakyLak · Pull Request #11932 · jiminny/app","depth":5,"bounds":{"left":0.015625,"top":0.40555555,"width":0.18710938,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.003125,"top":0.42569444,"width":0.08710937,"height":0.022222223},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.003125,"top":0.97430557,"width":0.0125,"height":0.022222223},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"bounds":{"left":0.01640625,"top":0.97430557,"width":0.0125,"height":0.022222223},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.029296875,"top":0.97430557,"width":0.0125,"height":0.022222223},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.0421875,"top":0.97430557,"width":0.0125,"height":0.022222223},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close bookmarks (⌘B)","depth":6,"bounds":{"left":0.05546875,"top":0.97430557,"width":0.0125,"height":0.022222223},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Bookmarks","depth":5,"bounds":{"left":0.09804688,"top":0.060416665,"width":0.03125,"height":0.013194445},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Bookmarks","depth":6,"bounds":{"left":0.09804688,"top":0.060416665,"width":0.03125,"height":0.013194445},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close sidebar","depth":6,"bounds":{"left":0.20976563,"top":0.05625,"width":0.0125,"height":0.022222223},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXTextField","text":"Search bookmarks","depth":7,"bounds":{"left":0.096875,"top":0.08680555,"width":0.12617187,"height":0.022222223},"help_text":"","role_description":"search text field","subrole":"AXSearchField","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Skip to content","depth":6,"bounds":{"left":0.2296875,"top":0.045138888,"width":0.000390625,"height":0.00069444446},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Skip to content","depth":7,"bounds":{"left":0.2296875,"top":0.046527777,"width":0.003515625,"height":0.18680556},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Open menu","depth":10,"bounds":{"left":0.2359375,"top":0.05625,"width":0.0125,"height":0.022222223},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Homepage (g then d)","depth":9,"bounds":{"left":0.253125,"top":0.05625,"width":0.0125,"height":0.022222223},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"jiminny","depth":12,"bounds":{"left":0.26875,"top":0.05625,"width":0.022265624,"height":0.022222223},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"jiminny","depth":14,"bounds":{"left":0.27109376,"top":0.061805554,"width":0.017578125,"height":0.011805556},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"app","depth":12,"bounds":{"left":0.296875,"top":0.05625,"width":0.02109375,"height":0.022222223},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"app","depth":14,"bounds":{"left":0.29921874,"top":0.061805554,"width":0.01015625,"height":0.011805556},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Search or jump to…","depth":9,"bounds":{"left":0.7789062,"top":0.05625,"width":0.07734375,"height":0.022222223},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
-8550380783368550902
|
-2714158607226099518
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
[SRD-6793] Les Mills activity types not pulling in - Jira
Workers | Datadog
Developers | HubSpot
Developers | HubSpot
Inbox (1,576) - [EMAIL] - Jiminny Mail
Inbox (1,576) - [EMAIL] - Jiminny Mail
120216 is your HubSpot Log In Code - [EMAIL] - Jiminny Mail
120216 is your HubSpot Log In Code - [EMAIL] - Jiminny Mail
CloudWatch | eu-west-1
CloudWatch | eu-west-1
New Tab
New Tab
Configure SSH access to multiple environment - Engineering - Confluence
Configure SSH access to multiple environment - Engineering - Confluence
fix-cache-for-business-processes by ilian-jiminny · Pull Request #11985 · jiminny/app
fix-cache-for-business-processes by ilian-jiminny · Pull Request #11985 · jiminny/app
Auth Proxy
Auth Proxy
Dashboard · Jiminny · Membrane
Dashboard · Jiminny · Membrane
App "Zoho CRM" · Jiminny · Membrane
App "Zoho CRM" · Jiminny · Membrane
Comparing master...JY-20692-fix-integration-app-[API_KEY] · jiminny/app
Comparing master...JY-20692-fix-integration-app-[API_KEY] · jiminny/app
Close tab
JY-20543 add AJ reports User pilot tracking by LakyLak · Pull Request #11932 · jiminny/app
JY-20543 add AJ reports User pilot tracking by LakyLak · Pull Request #11932 · jiminny/app
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Close bookmarks (⌘B)
Bookmarks
Bookmarks
Close sidebar
Search bookmarks
Skip to content
Skip to content
Open menu
Homepage (g then d)
jiminny
jiminny
app
app
Search or jump to…...
|
47963
|
|
53676
|
1160
|
17
|
2026-04-20T08:23:21.687662+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776673401687_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
Analyzing…
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers\Webhook;
use Carbon\Carbon;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Jiminny\Exceptions\ModelNotFoundException;
use Jiminny\Http\Controllers\AbstractController;
use Jiminny\Jobs\AutomatedReports\SendReportJob;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsCallbackService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class ReportController extends AbstractController
{
/**
* Log prefix for all log messages
*/
private const string LOG_PREFIX = '[Report Ready]';
public function __construct(
private readonly AutomatedReportsService $automatedReportsService,
private readonly BusDispatcher $dispatcher,
private readonly LoggerInterface $logger,
private readonly AutomatedReportsCallbackService $callbackService,
) {
}
public function ready(Request $request): JsonResponse
{
$payload = $request->all();
$now = Carbon::now();
$this->logger->info(self::LOG_PREFIX . ' Webhook received', [
'payload' => $payload,
]);
// validate
$reportUuid = $this->callbackService->getResultUuid($payload);
if (empty($reportUuid)) {
return response()->json(['status' => 'error', 'message' => 'Request ID is empty'], status: 400);
}
try {
$report = $this->automatedReportsService->getReportResult($reportUuid);
// validate
if ($this->callbackService->isProcessed($report)) {
$this->logger->warning(self::LOG_PREFIX . ' Report has been already processed', [
'uuid' => $reportUuid,
'currentStatus' => $report->getStatusLabel(),
]);
return response()->json(['status' => 'already_processed']);
}
// always try to get a child podcast cause report configuration cannot be trusted
$reportPodcast = $this->automatedReportsService->findChildResult(
result: $report,
type: AutomatedReportsService::MEDIA_TYPE_PODCAST
);
// update results
$report->update([
'status' => $this->callbackService->getPrimaryStatus($report, $payload),
'response' => $payload,
'generated_at' => $now,
]);
// if a podcast is set, update it
$reportPodcast?->update([
'status' => $this->callbackService->getPodcastStatus($payload),
'response' => $payload,
'generated_at' => $now,
]);
$this->logger->info(self::LOG_PREFIX . ' Report has been processed', [
'uuid' => $reportUuid,
'child_uuid' => $reportPodcast?->getUuid(),
]);
if (! $this->callbackService->isSuccess($payload)) {
$this->logger->warning(self::LOG_PREFIX . ' Error creating report', $payload);
return response()->json(['status' => 'ok']);
}
// If one-off, send the report immediately, if not leave it for the scheduler (automated-reports:send)
if ($report->getReport()->getFrequency() === AutomatedReportsService::FREQUENCY_ONE_OFF) {
// send the primary report
$this->dispatcher->dispatch(new SendReportJob($reportUuid));
// send the podcast report if it set and generated
if ($reportPodcast && $reportPodcast->getStatus() === AutomatedReportResult::STATUS_GENERATED) {
$this->dispatcher->dispatch(new SendReportJob(reportUuid: $reportPodcast->getUuid()));
}
}
// Track Datadog metrics for automated reports
$automatedReport = $report->getReport();
$this->callbackService->pushToDatadog($automatedReport, $report);
if ($reportPodcast) {
$this->callbackService->pushToDatadog($automatedReport, $reportPodcast);
}
} catch (ModelNotFoundException $exception) {
$this->logger->error(self::LOG_PREFIX . ' Report not found', [
'uuid' => $reportUuid,
'error' => $exception->getMessage(),
]);
return response()->json(['status' => 'error', 'message' => 'Report not found'], status: 404);
} catch (Throwable $exception) {
$this->logger->error(self::LOG_PREFIX . ' Failed to update report status', [
'uuid' => $reportUuid,
'error' => $exception->getMessage(),
]);
return response()->json(['status' => 'error', 'message' => 'Failed to update report status'], status: 500);
}
return response()->json(['status' => 'ok']);
}
}
Editor
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci, folder
.cursor, folder
.github
.sonarlint, folder
.vscode, folder
.windsurf, folder
app, sources root
Actions, folder
Component, folder
Configuration, folder
Console, folder
Commands, folder
Activities, folder
Analytics, folder
Calendars, folder
Crm, folder
DealInsights, folder
Dev, folder
Dialers, folder
DTOs, folder
Elasticsearch, folder
EngagementStats, folder
GeckoExport, folder
Livestream, folder
Mailboxes, folder
Migrate, folder
PlaybackThemes, folder
Playbooks, folder
Playlists, folder
Postmark, folder
ProphetAi, folder
Reports, folder
AutomatedReportsCommand.php, class
AutomatedReportsRetentionPolicyCommand.php, class
AutomatedReportsSendCommand.php, class
CreateMockAskJiminnyReportResultCommand.php, class
DeleteReportCommand.php, class
GenerateMarketingReport.php, class
Team.php, class
Usage.php, class
Slack, folder
Teams, folder
Tracks, folder
Transcription, folder
Twilio, folder
Users, folder
Vocabulary, folder
Zoom, folder
CoachingFeedbacksUpdateEsActivities.php, class
Command.php, class
CreateDatabaseUsers.php, class
DatabaseTableCount.php, class
DeleteOldAiCrmNotesCommand.php, class
DeleteS3LeftoversCommand.php, class
DevPostmanCommand.php, final class
DiarizeViaAiParticipantIdentificationCommand.php, class
EncryptTokensCommand.php, class
EngagementStatsRegenerateCommand.php, class
FeatureFlagsHelper.php
FixCrossTenantIssues.php, class
FlushRolesPermissionsCache.php, class
GenerateInternalWebhookToken.php, class
GroupSetDefaultLanguageCommand.php, final class
HelperTruncateCoachingTables.php, class
HubspotJournalPollingCommand.php, class
HubspotWebhookServiceCommand.php, class
ImportRecording.php, class
ImportUsersFromCsvFile.php, final class
IterateUsersCommand.php, abstract class
JiminnyCacheClearCommand.php, class
JiminnyDebugCommand.php, class
JiminnySetEncryptedTokenManagerModeCommand.php, class
JiminnyTokenInfoCommand.php, class
MakeSlackLiveCoachingChatNotesOn.php, class
ManageScimForTeam.php, class
MarkBranchForEnvironmentPipelineCommand.php, class
MuteOrganizerChannel.php, class
PhpApm.php, class
PropagateCoachingFeedbackCreatedAtToSectionFeedbacks.php, class
PurgeConferences.php, class
PurgeSoftDeletedOpportunitiesCommand.php, class
PurgeSyncBatchesCommand.php, class
RecalculateDealRisksCommand.php, class
RemoveDeleteMarkersCommand.php, class
RemoveExpiredNudgesCommand.php, class
RemoveUnusedParticipantSpeechesCommand.php, class
ResetElasticSearch.php, class
RestoreActivityCrmProviderIdCommand.php, class
RestoreActivityTypeCommand.php, class
SeedActivities.php, class
SyncActivity.php, class
TrackImported.php, class
UpdateActivitiesAverageScoreExcludingFeedbacksNotSetVisibleToAll.php, class
WhichWorkerIsWorkingOnWhichJob.php, class
Scheduling, folder
Kernel.php, class
Contracts, folder
Domain, folder
DTO, folder
Emails, folder
Enums, folder
Events, folder
Activities, folder
ActivityProvider, folder
AiAutomation, folder
Audio, folder
Bots, folder
Coaching, folder
Conferences, folder
Connections, folder
Crm, folder
ActivityCancelled.php, class
ActivityCancelledAsNoShow.php, class
ActivityLeadConverted.php, class
ActivityLinkedToCrm.php, class
ActivityLogged.php, class
ActivityScheduled.php, class
AutoLogActivity.php, class
EmailWithCRMObjectsProcessed.php, class
FollowupScheduled.php, class
LeadConverted.php, class
StageChanged.php, class
Dialers, folder
Messages, folder
Participants, folder
Provider, folder
Sessions, folder
Sms, folder
Softphone, folder
Video, folder
ActivityCreated.php, class
ActivityCrmProvidedUpdated.php, class
ActivityExported.php, class
ActivityTypeUpdated.php, class
ActivityUpdated.php, class
ExportTokenGenerated.php, class
NoteTaken.php, class
ProspectUpdated.php, class
SetupIntegrationEvent.php, class
StageUpdated.php, class
StatusUpdated.php, class
TitleUpdated.php, class
TrackImportedEvent.php
TrackReady.php, class
AiAutomation, folder
AiCallScoring, folder
Auth, folder
Calendars, folder
Crm, folder
ElasticSearch, folder
Groups, folder
Import, folder
Nudges, folder
Opportunities, folder
Playbooks, folder
Playlists, folder
Sidekick, folder
Teams, folder
Transcription, folder
Users, folder
Event.php, abstract class
EventDispatcher.php, class
Exceptions, folder
FFMpeg, folder
Formats, folder
Guards, folder
Helpers, folder
Http, folder
AccessTokenProvider, folder
Controllers, folder
API, folder
Auth, folder
CustomerApi, folder
Internal, folder
Kiosk, folder
Settings, folder
Telephony, folder
Webhook, folder
Hubspot, folder
IntegrationAppSubscriptions, folder
ActivityProviderController.php, class
ActivityTranscriptionController.php, class
BaseController.php, class
CalendarController.php, final class
ReportController.php, class
SoftphoneWebhookController.php, final class
AbstractController.php, abstract class
CommentContextInterface.php, interface
ConferencesOptInOutController.php, class
Controller.php, class
ExportController.php, final class
FrontendController.php, class
FrontendControllerTrait.php
GeocodingController.php, final class
HealthCheckController.php, class
LiveCoachController.php, final class
MissingTeamController.php, class
MobileController.php, class
NotificationController.php, class
NotificationProviderController.php, class
PlaybackController.php, final class
PlaylistController.php, final class
PusherController.php, class
SlackController.php, final class
SupportController.php, final class
TeamSetupController.php, class
UserAutomatedReportsController.php, class
WelcomeController.php, class
Middleware
Requests
Resources
Responses
Serializers
Transformers
Kernel.php, class
PlaylistTrackResourceTrait.php
ValidateCrmConnectionRequiredTrait.php
Integrations
Interactions
Jobs
Listeners
Activities
ActivityProvider
Audio
Bots
Coaching
Intercom
Planhat
UserPilot
CreateActivityLoggedEvent.php, class
CreateExportedEvent.php, class
CreatePlaylistCreatedEvent.php, class
CreateSharedEvent.php, class
CreateAvailabilityNotification.php, class
CreateCoachingFeedbackNotification.php, class
CreateCommentNotification.php, class
CreateLiveCoachNotification.php, class
CreateMentionNotification.php, class
CreateMessageCreatedNotification.php, class
CreateNotifyContributorNotification.php, class
CreateRequestNotification.php, class
CreateShareNotification.php, class
Conferences, folder...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"help_text":"Git Branch: master","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Analyzing…","depth":4,"role_description":"text"},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Http\\Controllers\\Webhook;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Contracts\\Bus\\Dispatcher as BusDispatcher;\nuse Illuminate\\Http\\JsonResponse;\nuse Illuminate\\Http\\Request;\nuse Jiminny\\Exceptions\\ModelNotFoundException;\nuse Jiminny\\Http\\Controllers\\AbstractController;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportJob;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsCallbackService;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Psr\\Log\\LoggerInterface;\nuse Throwable;\n\nclass ReportController extends AbstractController\n{\n /**\n * Log prefix for all log messages\n */\n private const string LOG_PREFIX = '[Report Ready]';\n\n public function __construct(\n private readonly AutomatedReportsService $automatedReportsService,\n private readonly BusDispatcher $dispatcher,\n private readonly LoggerInterface $logger,\n private readonly AutomatedReportsCallbackService $callbackService,\n ) {\n }\n\n public function ready(Request $request): JsonResponse\n {\n $payload = $request->all();\n $now = Carbon::now();\n\n $this->logger->info(self::LOG_PREFIX . ' Webhook received', [\n 'payload' => $payload,\n ]);\n\n // validate\n $reportUuid = $this->callbackService->getResultUuid($payload);\n if (empty($reportUuid)) {\n return response()->json(['status' => 'error', 'message' => 'Request ID is empty'], status: 400);\n }\n\n try {\n $report = $this->automatedReportsService->getReportResult($reportUuid);\n\n // validate\n if ($this->callbackService->isProcessed($report)) {\n $this->logger->warning(self::LOG_PREFIX . ' Report has been already processed', [\n 'uuid' => $reportUuid,\n 'currentStatus' => $report->getStatusLabel(),\n ]);\n\n return response()->json(['status' => 'already_processed']);\n }\n\n // always try to get a child podcast cause report configuration cannot be trusted\n $reportPodcast = $this->automatedReportsService->findChildResult(\n result: $report,\n type: AutomatedReportsService::MEDIA_TYPE_PODCAST\n );\n\n // update results\n $report->update([\n 'status' => $this->callbackService->getPrimaryStatus($report, $payload),\n 'response' => $payload,\n 'generated_at' => $now,\n ]);\n // if a podcast is set, update it\n $reportPodcast?->update([\n 'status' => $this->callbackService->getPodcastStatus($payload),\n 'response' => $payload,\n 'generated_at' => $now,\n ]);\n\n $this->logger->info(self::LOG_PREFIX . ' Report has been processed', [\n 'uuid' => $reportUuid,\n 'child_uuid' => $reportPodcast?->getUuid(),\n ]);\n\n if (! $this->callbackService->isSuccess($payload)) {\n $this->logger->warning(self::LOG_PREFIX . ' Error creating report', $payload);\n\n return response()->json(['status' => 'ok']);\n }\n\n // If one-off, send the report immediately, if not leave it for the scheduler (automated-reports:send)\n if ($report->getReport()->getFrequency() === AutomatedReportsService::FREQUENCY_ONE_OFF) {\n // send the primary report\n $this->dispatcher->dispatch(new SendReportJob($reportUuid));\n\n // send the podcast report if it set and generated\n if ($reportPodcast && $reportPodcast->getStatus() === AutomatedReportResult::STATUS_GENERATED) {\n $this->dispatcher->dispatch(new SendReportJob(reportUuid: $reportPodcast->getUuid()));\n }\n }\n\n // Track Datadog metrics for automated reports\n $automatedReport = $report->getReport();\n $this->callbackService->pushToDatadog($automatedReport, $report);\n\n if ($reportPodcast) {\n $this->callbackService->pushToDatadog($automatedReport, $reportPodcast);\n }\n } catch (ModelNotFoundException $exception) {\n $this->logger->error(self::LOG_PREFIX . ' Report not found', [\n 'uuid' => $reportUuid,\n 'error' => $exception->getMessage(),\n ]);\n\n return response()->json(['status' => 'error', 'message' => 'Report not found'], status: 404);\n } catch (Throwable $exception) {\n $this->logger->error(self::LOG_PREFIX . ' Failed to update report status', [\n 'uuid' => $reportUuid,\n 'error' => $exception->getMessage(),\n ]);\n\n return response()->json(['status' => 'error', 'message' => 'Failed to update report status'], status: 500);\n }\n\n return response()->json(['status' => 'ok']);\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Http\\Controllers\\Webhook;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Contracts\\Bus\\Dispatcher as BusDispatcher;\nuse Illuminate\\Http\\JsonResponse;\nuse Illuminate\\Http\\Request;\nuse Jiminny\\Exceptions\\ModelNotFoundException;\nuse Jiminny\\Http\\Controllers\\AbstractController;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportJob;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsCallbackService;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Psr\\Log\\LoggerInterface;\nuse Throwable;\n\nclass ReportController extends AbstractController\n{\n /**\n * Log prefix for all log messages\n */\n private const string LOG_PREFIX = '[Report Ready]';\n\n public function __construct(\n private readonly AutomatedReportsService $automatedReportsService,\n private readonly BusDispatcher $dispatcher,\n private readonly LoggerInterface $logger,\n private readonly AutomatedReportsCallbackService $callbackService,\n ) {\n }\n\n public function ready(Request $request): JsonResponse\n {\n $payload = $request->all();\n $now = Carbon::now();\n\n $this->logger->info(self::LOG_PREFIX . ' Webhook received', [\n 'payload' => $payload,\n ]);\n\n // validate\n $reportUuid = $this->callbackService->getResultUuid($payload);\n if (empty($reportUuid)) {\n return response()->json(['status' => 'error', 'message' => 'Request ID is empty'], status: 400);\n }\n\n try {\n $report = $this->automatedReportsService->getReportResult($reportUuid);\n\n // validate\n if ($this->callbackService->isProcessed($report)) {\n $this->logger->warning(self::LOG_PREFIX . ' Report has been already processed', [\n 'uuid' => $reportUuid,\n 'currentStatus' => $report->getStatusLabel(),\n ]);\n\n return response()->json(['status' => 'already_processed']);\n }\n\n // always try to get a child podcast cause report configuration cannot be trusted\n $reportPodcast = $this->automatedReportsService->findChildResult(\n result: $report,\n type: AutomatedReportsService::MEDIA_TYPE_PODCAST\n );\n\n // update results\n $report->update([\n 'status' => $this->callbackService->getPrimaryStatus($report, $payload),\n 'response' => $payload,\n 'generated_at' => $now,\n ]);\n // if a podcast is set, update it\n $reportPodcast?->update([\n 'status' => $this->callbackService->getPodcastStatus($payload),\n 'response' => $payload,\n 'generated_at' => $now,\n ]);\n\n $this->logger->info(self::LOG_PREFIX . ' Report has been processed', [\n 'uuid' => $reportUuid,\n 'child_uuid' => $reportPodcast?->getUuid(),\n ]);\n\n if (! $this->callbackService->isSuccess($payload)) {\n $this->logger->warning(self::LOG_PREFIX . ' Error creating report', $payload);\n\n return response()->json(['status' => 'ok']);\n }\n\n // If one-off, send the report immediately, if not leave it for the scheduler (automated-reports:send)\n if ($report->getReport()->getFrequency() === AutomatedReportsService::FREQUENCY_ONE_OFF) {\n // send the primary report\n $this->dispatcher->dispatch(new SendReportJob($reportUuid));\n\n // send the podcast report if it set and generated\n if ($reportPodcast && $reportPodcast->getStatus() === AutomatedReportResult::STATUS_GENERATED) {\n $this->dispatcher->dispatch(new SendReportJob(reportUuid: $reportPodcast->getUuid()));\n }\n }\n\n // Track Datadog metrics for automated reports\n $automatedReport = $report->getReport();\n $this->callbackService->pushToDatadog($automatedReport, $report);\n\n if ($reportPodcast) {\n $this->callbackService->pushToDatadog($automatedReport, $reportPodcast);\n }\n } catch (ModelNotFoundException $exception) {\n $this->logger->error(self::LOG_PREFIX . ' Report not found', [\n 'uuid' => $reportUuid,\n 'error' => $exception->getMessage(),\n ]);\n\n return response()->json(['status' => 'error', 'message' => 'Report not found'], status: 404);\n } catch (Throwable $exception) {\n $this->logger->error(self::LOG_PREFIX . ' Failed to update report status', [\n 'uuid' => $reportUuid,\n 'error' => $exception->getMessage(),\n ]);\n\n return response()->json(['status' => 'error', 'message' => 'Failed to update report status'], status: 500);\n }\n\n return response()->json(['status' => 'ok']);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor","depth":4,"role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"app ~/jiminny/app","depth":6,"role_description":"text"},{"role":"AXStaticText","text":".circleci, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".cursor, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".vscode, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".windsurf, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"Actions, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Component, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Configuration, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Console, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Commands, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activities, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Analytics, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Calendars, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Crm, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Dev, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Dialers, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DTOs, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Elasticsearch, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"EngagementStats, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"GeckoExport, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Livestream, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Mailboxes, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Migrate, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackThemes, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Playbooks, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Playlists, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Postmark, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ProphetAi, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Reports, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsRetentionPolicyCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AutomatedReportsSendCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CreateMockAskJiminnyReportResultCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DeleteReportCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"GenerateMarketingReport.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Team.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Usage.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Slack, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Teams, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Tracks, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Transcription, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Twilio, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Users, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Vocabulary, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Zoom, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CoachingFeedbacksUpdateEsActivities.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Command.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CreateDatabaseUsers.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DatabaseTableCount.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DeleteOldAiCrmNotesCommand.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DeleteS3LeftoversCommand.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DevPostmanCommand.php, final class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DiarizeViaAiParticipantIdentificationCommand.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"EncryptTokensCommand.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"EngagementStatsRegenerateCommand.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"FeatureFlagsHelper.php","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"FixCrossTenantIssues.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"FlushRolesPermissionsCache.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"GenerateInternalWebhookToken.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"GroupSetDefaultLanguageCommand.php, final class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"HelperTruncateCoachingTables.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"HubspotJournalPollingCommand.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"HubspotWebhookServiceCommand.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ImportRecording.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ImportUsersFromCsvFile.php, final class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"IterateUsersCommand.php, abstract class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"JiminnyCacheClearCommand.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"JiminnyDebugCommand.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"JiminnySetEncryptedTokenManagerModeCommand.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"JiminnyTokenInfoCommand.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"MakeSlackLiveCoachingChatNotesOn.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ManageScimForTeam.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"MarkBranchForEnvironmentPipelineCommand.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"MuteOrganizerChannel.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PhpApm.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PropagateCoachingFeedbackCreatedAtToSectionFeedbacks.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PurgeConferences.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PurgeSoftDeletedOpportunitiesCommand.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PurgeSyncBatchesCommand.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"RecalculateDealRisksCommand.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"RemoveDeleteMarkersCommand.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"RemoveExpiredNudgesCommand.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"RemoveUnusedParticipantSpeechesCommand.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ResetElasticSearch.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"RestoreActivityCrmProviderIdCommand.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"RestoreActivityTypeCommand.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"SeedActivities.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"SyncActivity.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"TrackImported.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"UpdateActivitiesAverageScoreExcludingFeedbacksNotSetVisibleToAll.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"WhichWorkerIsWorkingOnWhichJob.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Scheduling, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Kernel.php, class","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Contracts, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Domain, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"DTO, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Emails, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Enums, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Events, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Activities, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivityProvider, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Audio, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Bots, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Coaching, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Conferences, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Connections, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Crm, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ActivityCancelled.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ActivityCancelledAsNoShow.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ActivityLeadConverted.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ActivityLinkedToCrm.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ActivityLogged.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ActivityScheduled.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AutoLogActivity.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"EmailWithCRMObjectsProcessed.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"FollowupScheduled.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"LeadConverted.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"StageChanged.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Dialers, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Messages, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Participants, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Provider, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Sessions, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Sms, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Softphone, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Video, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ActivityCreated.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ActivityCrmProvidedUpdated.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ActivityExported.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ActivityTypeUpdated.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ActivityUpdated.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ExportTokenGenerated.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"NoteTaken.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ProspectUpdated.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"SetupIntegrationEvent.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"StageUpdated.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"StatusUpdated.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"TitleUpdated.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"TrackImportedEvent.php","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"TrackReady.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Auth, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Calendars, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Crm, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ElasticSearch, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Groups, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Import, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Nudges, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Opportunities, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Playbooks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Playlists, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Sidekick, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Teams, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Transcription, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Users, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Event.php, abstract class","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"EventDispatcher.php, class","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Exceptions, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"FFMpeg, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Formats, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Guards, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Helpers, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Http, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"AccessTokenProvider, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Controllers, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"API, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Auth, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CustomerApi, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Internal, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Kiosk, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Settings, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Telephony, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Webhook, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Hubspot, folder","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"IntegrationAppSubscriptions, folder","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ActivityProviderController.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ActivityTranscriptionController.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BaseController.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CalendarController.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ReportController.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"SoftphoneWebhookController.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AbstractController.php, abstract class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CommentContextInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ConferencesOptInOutController.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Controller.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ExportController.php, final class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"FrontendController.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"FrontendControllerTrait.php","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"GeocodingController.php, final class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"HealthCheckController.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"LiveCoachController.php, final class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"MissingTeamController.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"MobileController.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"NotificationController.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"NotificationProviderController.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackController.php, final class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PlaylistController.php, final class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"PusherController.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"SlackController.php, final class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"SupportController.php, final class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"TeamSetupController.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"UserAutomatedReportsController.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"WelcomeController.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Middleware","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Requests","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Resources","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Responses","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Serializers","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Transformers","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Kernel.php, class","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"PlaylistTrackResourceTrait.php","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ValidateCrmConnectionRequiredTrait.php","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Integrations","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Interactions","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Jobs","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Listeners","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Activities","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivityProvider","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Audio","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Bots","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Coaching","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Intercom","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Planhat","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"UserPilot","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CreateActivityLoggedEvent.php, class","depth":12,"role_description":"text"},{"role":"AXStaticText","text":"CreateExportedEvent.php, class","depth":12,"role_description":"text"},{"role":"AXStaticText","text":"CreatePlaylistCreatedEvent.php, class","depth":12,"role_description":"text"},{"role":"AXStaticText","text":"CreateSharedEvent.php, class","depth":12,"role_description":"text"},{"role":"AXStaticText","text":"CreateAvailabilityNotification.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CreateCoachingFeedbackNotification.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CreateCommentNotification.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CreateLiveCoachNotification.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CreateMentionNotification.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CreateMessageCreatedNotification.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CreateNotifyContributorNotification.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CreateRequestNotification.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CreateShareNotification.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Conferences, folder","depth":10,"role_description":"text"}]...
|
-8550298393694112458
|
2235692769888950356
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
Analyzing…
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers\Webhook;
use Carbon\Carbon;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Jiminny\Exceptions\ModelNotFoundException;
use Jiminny\Http\Controllers\AbstractController;
use Jiminny\Jobs\AutomatedReports\SendReportJob;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsCallbackService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class ReportController extends AbstractController
{
/**
* Log prefix for all log messages
*/
private const string LOG_PREFIX = '[Report Ready]';
public function __construct(
private readonly AutomatedReportsService $automatedReportsService,
private readonly BusDispatcher $dispatcher,
private readonly LoggerInterface $logger,
private readonly AutomatedReportsCallbackService $callbackService,
) {
}
public function ready(Request $request): JsonResponse
{
$payload = $request->all();
$now = Carbon::now();
$this->logger->info(self::LOG_PREFIX . ' Webhook received', [
'payload' => $payload,
]);
// validate
$reportUuid = $this->callbackService->getResultUuid($payload);
if (empty($reportUuid)) {
return response()->json(['status' => 'error', 'message' => 'Request ID is empty'], status: 400);
}
try {
$report = $this->automatedReportsService->getReportResult($reportUuid);
// validate
if ($this->callbackService->isProcessed($report)) {
$this->logger->warning(self::LOG_PREFIX . ' Report has been already processed', [
'uuid' => $reportUuid,
'currentStatus' => $report->getStatusLabel(),
]);
return response()->json(['status' => 'already_processed']);
}
// always try to get a child podcast cause report configuration cannot be trusted
$reportPodcast = $this->automatedReportsService->findChildResult(
result: $report,
type: AutomatedReportsService::MEDIA_TYPE_PODCAST
);
// update results
$report->update([
'status' => $this->callbackService->getPrimaryStatus($report, $payload),
'response' => $payload,
'generated_at' => $now,
]);
// if a podcast is set, update it
$reportPodcast?->update([
'status' => $this->callbackService->getPodcastStatus($payload),
'response' => $payload,
'generated_at' => $now,
]);
$this->logger->info(self::LOG_PREFIX . ' Report has been processed', [
'uuid' => $reportUuid,
'child_uuid' => $reportPodcast?->getUuid(),
]);
if (! $this->callbackService->isSuccess($payload)) {
$this->logger->warning(self::LOG_PREFIX . ' Error creating report', $payload);
return response()->json(['status' => 'ok']);
}
// If one-off, send the report immediately, if not leave it for the scheduler (automated-reports:send)
if ($report->getReport()->getFrequency() === AutomatedReportsService::FREQUENCY_ONE_OFF) {
// send the primary report
$this->dispatcher->dispatch(new SendReportJob($reportUuid));
// send the podcast report if it set and generated
if ($reportPodcast && $reportPodcast->getStatus() === AutomatedReportResult::STATUS_GENERATED) {
$this->dispatcher->dispatch(new SendReportJob(reportUuid: $reportPodcast->getUuid()));
}
}
// Track Datadog metrics for automated reports
$automatedReport = $report->getReport();
$this->callbackService->pushToDatadog($automatedReport, $report);
if ($reportPodcast) {
$this->callbackService->pushToDatadog($automatedReport, $reportPodcast);
}
} catch (ModelNotFoundException $exception) {
$this->logger->error(self::LOG_PREFIX . ' Report not found', [
'uuid' => $reportUuid,
'error' => $exception->getMessage(),
]);
return response()->json(['status' => 'error', 'message' => 'Report not found'], status: 404);
} catch (Throwable $exception) {
$this->logger->error(self::LOG_PREFIX . ' Failed to update report status', [
'uuid' => $reportUuid,
'error' => $exception->getMessage(),
]);
return response()->json(['status' => 'error', 'message' => 'Failed to update report status'], status: 500);
}
return response()->json(['status' => 'ok']);
}
}
Editor
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci, folder
.cursor, folder
.github
.sonarlint, folder
.vscode, folder
.windsurf, folder
app, sources root
Actions, folder
Component, folder
Configuration, folder
Console, folder
Commands, folder
Activities, folder
Analytics, folder
Calendars, folder
Crm, folder
DealInsights, folder
Dev, folder
Dialers, folder
DTOs, folder
Elasticsearch, folder
EngagementStats, folder
GeckoExport, folder
Livestream, folder
Mailboxes, folder
Migrate, folder
PlaybackThemes, folder
Playbooks, folder
Playlists, folder
Postmark, folder
ProphetAi, folder
Reports, folder
AutomatedReportsCommand.php, class
AutomatedReportsRetentionPolicyCommand.php, class
AutomatedReportsSendCommand.php, class
CreateMockAskJiminnyReportResultCommand.php, class
DeleteReportCommand.php, class
GenerateMarketingReport.php, class
Team.php, class
Usage.php, class
Slack, folder
Teams, folder
Tracks, folder
Transcription, folder
Twilio, folder
Users, folder
Vocabulary, folder
Zoom, folder
CoachingFeedbacksUpdateEsActivities.php, class
Command.php, class
CreateDatabaseUsers.php, class
DatabaseTableCount.php, class
DeleteOldAiCrmNotesCommand.php, class
DeleteS3LeftoversCommand.php, class
DevPostmanCommand.php, final class
DiarizeViaAiParticipantIdentificationCommand.php, class
EncryptTokensCommand.php, class
EngagementStatsRegenerateCommand.php, class
FeatureFlagsHelper.php
FixCrossTenantIssues.php, class
FlushRolesPermissionsCache.php, class
GenerateInternalWebhookToken.php, class
GroupSetDefaultLanguageCommand.php, final class
HelperTruncateCoachingTables.php, class
HubspotJournalPollingCommand.php, class
HubspotWebhookServiceCommand.php, class
ImportRecording.php, class
ImportUsersFromCsvFile.php, final class
IterateUsersCommand.php, abstract class
JiminnyCacheClearCommand.php, class
JiminnyDebugCommand.php, class
JiminnySetEncryptedTokenManagerModeCommand.php, class
JiminnyTokenInfoCommand.php, class
MakeSlackLiveCoachingChatNotesOn.php, class
ManageScimForTeam.php, class
MarkBranchForEnvironmentPipelineCommand.php, class
MuteOrganizerChannel.php, class
PhpApm.php, class
PropagateCoachingFeedbackCreatedAtToSectionFeedbacks.php, class
PurgeConferences.php, class
PurgeSoftDeletedOpportunitiesCommand.php, class
PurgeSyncBatchesCommand.php, class
RecalculateDealRisksCommand.php, class
RemoveDeleteMarkersCommand.php, class
RemoveExpiredNudgesCommand.php, class
RemoveUnusedParticipantSpeechesCommand.php, class
ResetElasticSearch.php, class
RestoreActivityCrmProviderIdCommand.php, class
RestoreActivityTypeCommand.php, class
SeedActivities.php, class
SyncActivity.php, class
TrackImported.php, class
UpdateActivitiesAverageScoreExcludingFeedbacksNotSetVisibleToAll.php, class
WhichWorkerIsWorkingOnWhichJob.php, class
Scheduling, folder
Kernel.php, class
Contracts, folder
Domain, folder
DTO, folder
Emails, folder
Enums, folder
Events, folder
Activities, folder
ActivityProvider, folder
AiAutomation, folder
Audio, folder
Bots, folder
Coaching, folder
Conferences, folder
Connections, folder
Crm, folder
ActivityCancelled.php, class
ActivityCancelledAsNoShow.php, class
ActivityLeadConverted.php, class
ActivityLinkedToCrm.php, class
ActivityLogged.php, class
ActivityScheduled.php, class
AutoLogActivity.php, class
EmailWithCRMObjectsProcessed.php, class
FollowupScheduled.php, class
LeadConverted.php, class
StageChanged.php, class
Dialers, folder
Messages, folder
Participants, folder
Provider, folder
Sessions, folder
Sms, folder
Softphone, folder
Video, folder
ActivityCreated.php, class
ActivityCrmProvidedUpdated.php, class
ActivityExported.php, class
ActivityTypeUpdated.php, class
ActivityUpdated.php, class
ExportTokenGenerated.php, class
NoteTaken.php, class
ProspectUpdated.php, class
SetupIntegrationEvent.php, class
StageUpdated.php, class
StatusUpdated.php, class
TitleUpdated.php, class
TrackImportedEvent.php
TrackReady.php, class
AiAutomation, folder
AiCallScoring, folder
Auth, folder
Calendars, folder
Crm, folder
ElasticSearch, folder
Groups, folder
Import, folder
Nudges, folder
Opportunities, folder
Playbooks, folder
Playlists, folder
Sidekick, folder
Teams, folder
Transcription, folder
Users, folder
Event.php, abstract class
EventDispatcher.php, class
Exceptions, folder
FFMpeg, folder
Formats, folder
Guards, folder
Helpers, folder
Http, folder
AccessTokenProvider, folder
Controllers, folder
API, folder
Auth, folder
CustomerApi, folder
Internal, folder
Kiosk, folder
Settings, folder
Telephony, folder
Webhook, folder
Hubspot, folder
IntegrationAppSubscriptions, folder
ActivityProviderController.php, class
ActivityTranscriptionController.php, class
BaseController.php, class
CalendarController.php, final class
ReportController.php, class
SoftphoneWebhookController.php, final class
AbstractController.php, abstract class
CommentContextInterface.php, interface
ConferencesOptInOutController.php, class
Controller.php, class
ExportController.php, final class
FrontendController.php, class
FrontendControllerTrait.php
GeocodingController.php, final class
HealthCheckController.php, class
LiveCoachController.php, final class
MissingTeamController.php, class
MobileController.php, class
NotificationController.php, class
NotificationProviderController.php, class
PlaybackController.php, final class
PlaylistController.php, final class
PusherController.php, class
SlackController.php, final class
SupportController.php, final class
TeamSetupController.php, class
UserAutomatedReportsController.php, class
WelcomeController.php, class
Middleware
Requests
Resources
Responses
Serializers
Transformers
Kernel.php, class
PlaylistTrackResourceTrait.php
ValidateCrmConnectionRequiredTrait.php
Integrations
Interactions
Jobs
Listeners
Activities
ActivityProvider
Audio
Bots
Coaching
Intercom
Planhat
UserPilot
CreateActivityLoggedEvent.php, class
CreateExportedEvent.php, class
CreatePlaylistCreatedEvent.php, class
CreateSharedEvent.php, class
CreateAvailabilityNotification.php, class
CreateCoachingFeedbackNotification.php, class
CreateCommentNotification.php, class
CreateLiveCoachNotification.php, class
CreateMentionNotification.php, class
CreateMessageCreatedNotification.php, class
CreateNotifyContributorNotification.php, class
CreateRequestNotification.php, class
CreateShareNotification.php, class
Conferences, folder...
|
NULL
|
|
75460
|
1882
|
21
|
2026-04-24T06:28:02.324554+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777012082324_m2.jpg...
|
Firefox
|
(66) Inbox | kovaliklukas@proton.me | Proton Mail (66) Inbox | kovaliklukas@proton.me | Proton Mail — Personal...
|
True
|
mail.proton.me/u/0/inbox
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
DXP4800PLUS-B5F8
5 Signs You Have Successfully Hur DXP4800PLUS-B5F8
5 Signs You Have Successfully Hurt a Narcissist; - [EMAIL] - Gmail
(66) Inbox | [EMAIL] | Proton Mail
Western Digital Red Plus 3.5 6TB 5400rpm 256MB SATA3 (WD60EFPX) от 241,72 € (472,76 лв.) Вътрешен хард диск Western Digital - Pazaruvaj.com
Western Digital Red Plus 3.5 6TB 5400rpm 256MB SATA3 (WD60EFPX) от 241,72 € (472,76 лв.) Вътрешен хард диск Western Digital - Pazaruvaj.com
Today's Deals
Today's Deals
architecture - screenpipe docs
architecture - screenpipe docs
[CircleCI] Workflow failed: jiminny / app on JY-20157-AJ-report-not-send-notification - [EMAIL] - Gmail
[CircleCI] Workflow failed: jiminny / app on JY-20157-AJ-report-not-send-notification - [EMAIL] - Gmail
Screenpipe — Archive
Screenpipe — Archive
SQLite Web: archive.db
SQLite Web: archive.db
SQLite Web: db.sqlite
SQLite Web: db.sqlite
Claude Platform
Claude Platform
rescue time detailed overview - Google Search
rescue time detailed overview - Google Search
Hey @louis030195 Ill check during my - screenpipe.com
Hey @louis030195 Ill check during my - screenpipe.com
GitHub - screenpipe/screenpipe: Run agents that work for you based on what you do. AI finally knows what you are doing · GitHub
GitHub - screenpipe/screenpipe: Run agents that work for you based on what you do. AI finally knows what you are doing · GitHub
Gong Pricing in 2026: Costs, Plans & Is It Worth It?
Gong Pricing in 2026: Costs, Plans & Is It Worth It?
YouTube
YouTube
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Proton Mail
needs your permission to
enable desktop notifications
.
Close this banner
Close this banner
Proton Mail
Proton Mail
Proton Mail
Proton applications
New message
Navigation
Navigation
Inbox 66 unread conversations
Inbox
66
Drafts
Drafts
Sent
Sent
Starred
Starred
More
More
More
More
Views
Views
Views
Views
Newsletters
Newsletters
Folders
Folders
Folders
Folders
Create a new folder
Create a new folder
Manage your folders
Manage your folders
Labels
Labels
Labels
Labels
Create a new label
Create a new label
Manage your labels
Manage your labels
gmail.com 26-04-2025 17:28
gmail.com 26-04-2025 17:28
Important
Important
Show navigation bar
Show navigation bar
Your current storage: 17.75 MB / 500.00 MB
17.75 MB / 500.00 MB
17.75 MB
/
500.00 MB
Proton Mail [IP_ADDRESS]
Proton Mail
[IP_ADDRESS]
Search
Search
Search messages
Upgrade
Upgrade
Attention required Toggle settings
Attention required
Toggle settings
kovaliklukas <[EMAIL]>
Conversation list 66 unread messages
Conversation list
66 unread messages
Select all messages
Select all messages
Inbox
Inbox
More
More
All
All
Sort conversations
Previous page
Previous page
1/4
1
/
4
Next page
Next page
Upgrade to level up your privacy.
Upgrade
Close
Close
Unread email
Streamlit team
Thursday, April 23rd, 2026 at 7:58 PM
🔥 Check out Release 1.56 + new pivot table component, and join the virtual meetup!
🔥 Check out Release 1.56 + new pivot table component, and join the virtual meetup!
Unread email
Team Vivaldi
Wednesday, April 22nd, 2026 at 7:29 AM
Find anything in Vivaldi instantly.
Find anything in Vivaldi instantly.
Unread email
Team Vivaldi
Tuesday, April 21st, 2026 at 7:06 AM
Workspaces: Because 50+ tabs is normal
Workspaces: Because 50+ tabs is normal
Unread email
Team Vivaldi
Monday, April 20th, 2026 at 6:53 AM
Your feedback drives us
Your feedback drives us
Unread email
Team Vivaldi
Sunday, April 19th, 2026 at 6:37 AM
Advanced Tab Mastery
Advanced Tab Mastery
Unread email
Matúš Kostolný Denník N
Saturday, April 18th, 2026 at 12:14 PM
Jovinečko na polícii: Kvôli Pellegrinimu mi volala Plačková. Núkala okolo 30-tisíc eur
Jovinečko na polícii: Kvôli Pellegrinimu mi volala Plačková. Núkala okolo 30-tisíc eur
Unread email
Team Vivaldi
Saturday, April 18th, 2026 at 6:55 AM
Take Vivaldi Everywhere with Sync
Take Vivaldi Everywhere with Sync
Proton
Official
Saturday, April 18th, 2026 at 5:21 AM
Born Private, Proton Workspace, private video calling + more
Born Private, Proton Workspace, private video calling + more
Unread email
Team Vivaldi
Friday, April 17th, 2026 at 5:45 AM
The Sidebar: Your Secret Productivity Weapon
The Sidebar: Your Secret Productivity Weapon
Unread email
Team Vivaldi
Thursday, April 16th, 2026 at 6:48 AM
What you know about tabs is wrong, but we’ve got you covered!
What you know about tabs is wrong, but we’ve got you covered!
Unread email
Team Vivaldi
Wednesday, April 15th, 2026 at 6:57 AM
Welcome to Vivaldi!
Welcome to Vivaldi!
Unread email
Team Vivaldi
Monday, April 13th, 2026 at 6:42 AM
2 messages in conversation
Almost there! Complete your Vivaldi account setup
Almost there! Complete your Vivaldi account setup
Unread email
Ancestry
Friday, April 10th, 2026 at 5:20 PM
2 messages in conversation
Have you tried Archives yet?
Have you tried Archives yet?
Unread email
Matúš Kostolný Denník N
Friday, April 10th, 2026 at 11:00 AM
Fico si v tichosti zväčšil byt v rezidencii pod Slavínom
Fico si v tichosti zväčšil byt v rezidencii pod Slavínom
Disney+
Friday, April 10th, 2026 at 10:22 AM
5 messages in conversation
Complete your Disney+ Subscription
Complete your Disney+ Subscription
Team Vivaldi
Wednesday, April 8th, 2026 at 8:52 PM
Vivaldi Browser
Vivaldi Browser
Has 13 attachments (29.19 KB)
attachment-7.jpeg
attachment-1.png
+
8
Unread email
Ancestry
Tuesday, April 7th, 2026 at 5:20 PM
Meet Archives: The budget-friendly sister site to Ancestry
Meet Archives: The budget-friendly sister site to Ancestry
Unread email
Matúš Kostolný Denník N
Thursday, April 2nd, 2026 at 12:52 PM
Príbeh takáčovcov z podsvetia: výpalné, výbuchy a krytie zhora. A teraz ich využili proti Čurillovi
Príbeh takáčovcov z podsvetia: výpalné, výbuchy a krytie zhora. A teraz ich využili proti Čurillovi
MyHeritage SuperSearch Alerts
Vlcsak - 6 нови резултата
Vlcsak - 6 нови резултата
Mark as unread
Mark as unread
Move to trash
Move to trash
Move to archive
Move to archive
Snooze
Snooze
Star conversation
Star conversation
Unread email
Proton
Official
Tuesday, March 31st, 2026 at 7:22 AM
Last chance to save 40% on Mail Plus
Last chance to save 40% on Mail Plus
SmartyMe
Monday, March 30th, 2026 at 2:11 AM
Activated for [EMAIL]
Activated for [EMAIL]
Unread email
Matúš Kostolný Denník N
Friday, March 27th, 2026 at 11:15 AM
Kaštiele skupuje firma zo schátranej chaty. Stopy vedú k Pellegriniho kamarátovi, ktorého pozná Fico aj Bödör
Kaštiele skupuje firma zo schátranej chaty. Stopy vedú k Pellegriniho kamarátovi, ktorého pozná Fico aj Bödör
Unread email
SmartyMe
Friday, March 27th, 2026 at 12:21 AM
Before the season moves on
Before the season moves on
Unread email
SmartyMe
Thursday, March 26th, 2026 at 2:49 AM
Your plan is almost ready — don't miss it!
Your plan is almost ready — don't miss it!
Proton
Official
Wednesday, March 25th, 2026 at 12:38 PM
Spring privacy refresh: Get 40% off Mail Plus
Spring privacy refresh: Get 40% off Mail Plus
Unread email
SmartyMe
Tuesday, March 24th, 2026 at 9:10 AM
2 messages in conversation
Spring is a good time to upgrade
Spring is a good time to upgrade
Matúš Kostolný Denník N
Friday, March 20th, 2026 at 10:09 AM
Viac voľna, zdravší rozpočet či nové byty. Čo všetko Fico sľuboval a dnes vraví, že mu to je ukradnuté
Viac voľna, zdravší rozpočet či nové byty. Čo všetko Fico sľuboval a dnes vraví, že mu to je ukradnuté
Streamlit team
Thursday, March 19th, 2026 at 6:52 PM
🎈 1 million devs, dynamic containers, new components gallery, + more!
🎈 1 million devs, dynamic containers, new components gallery, + more!
Ancestry
Wednesday, March 18th, 2026 at 11:05 PM
2 messages in conversation
Madalyn, check out these new and updated collections
Madalyn, check out these new and updated collections
Kate from Relatio
Wednesday, March 18th, 2026 at 9:10 PM
Last Chance: 75% Off Men's Health Plan Ends Soon!
Last Chance: 75% Off Men's Health Plan Ends Soon!
Kate from Relatio
Tuesday, March 17th, 2026 at 9:10 PM
Special discount of 75% for you!
Special discount of 75% for you!
MyHeritage SuperSearch Alerts
Tuesday, March 17th, 2026 at 9:48 AM
Vltsak - 4 new results
Vltsak - 4 new results
SmartyMe
Monday, March 16th, 2026 at 11:27 PM
A Little Luck, A Lot Smarter 🍀
A Little Luck, A Lot Smarter 🍀
Kate from Relatio
Monday, March 16th, 2026 at 9:10 PM
Unprecedented 75% OFF for You Today
Unprecedented 75% OFF for You Today
Unread email
NuPhy
Monday, March 16th, 2026 at 11:13 AM
Confirm you want to receive email marketing
Confirm you want to receive email marketing
Unread email
NuPhy
Monday, March 16th, 2026 at 11:07 AM
Customer account confirmation
Customer account confirmation
NuPhy
Monday, March 16th, 2026 at 11:07 AM
Customer account activation
Customer account activation
NuPhy
Monday, March 16th, 2026 at 11:05 AM
2 messages in conversation
Verify your email with NuPhy
Verify your email with NuPhy
NuPhy
Monday, March 16th, 2026 at 11:04 AM
Exclusive: Get 10% off for every friend you refer
Exclusive: Get 10% off for every friend you refer
Kate from Relatio
Sunday, March 15th, 2026 at 10:10 PM
Oops, We Made a Mistake..
Oops, We Made a Mistake..
Kate from Relatio
Sunday, March 15th, 2026 at 10:00 PM
Your Quiz Results Are Ready: We’ve reserved a private offer for you
Your Quiz Results Are Ready: We’ve reserved a private offer for you
Unread email
Sam Morgan
Sunday, March 15th, 2026 at 12:00 AM
Reminder - confirm your email address
Reminder - confirm your email address
Unread email
Kate from Relatio
Saturday, March 14th, 2026 at 11:25 PM
Confirm your email address
Confirm your email address
Unread email
SmartyMe
Saturday, March 14th, 2026 at 6:45 PM
10 minutes. That’s enough.
10 minutes. That’s enough.
Unread email
SmartyMe
Friday, March 13th, 2026 at 6:43 PM
Your plan price is reserved
Your plan price is reserved
Unread email
Matúš Kostolný Denník N
Friday, March 13th, 2026 at 11:29 AM
Nerozumieme, prečo financujete Putinovu vojnu. Reportáž z mesta, kde sa zastavila ruská ropa
Nerozumieme, prečo financujete Putinovu vojnu. Reportáž z mesta, kde sa zastavila ruská ropa
SmartyMe
Thursday, March 12th, 2026 at 1:53 PM
Your updated plan is ready
Your updated plan is ready
Unread email
SmartyMe
Wednesday, March 11th, 2026 at 9:17 AM
Your plan is about to be deleted
Your plan is about to be deleted
Unread email
SmartyMe ★
Tuesday, March 10th, 2026 at 2:18 AM
Your personalized growth plan is almost ready
Your personalized growth plan is almost ready
Unread email
Matúš Kostolný Denník N
Friday, March 6th, 2026 at 10:13 AM
Naďa Marcinková je kľúčovou svedkyňou vyšetrovania Epsteina. Podľa FBI ju sexuálne vykorisťoval
Naďa Marcinková je kľúčovou svedkyňou vyšetrovania Epsteina. Podľa FBI ju sexuálne vykorisťoval
Go to previous page
Go to previous page
Go to page 1
Go to page 1
Go to page 2
Go to page 2
Go to page 3
Go to page 3
Go to page 4
Go to page 4
Go to next page
Go to next page
Show side panel
Show side panel
Toggle Contacts app
Toggle Contacts app
Toggle Calendar app
Toggle Calendar app
Toggle security center app
Toggle security center app
Proton VPN Toggle VPN dashboard
Toggle VPN dashboard
Toggle referral
Toggle referral
New
Share your availability with booking pages in Proton Calendar, and host secure meetings with Proton Meet.
Close
Close...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"DXP4800PLUS-B5F8","depth":4,"bounds":{"left":0.27210772,"top":1.0,"width":0.03673537,"height":-0.051875472},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"5 Signs You Have Successfully Hurt a Narcissist; - kovaliklukas@gmail.com - Gmail","depth":4,"bounds":{"left":0.30884308,"top":1.0,"width":0.03656915,"height":-0.051875472},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"(66) Inbox | kovaliklukas@proton.me | Proton Mail","depth":4,"bounds":{"left":0.34541222,"top":1.0,"width":0.03673537,"height":-0.051875472},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXRadioButton","text":"Western Digital Red Plus 3.5 6TB 5400rpm 256MB SATA3 (WD60EFPX) от 241,72 € (472,76 лв.) Вътрешен хард диск Western Digital - Pazaruvaj.com","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.113696806,"height":-0.094972014},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Western Digital Red Plus 3.5 6TB 5400rpm 256MB SATA3 (WD60EFPX) от 241,72 € (472,76 лв.) Вътрешен хард диск Western Digital - Pazaruvaj.com","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Today's Deals","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Today's Deals","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"architecture - screenpipe docs","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"architecture - screenpipe docs","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[CircleCI] Workflow failed: jiminny / app on JY-20157-AJ-report-not-send-notification - kovaliklukas@gmail.com - Gmail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[CircleCI] Workflow failed: jiminny / app on JY-20157-AJ-report-not-send-notification - kovaliklukas@gmail.com - Gmail","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Screenpipe — Archive","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Screenpipe — Archive","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"SQLite Web: archive.db","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"SQLite Web: archive.db","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"SQLite Web: db.sqlite","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"SQLite Web: db.sqlite","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Claude Platform","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Claude Platform","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"rescue time detailed overview - Google Search","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"rescue time detailed overview - Google Search","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Hey @louis030195 Ill check during my - screenpipe.com","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Hey @louis030195 Ill check during my - screenpipe.com","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"GitHub - screenpipe/screenpipe: Run agents that work for you based on what you do. AI finally knows what you are doing · GitHub","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"GitHub - screenpipe/screenpipe: Run agents that work for you based on what you do. AI finally knows what you are doing · GitHub","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Gong Pricing in 2026: Costs, Plans & Is It Worth It?","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gong Pricing in 2026: Costs, Plans & Is It Worth It?","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"YouTube","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"YouTube","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bitwarden","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Proton Mail","depth":11,"bounds":{"left":0.48470744,"top":1.0,"width":0.025598405,"height":-0.059457302},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"needs your permission to","depth":11,"bounds":{"left":0.5103058,"top":1.0,"width":0.059840426,"height":-0.059457302},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"enable desktop notifications","depth":11,"bounds":{"left":0.57014626,"top":1.0,"width":0.06466091,"height":-0.058260202},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":".","depth":11,"bounds":{"left":0.63480717,"top":1.0,"width":0.0014960107,"height":-0.059457302},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close this banner","depth":10,"bounds":{"left":0.73703456,"top":1.0,"width":0.011968086,"height":-0.051875472},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Close this banner","depth":12,"bounds":{"left":0.74268615,"top":1.0,"width":0.016123671,"height":-0.052274585},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Proton Mail","depth":10,"bounds":{"left":0.38364363,"top":1.0,"width":0.0003324468,"height":-0.07980847},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Proton Mail","depth":11,"bounds":{"left":0.38364363,"top":1.0,"width":0.034242023,"height":-0.082601786},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Proton Mail","depth":10,"bounds":{"left":0.38929522,"top":1.0,"width":0.059840426,"height":-0.090183616},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Proton applications","depth":11,"bounds":{"left":0.44980052,"top":1.0,"width":0.011968086,"height":-0.090183616},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New message","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Navigation","depth":12,"bounds":{"left":0.38364363,"top":1.0,"width":0.0003324468,"height":-0.07980847},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Navigation","depth":13,"bounds":{"left":0.38364363,"top":1.0,"width":0.043882977,"height":-0.08220267},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Inbox 66 unread conversations","depth":15,"help_text":"Inbox","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Inbox","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"66","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Drafts","depth":15,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Drafts","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Sent","depth":15,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Sent","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Starred","depth":15,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Starred","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"More","depth":14,"bounds":{"left":0.38763297,"top":1.0,"width":0.0003324468,"height":-0.07980847},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"More","depth":15,"bounds":{"left":0.38763297,"top":1.0,"width":0.011469414,"height":-0.08100557},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"More","depth":14,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Views","depth":15,"bounds":{"left":0.38763297,"top":1.0,"width":0.0003324468,"height":-0.07980847},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Views","depth":16,"bounds":{"left":0.38763297,"top":1.0,"width":0.013297873,"height":-0.08100557},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Views","depth":15,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"Views","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Newsletters","depth":15,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Newsletters","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Folders","depth":15,"bounds":{"left":0.38763297,"top":1.0,"width":0.0003324468,"height":-0.07980847},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Folders","depth":16,"bounds":{"left":0.38763297,"top":1.0,"width":0.016289894,"height":-0.08100557},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Folders","depth":15,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Folders","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Create a new folder","depth":15,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Create a new folder","depth":17,"bounds":{"left":0.44414893,"top":1.0,"width":0.01462766,"height":-0.08100557},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Manage your folders","depth":15,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Manage your folders","depth":17,"bounds":{"left":0.45478722,"top":1.0,"width":0.017785905,"height":-0.08100557},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Labels","depth":15,"bounds":{"left":0.38763297,"top":1.0,"width":0.0003324468,"height":-0.07980847},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Labels","depth":16,"bounds":{"left":0.38763297,"top":1.0,"width":0.014461436,"height":-0.08100557},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Labels","depth":15,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"Labels","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Create a new label","depth":15,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Create a new label","depth":17,"bounds":{"left":0.44414893,"top":1.0,"width":0.01462766,"height":-0.08100557},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Manage your labels","depth":15,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Manage your labels","depth":17,"bounds":{"left":0.45478722,"top":1.0,"width":0.017785905,"height":-0.08100557},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"gmail.com 26-04-2025 17:28","depth":15,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"gmail.com 26-04-2025 17:28","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Important","depth":15,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Important","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Show navigation bar","depth":13,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Show navigation bar","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Your current storage: 17.75 MB / 500.00 MB","depth":11,"bounds":{"left":0.38763297,"top":1.0,"width":0.017952127,"height":-0.08100557},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"17.75 MB / 500.00 MB","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"17.75 MB","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"/","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"500.00 MB","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Proton Mail 5.0.111.4","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Proton Mail","depth":13,"bounds":{"left":0.45013297,"top":1.0,"width":0.010305851,"height":-0.08060658},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"5.0.111.4","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Search","depth":12,"bounds":{"left":0.47456783,"top":1.0,"width":0.00930851,"height":-0.09337592},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Search","depth":14,"bounds":{"left":0.4788896,"top":1.0,"width":0.015458777,"height":-0.0889864},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXTextField","text":"Search messages","depth":12,"bounds":{"left":0.48271278,"top":1.0,"width":0.108211435,"height":-0.0909816},"help_text":"","role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Upgrade","depth":13,"bounds":{"left":0.68234706,"top":1.0,"width":0.0390625,"height":-0.090183616},"help_text":"Go to subscription plans","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Upgrade","depth":15,"bounds":{"left":0.69697475,"top":1.0,"width":0.019115692,"height":-0.09776533},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Attention required Toggle settings","depth":13,"bounds":{"left":0.72273934,"top":1.0,"width":0.011968086,"height":-0.090183616},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Attention required","depth":16,"bounds":{"left":0.73204786,"top":1.0,"width":0.019780586,"height":-0.09297681},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Toggle settings","depth":15,"bounds":{"left":0.72855717,"top":1.0,"width":0.017453458,"height":-0.091380715},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"kovaliklukas <kovaliklukas@proton.me>","depth":13,"bounds":{"left":0.73636967,"top":1.0,"width":0.00930851,"height":-0.09337592},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation list 66 unread messages","depth":15,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation list","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"66 unread messages","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Select all messages","depth":19,"help_text":"","role_description":"checkbox","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Select all messages","depth":20,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Inbox","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Inbox","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"More","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"All","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"All","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Sort conversations","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Previous page","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Previous page","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"1/4","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"/","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"4","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Next page","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Next page","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Upgrade to level up your privacy.","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Upgrade","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Close","depth":15,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Close","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"bounds":{"left":0.48537233,"top":0.39784518,"width":0.016456118,"height":0.029928172},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Streamlit team","depth":19,"bounds":{"left":0.48603722,"top":0.40742218,"width":0.032579787,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Thursday, April 23rd, 2026 at 7:58 PM","depth":18,"bounds":{"left":0.7084442,"top":0.39864326,"width":0.021775266,"height":0.10973663},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"🔥 Check out Release 1.56 + new pivot table component, and join the virtual meetup!","depth":17,"bounds":{"left":0.48603722,"top":0.42218676,"width":0.18833111,"height":0.015961692},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"🔥 Check out Release 1.56 + new pivot table component, and join the virtual meetup!","depth":18,"bounds":{"left":0.48603722,"top":0.4233839,"width":0.18833111,"height":0.014764565},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"bounds":{"left":0.48537233,"top":0.44972068,"width":0.016456118,"height":0.029928172},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Vivaldi","depth":19,"bounds":{"left":0.48603722,"top":0.4592977,"width":0.028922873,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Wednesday, April 22nd, 2026 at 7:29 AM","depth":18,"bounds":{"left":0.70495343,"top":0.45051876,"width":0.026928192,"height":0.10973663},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Find anything in Vivaldi instantly.","depth":17,"bounds":{"left":0.48603722,"top":0.47406226,"width":0.07430186,"height":0.015961692},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Find anything in Vivaldi instantly.","depth":18,"bounds":{"left":0.48603722,"top":0.47525936,"width":0.07430186,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"bounds":{"left":0.48537233,"top":0.50159615,"width":0.016456118,"height":0.029928172},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Vivaldi","depth":19,"bounds":{"left":0.48603722,"top":0.5111732,"width":0.028922873,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Tuesday, April 21st, 2026 at 7:06 AM","depth":18,"bounds":{"left":0.71127,"top":0.50239426,"width":0.019780586,"height":0.10973663},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Workspaces: Because 50+ tabs is normal","depth":17,"bounds":{"left":0.48603722,"top":0.52593774,"width":0.0930851,"height":0.015961692},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Workspaces: Because 50+ tabs is normal","depth":18,"bounds":{"left":0.48603722,"top":0.5271349,"width":0.0930851,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"bounds":{"left":0.48537233,"top":0.5534717,"width":0.016456118,"height":0.029928172},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Vivaldi","depth":19,"bounds":{"left":0.48603722,"top":0.56304866,"width":0.028922873,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Monday, April 20th, 2026 at 6:53 AM","depth":18,"bounds":{"left":0.71210104,"top":0.55426973,"width":0.018949468,"height":0.10973663},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Your feedback drives us","depth":17,"bounds":{"left":0.48603722,"top":0.57781327,"width":0.05435505,"height":0.015961692},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Your feedback drives us","depth":18,"bounds":{"left":0.48603722,"top":0.57901037,"width":0.05435505,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"bounds":{"left":0.48537233,"top":0.60534716,"width":0.016456118,"height":0.029928172},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Vivaldi","depth":19,"bounds":{"left":0.48603722,"top":0.6149242,"width":0.028922873,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sunday, April 19th, 2026 at 6:37 AM","depth":18,"bounds":{"left":0.71326464,"top":0.60614526,"width":0.01761968,"height":0.10973663},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Advanced Tab Mastery","depth":17,"bounds":{"left":0.48603722,"top":0.62968874,"width":0.052027926,"height":0.015961692},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Advanced Tab Mastery","depth":18,"bounds":{"left":0.48603722,"top":0.6308859,"width":0.052027926,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"bounds":{"left":0.48537233,"top":0.6572227,"width":0.016456118,"height":0.029928172},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Matúš Kostolný Denník N","depth":19,"bounds":{"left":0.48603722,"top":0.66679966,"width":0.056349736,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Saturday, April 18th, 2026 at 12:14 PM","depth":18,"bounds":{"left":0.7155917,"top":0.65802073,"width":0.020611702,"height":0.10973663},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Jovinečko na polícii: Kvôli Pellegrinimu mi volala Plačková. Núkala okolo 30-tisíc eur","depth":17,"bounds":{"left":0.48603722,"top":0.6815643,"width":0.18849733,"height":0.015961692},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jovinečko na polícii: Kvôli Pellegrinimu mi volala Plačková. Núkala okolo 30-tisíc eur","depth":18,"bounds":{"left":0.48603722,"top":0.6827614,"width":0.18849733,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"bounds":{"left":0.48537233,"top":0.70909816,"width":0.016456118,"height":0.029928172},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Vivaldi","depth":19,"bounds":{"left":0.48603722,"top":0.7186752,"width":0.028922873,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Saturday, April 18th, 2026 at 6:55 AM","depth":18,"bounds":{"left":0.7155917,"top":0.70989627,"width":0.020611702,"height":0.10973663},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Take Vivaldi Everywhere with Sync","depth":17,"bounds":{"left":0.48603722,"top":0.73343974,"width":0.078457445,"height":0.015961692},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Take Vivaldi Everywhere with Sync","depth":18,"bounds":{"left":0.48603722,"top":0.73463684,"width":0.078457445,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Proton","depth":20,"bounds":{"left":0.48603722,"top":0.77055067,"width":0.014461436,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Official","depth":21,"bounds":{"left":0.5049867,"top":0.7717478,"width":0.012632979,"height":0.011173184},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Saturday, April 18th, 2026 at 5:21 AM","depth":18,"bounds":{"left":0.71575797,"top":0.76177174,"width":0.020777926,"height":0.10973663},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Born Private, Proton Workspace, private video calling + more","depth":17,"bounds":{"left":0.48603722,"top":0.7853152,"width":0.1341423,"height":0.015961692},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Born Private, Proton Workspace, private video calling + more","depth":18,"bounds":{"left":0.48603722,"top":0.7865124,"width":0.1341423,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"bounds":{"left":0.48537233,"top":0.81284916,"width":0.016456118,"height":0.029928172},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Vivaldi","depth":19,"bounds":{"left":0.48603722,"top":0.8224262,"width":0.028922873,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Friday, April 17th, 2026 at 5:45 AM","depth":18,"bounds":{"left":0.71575797,"top":0.8136473,"width":0.01462766,"height":0.10973663},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"The Sidebar: Your Secret Productivity Weapon","depth":17,"bounds":{"left":0.48603722,"top":0.83719075,"width":0.104222074,"height":0.015961692},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Sidebar: Your Secret Productivity Weapon","depth":18,"bounds":{"left":0.48603722,"top":0.83838785,"width":0.104222074,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"bounds":{"left":0.48537233,"top":0.86472464,"width":0.016456118,"height":0.029928172},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Vivaldi","depth":19,"bounds":{"left":0.48603722,"top":0.8743017,"width":0.028922873,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Thursday, April 16th, 2026 at 6:48 AM","depth":18,"bounds":{"left":0.7155917,"top":0.86552274,"width":0.021775266,"height":0.10973663},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"What you know about tabs is wrong, but we’ve got you covered!","depth":17,"bounds":{"left":0.48603722,"top":0.8890662,"width":0.14361702,"height":0.015961692},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"What you know about tabs is wrong, but we’ve got you covered!","depth":18,"bounds":{"left":0.48603722,"top":0.8902634,"width":0.14361702,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"bounds":{"left":0.48537233,"top":0.91660017,"width":0.016456118,"height":0.029928172},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Vivaldi","depth":19,"bounds":{"left":0.48603722,"top":0.9261772,"width":0.028922873,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Wednesday, April 15th, 2026 at 6:57 AM","depth":18,"bounds":{"left":0.7155917,"top":0.9173983,"width":0.026928192,"height":0.082601726},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Welcome to Vivaldi!","depth":17,"bounds":{"left":0.48603722,"top":0.94094175,"width":0.04454787,"height":0.015961692},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Welcome to Vivaldi!","depth":18,"bounds":{"left":0.48603722,"top":0.94213885,"width":0.04454787,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"bounds":{"left":0.48537233,"top":0.96847564,"width":0.016456118,"height":0.029928172},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Vivaldi","depth":19,"bounds":{"left":0.48603722,"top":0.9780527,"width":0.028922873,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Monday, April 13th, 2026 at 6:42 AM","depth":18,"bounds":{"left":0.7155917,"top":0.96927375,"width":0.018783245,"height":0.030726254},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2 messages in conversation","depth":18,"bounds":{"left":0.48570478,"top":0.96927375,"width":0.02925532,"height":0.030726254},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Almost there! Complete your Vivaldi account setup","depth":17,"bounds":{"left":0.49368352,"top":0.9928172,"width":0.11419548,"height":0.007182777},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Almost there! Complete your Vivaldi account setup","depth":18,"bounds":{"left":0.49368352,"top":0.9940144,"width":0.11419548,"height":0.0059856176},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"bounds":{"left":0.48537233,"top":1.0,"width":0.016456118,"height":-0.020351171},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Ancestry","depth":19,"bounds":{"left":0.48603722,"top":1.0,"width":0.020611702,"height":-0.029928207},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Friday, April 10th, 2026 at 5:20 PM","depth":18,"bounds":{"left":0.71542555,"top":1.0,"width":0.01462766,"height":-0.021149278},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2 messages in conversation","depth":18,"bounds":{"left":0.48570478,"top":1.0,"width":0.02925532,"height":-0.021149278},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Have you tried Archives yet?","depth":17,"bounds":{"left":0.49368352,"top":1.0,"width":0.064328454,"height":-0.044692755},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Have you tried Archives yet?","depth":18,"bounds":{"left":0.49368352,"top":1.0,"width":0.064328454,"height":-0.045889854},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"bounds":{"left":0.48537233,"top":1.0,"width":0.016456118,"height":-0.07222664},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Matúš Kostolný Denník N","depth":19,"bounds":{"left":0.48603722,"top":1.0,"width":0.056349736,"height":-0.08180368},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Friday, April 10th, 2026 at 11:00 AM","depth":18,"bounds":{"left":0.71542555,"top":1.0,"width":0.01462766,"height":-0.07302475},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Fico si v tichosti zväčšil byt v rezidencii pod Slavínom","depth":17,"bounds":{"left":0.48603722,"top":1.0,"width":0.12017952,"height":-0.09656823},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Fico si v tichosti zväčšil byt v rezidencii pod Slavínom","depth":18,"bounds":{"left":0.48603722,"top":1.0,"width":0.12017952,"height":-0.09776533},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Disney+","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Friday, April 10th, 2026 at 10:22 AM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"5 messages in conversation","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Complete your Disney+ Subscription","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Complete your Disney+ Subscription","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Vivaldi","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Wednesday, April 8th, 2026 at 8:52 PM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Vivaldi Browser","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Vivaldi Browser","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Has 13 attachments (29.19 KB)","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"attachment-7.jpeg","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"attachment-1.png","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"+","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"8","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Ancestry","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Tuesday, April 7th, 2026 at 5:20 PM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Meet Archives: The budget-friendly sister site to Ancestry","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Meet Archives: The budget-friendly sister site to Ancestry","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Matúš Kostolný Denník N","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Thursday, April 2nd, 2026 at 12:52 PM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Príbeh takáčovcov z podsvetia: výpalné, výbuchy a krytie zhora. A teraz ich využili proti Čurillovi","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Príbeh takáčovcov z podsvetia: výpalné, výbuchy a krytie zhora. A teraz ich využili proti Čurillovi","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"MyHeritage SuperSearch Alerts","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Vlcsak - 6 нови резултата","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Vlcsak - 6 нови резултата","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Mark as unread","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Mark as unread","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Move to trash","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Move to trash","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Move to archive","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Move to archive","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Snooze","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Snooze","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Star conversation","depth":17,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Star conversation","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Proton","depth":20,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Official","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Tuesday, March 31st, 2026 at 7:22 AM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Last chance to save 40% on Mail Plus","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Last chance to save 40% on Mail Plus","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"SmartyMe","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Monday, March 30th, 2026 at 2:11 AM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Activated for thetwo.graduate309@aleeas.com","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Activated for thetwo.graduate309@aleeas.com","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Matúš Kostolný Denník N","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Friday, March 27th, 2026 at 11:15 AM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Kaštiele skupuje firma zo schátranej chaty. Stopy vedú k Pellegriniho kamarátovi, ktorého pozná Fico aj Bödör","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Kaštiele skupuje firma zo schátranej chaty. Stopy vedú k Pellegriniho kamarátovi, ktorého pozná Fico aj Bödör","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"SmartyMe","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Friday, March 27th, 2026 at 12:21 AM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Before the season moves on","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Before the season moves on","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"SmartyMe","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Thursday, March 26th, 2026 at 2:49 AM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Your plan is almost ready — don't miss it!","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Your plan is almost ready — don't miss it!","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Proton","depth":20,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Official","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Wednesday, March 25th, 2026 at 12:38 PM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Spring privacy refresh: Get 40% off Mail Plus","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Spring privacy refresh: Get 40% off Mail Plus","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"SmartyMe","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Tuesday, March 24th, 2026 at 9:10 AM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2 messages in conversation","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Spring is a good time to upgrade","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Spring is a good time to upgrade","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Matúš Kostolný Denník N","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Friday, March 20th, 2026 at 10:09 AM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Viac voľna, zdravší rozpočet či nové byty. Čo všetko Fico sľuboval a dnes vraví, že mu to je ukradnuté","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Viac voľna, zdravší rozpočet či nové byty. Čo všetko Fico sľuboval a dnes vraví, že mu to je ukradnuté","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Streamlit team","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Thursday, March 19th, 2026 at 6:52 PM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"🎈 1 million devs, dynamic containers, new components gallery, + more!","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"🎈 1 million devs, dynamic containers, new components gallery, + more!","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Ancestry","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Wednesday, March 18th, 2026 at 11:05 PM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2 messages in conversation","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Madalyn, check out these new and updated collections","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Madalyn, check out these new and updated collections","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Kate from Relatio","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Wednesday, March 18th, 2026 at 9:10 PM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Last Chance: 75% Off Men's Health Plan Ends Soon!","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Last Chance: 75% Off Men's Health Plan Ends Soon!","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Kate from Relatio","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Tuesday, March 17th, 2026 at 9:10 PM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Special discount of 75% for you!","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Special discount of 75% for you!","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"MyHeritage SuperSearch Alerts","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Tuesday, March 17th, 2026 at 9:48 AM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Vltsak - 4 new results","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Vltsak - 4 new results","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"SmartyMe","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Monday, March 16th, 2026 at 11:27 PM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"A Little Luck, A Lot Smarter 🍀","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"A Little Luck, A Lot Smarter 🍀","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Kate from Relatio","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Monday, March 16th, 2026 at 9:10 PM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Unprecedented 75% OFF for You Today","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unprecedented 75% OFF for You Today","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"NuPhy","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Monday, March 16th, 2026 at 11:13 AM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Confirm you want to receive email marketing","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Confirm you want to receive email marketing","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"NuPhy","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Monday, March 16th, 2026 at 11:07 AM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Customer account confirmation","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Customer account confirmation","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"NuPhy","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Monday, March 16th, 2026 at 11:07 AM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Customer account activation","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Customer account activation","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"NuPhy","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Monday, March 16th, 2026 at 11:05 AM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2 messages in conversation","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Verify your email with NuPhy","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Verify your email with NuPhy","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"NuPhy","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Monday, March 16th, 2026 at 11:04 AM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Exclusive: Get 10% off for every friend you refer","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Exclusive: Get 10% off for every friend you refer","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Kate from Relatio","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sunday, March 15th, 2026 at 10:10 PM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Oops, We Made a Mistake..","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Oops, We Made a Mistake..","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Kate from Relatio","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sunday, March 15th, 2026 at 10:00 PM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Your Quiz Results Are Ready: We’ve reserved a private offer for you","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Your Quiz Results Are Ready: We’ve reserved a private offer for you","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sam Morgan","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sunday, March 15th, 2026 at 12:00 AM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Reminder - confirm your email address","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Reminder - confirm your email address","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Kate from Relatio","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Saturday, March 14th, 2026 at 11:25 PM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Confirm your email address","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Confirm your email address","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"SmartyMe","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Saturday, March 14th, 2026 at 6:45 PM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"10 minutes. That’s enough.","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"10 minutes. That’s enough.","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"SmartyMe","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Friday, March 13th, 2026 at 6:43 PM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Your plan price is reserved","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Your plan price is reserved","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Matúš Kostolný Denník N","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Friday, March 13th, 2026 at 11:29 AM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Nerozumieme, prečo financujete Putinovu vojnu. Reportáž z mesta, kde sa zastavila ruská ropa","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Nerozumieme, prečo financujete Putinovu vojnu. Reportáž z mesta, kde sa zastavila ruská ropa","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"SmartyMe","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Thursday, March 12th, 2026 at 1:53 PM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Your updated plan is ready","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Your updated plan is ready","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"SmartyMe","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Wednesday, March 11th, 2026 at 9:17 AM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Your plan is about to be deleted","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Your plan is about to be deleted","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"SmartyMe ★","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Tuesday, March 10th, 2026 at 2:18 AM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Your personalized growth plan is almost ready","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Your personalized growth plan is almost ready","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unread email","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Matúš Kostolný Denník N","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Friday, March 6th, 2026 at 10:13 AM","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Naďa Marcinková je kľúčovou svedkyňou vyšetrovania Epsteina. Podľa FBI ju sexuálne vykorisťoval","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Naďa Marcinková je kľúčovou svedkyňou vyšetrovania Epsteina. Podľa FBI ju sexuálne vykorisťoval","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Go to previous page","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Go to previous page","depth":18,"bounds":{"left":0.57147604,"top":0.3339984,"width":0.018783245,"height":0.061851557},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Go to page 1","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Go to page 1","depth":18,"bounds":{"left":0.5817819,"top":0.3339984,"width":0.011303191,"height":0.061851557},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Go to page 2","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Go to page 2","depth":18,"bounds":{"left":0.5924202,"top":0.3339984,"width":0.010970744,"height":0.061851557},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Go to page 3","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Go to page 3","depth":18,"bounds":{"left":0.60388964,"top":0.3339984,"width":0.011136968,"height":0.061851557},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Go to page 4","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Go to page 4","depth":18,"bounds":{"left":0.61535907,"top":0.3339984,"width":0.011136968,"height":0.061851557},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Go to next page","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Go to next page","depth":18,"bounds":{"left":0.6286569,"top":0.3339984,"width":0.011136968,"height":0.061851557},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Show side panel","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"Show side panel","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Toggle Contacts app","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Toggle Contacts app","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Toggle Calendar app","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Toggle Calendar app","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Toggle security center app","depth":14,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Toggle security center app","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Proton VPN Toggle VPN dashboard","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Toggle VPN dashboard","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Toggle referral","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Toggle referral","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"New","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Share your availability with booking pages in Proton Calendar, and host secure meetings with Proton Meet.","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close","depth":8,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Close","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
-8550047190299644374
|
5869207957771505710
|
click
|
accessibility
|
NULL
|
DXP4800PLUS-B5F8
5 Signs You Have Successfully Hur DXP4800PLUS-B5F8
5 Signs You Have Successfully Hurt a Narcissist; - [EMAIL] - Gmail
(66) Inbox | [EMAIL] | Proton Mail
Western Digital Red Plus 3.5 6TB 5400rpm 256MB SATA3 (WD60EFPX) от 241,72 € (472,76 лв.) Вътрешен хард диск Western Digital - Pazaruvaj.com
Western Digital Red Plus 3.5 6TB 5400rpm 256MB SATA3 (WD60EFPX) от 241,72 € (472,76 лв.) Вътрешен хард диск Western Digital - Pazaruvaj.com
Today's Deals
Today's Deals
architecture - screenpipe docs
architecture - screenpipe docs
[CircleCI] Workflow failed: jiminny / app on JY-20157-AJ-report-not-send-notification - [EMAIL] - Gmail
[CircleCI] Workflow failed: jiminny / app on JY-20157-AJ-report-not-send-notification - [EMAIL] - Gmail
Screenpipe — Archive
Screenpipe — Archive
SQLite Web: archive.db
SQLite Web: archive.db
SQLite Web: db.sqlite
SQLite Web: db.sqlite
Claude Platform
Claude Platform
rescue time detailed overview - Google Search
rescue time detailed overview - Google Search
Hey @louis030195 Ill check during my - screenpipe.com
Hey @louis030195 Ill check during my - screenpipe.com
GitHub - screenpipe/screenpipe: Run agents that work for you based on what you do. AI finally knows what you are doing · GitHub
GitHub - screenpipe/screenpipe: Run agents that work for you based on what you do. AI finally knows what you are doing · GitHub
Gong Pricing in 2026: Costs, Plans & Is It Worth It?
Gong Pricing in 2026: Costs, Plans & Is It Worth It?
YouTube
YouTube
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
Proton Mail
needs your permission to
enable desktop notifications
.
Close this banner
Close this banner
Proton Mail
Proton Mail
Proton Mail
Proton applications
New message
Navigation
Navigation
Inbox 66 unread conversations
Inbox
66
Drafts
Drafts
Sent
Sent
Starred
Starred
More
More
More
More
Views
Views
Views
Views
Newsletters
Newsletters
Folders
Folders
Folders
Folders
Create a new folder
Create a new folder
Manage your folders
Manage your folders
Labels
Labels
Labels
Labels
Create a new label
Create a new label
Manage your labels
Manage your labels
gmail.com 26-04-2025 17:28
gmail.com 26-04-2025 17:28
Important
Important
Show navigation bar
Show navigation bar
Your current storage: 17.75 MB / 500.00 MB
17.75 MB / 500.00 MB
17.75 MB
/
500.00 MB
Proton Mail [IP_ADDRESS]
Proton Mail
[IP_ADDRESS]
Search
Search
Search messages
Upgrade
Upgrade
Attention required Toggle settings
Attention required
Toggle settings
kovaliklukas <[EMAIL]>
Conversation list 66 unread messages
Conversation list
66 unread messages
Select all messages
Select all messages
Inbox
Inbox
More
More
All
All
Sort conversations
Previous page
Previous page
1/4
1
/
4
Next page
Next page
Upgrade to level up your privacy.
Upgrade
Close
Close
Unread email
Streamlit team
Thursday, April 23rd, 2026 at 7:58 PM
🔥 Check out Release 1.56 + new pivot table component, and join the virtual meetup!
🔥 Check out Release 1.56 + new pivot table component, and join the virtual meetup!
Unread email
Team Vivaldi
Wednesday, April 22nd, 2026 at 7:29 AM
Find anything in Vivaldi instantly.
Find anything in Vivaldi instantly.
Unread email
Team Vivaldi
Tuesday, April 21st, 2026 at 7:06 AM
Workspaces: Because 50+ tabs is normal
Workspaces: Because 50+ tabs is normal
Unread email
Team Vivaldi
Monday, April 20th, 2026 at 6:53 AM
Your feedback drives us
Your feedback drives us
Unread email
Team Vivaldi
Sunday, April 19th, 2026 at 6:37 AM
Advanced Tab Mastery
Advanced Tab Mastery
Unread email
Matúš Kostolný Denník N
Saturday, April 18th, 2026 at 12:14 PM
Jovinečko na polícii: Kvôli Pellegrinimu mi volala Plačková. Núkala okolo 30-tisíc eur
Jovinečko na polícii: Kvôli Pellegrinimu mi volala Plačková. Núkala okolo 30-tisíc eur
Unread email
Team Vivaldi
Saturday, April 18th, 2026 at 6:55 AM
Take Vivaldi Everywhere with Sync
Take Vivaldi Everywhere with Sync
Proton
Official
Saturday, April 18th, 2026 at 5:21 AM
Born Private, Proton Workspace, private video calling + more
Born Private, Proton Workspace, private video calling + more
Unread email
Team Vivaldi
Friday, April 17th, 2026 at 5:45 AM
The Sidebar: Your Secret Productivity Weapon
The Sidebar: Your Secret Productivity Weapon
Unread email
Team Vivaldi
Thursday, April 16th, 2026 at 6:48 AM
What you know about tabs is wrong, but we’ve got you covered!
What you know about tabs is wrong, but we’ve got you covered!
Unread email
Team Vivaldi
Wednesday, April 15th, 2026 at 6:57 AM
Welcome to Vivaldi!
Welcome to Vivaldi!
Unread email
Team Vivaldi
Monday, April 13th, 2026 at 6:42 AM
2 messages in conversation
Almost there! Complete your Vivaldi account setup
Almost there! Complete your Vivaldi account setup
Unread email
Ancestry
Friday, April 10th, 2026 at 5:20 PM
2 messages in conversation
Have you tried Archives yet?
Have you tried Archives yet?
Unread email
Matúš Kostolný Denník N
Friday, April 10th, 2026 at 11:00 AM
Fico si v tichosti zväčšil byt v rezidencii pod Slavínom
Fico si v tichosti zväčšil byt v rezidencii pod Slavínom
Disney+
Friday, April 10th, 2026 at 10:22 AM
5 messages in conversation
Complete your Disney+ Subscription
Complete your Disney+ Subscription
Team Vivaldi
Wednesday, April 8th, 2026 at 8:52 PM
Vivaldi Browser
Vivaldi Browser
Has 13 attachments (29.19 KB)
attachment-7.jpeg
attachment-1.png
+
8
Unread email
Ancestry
Tuesday, April 7th, 2026 at 5:20 PM
Meet Archives: The budget-friendly sister site to Ancestry
Meet Archives: The budget-friendly sister site to Ancestry
Unread email
Matúš Kostolný Denník N
Thursday, April 2nd, 2026 at 12:52 PM
Príbeh takáčovcov z podsvetia: výpalné, výbuchy a krytie zhora. A teraz ich využili proti Čurillovi
Príbeh takáčovcov z podsvetia: výpalné, výbuchy a krytie zhora. A teraz ich využili proti Čurillovi
MyHeritage SuperSearch Alerts
Vlcsak - 6 нови резултата
Vlcsak - 6 нови резултата
Mark as unread
Mark as unread
Move to trash
Move to trash
Move to archive
Move to archive
Snooze
Snooze
Star conversation
Star conversation
Unread email
Proton
Official
Tuesday, March 31st, 2026 at 7:22 AM
Last chance to save 40% on Mail Plus
Last chance to save 40% on Mail Plus
SmartyMe
Monday, March 30th, 2026 at 2:11 AM
Activated for [EMAIL]
Activated for [EMAIL]
Unread email
Matúš Kostolný Denník N
Friday, March 27th, 2026 at 11:15 AM
Kaštiele skupuje firma zo schátranej chaty. Stopy vedú k Pellegriniho kamarátovi, ktorého pozná Fico aj Bödör
Kaštiele skupuje firma zo schátranej chaty. Stopy vedú k Pellegriniho kamarátovi, ktorého pozná Fico aj Bödör
Unread email
SmartyMe
Friday, March 27th, 2026 at 12:21 AM
Before the season moves on
Before the season moves on
Unread email
SmartyMe
Thursday, March 26th, 2026 at 2:49 AM
Your plan is almost ready — don't miss it!
Your plan is almost ready — don't miss it!
Proton
Official
Wednesday, March 25th, 2026 at 12:38 PM
Spring privacy refresh: Get 40% off Mail Plus
Spring privacy refresh: Get 40% off Mail Plus
Unread email
SmartyMe
Tuesday, March 24th, 2026 at 9:10 AM
2 messages in conversation
Spring is a good time to upgrade
Spring is a good time to upgrade
Matúš Kostolný Denník N
Friday, March 20th, 2026 at 10:09 AM
Viac voľna, zdravší rozpočet či nové byty. Čo všetko Fico sľuboval a dnes vraví, že mu to je ukradnuté
Viac voľna, zdravší rozpočet či nové byty. Čo všetko Fico sľuboval a dnes vraví, že mu to je ukradnuté
Streamlit team
Thursday, March 19th, 2026 at 6:52 PM
🎈 1 million devs, dynamic containers, new components gallery, + more!
🎈 1 million devs, dynamic containers, new components gallery, + more!
Ancestry
Wednesday, March 18th, 2026 at 11:05 PM
2 messages in conversation
Madalyn, check out these new and updated collections
Madalyn, check out these new and updated collections
Kate from Relatio
Wednesday, March 18th, 2026 at 9:10 PM
Last Chance: 75% Off Men's Health Plan Ends Soon!
Last Chance: 75% Off Men's Health Plan Ends Soon!
Kate from Relatio
Tuesday, March 17th, 2026 at 9:10 PM
Special discount of 75% for you!
Special discount of 75% for you!
MyHeritage SuperSearch Alerts
Tuesday, March 17th, 2026 at 9:48 AM
Vltsak - 4 new results
Vltsak - 4 new results
SmartyMe
Monday, March 16th, 2026 at 11:27 PM
A Little Luck, A Lot Smarter 🍀
A Little Luck, A Lot Smarter 🍀
Kate from Relatio
Monday, March 16th, 2026 at 9:10 PM
Unprecedented 75% OFF for You Today
Unprecedented 75% OFF for You Today
Unread email
NuPhy
Monday, March 16th, 2026 at 11:13 AM
Confirm you want to receive email marketing
Confirm you want to receive email marketing
Unread email
NuPhy
Monday, March 16th, 2026 at 11:07 AM
Customer account confirmation
Customer account confirmation
NuPhy
Monday, March 16th, 2026 at 11:07 AM
Customer account activation
Customer account activation
NuPhy
Monday, March 16th, 2026 at 11:05 AM
2 messages in conversation
Verify your email with NuPhy
Verify your email with NuPhy
NuPhy
Monday, March 16th, 2026 at 11:04 AM
Exclusive: Get 10% off for every friend you refer
Exclusive: Get 10% off for every friend you refer
Kate from Relatio
Sunday, March 15th, 2026 at 10:10 PM
Oops, We Made a Mistake..
Oops, We Made a Mistake..
Kate from Relatio
Sunday, March 15th, 2026 at 10:00 PM
Your Quiz Results Are Ready: We’ve reserved a private offer for you
Your Quiz Results Are Ready: We’ve reserved a private offer for you
Unread email
Sam Morgan
Sunday, March 15th, 2026 at 12:00 AM
Reminder - confirm your email address
Reminder - confirm your email address
Unread email
Kate from Relatio
Saturday, March 14th, 2026 at 11:25 PM
Confirm your email address
Confirm your email address
Unread email
SmartyMe
Saturday, March 14th, 2026 at 6:45 PM
10 minutes. That’s enough.
10 minutes. That’s enough.
Unread email
SmartyMe
Friday, March 13th, 2026 at 6:43 PM
Your plan price is reserved
Your plan price is reserved
Unread email
Matúš Kostolný Denník N
Friday, March 13th, 2026 at 11:29 AM
Nerozumieme, prečo financujete Putinovu vojnu. Reportáž z mesta, kde sa zastavila ruská ropa
Nerozumieme, prečo financujete Putinovu vojnu. Reportáž z mesta, kde sa zastavila ruská ropa
SmartyMe
Thursday, March 12th, 2026 at 1:53 PM
Your updated plan is ready
Your updated plan is ready
Unread email
SmartyMe
Wednesday, March 11th, 2026 at 9:17 AM
Your plan is about to be deleted
Your plan is about to be deleted
Unread email
SmartyMe ★
Tuesday, March 10th, 2026 at 2:18 AM
Your personalized growth plan is almost ready
Your personalized growth plan is almost ready
Unread email
Matúš Kostolný Denník N
Friday, March 6th, 2026 at 10:13 AM
Naďa Marcinková je kľúčovou svedkyňou vyšetrovania Epsteina. Podľa FBI ju sexuálne vykorisťoval
Naďa Marcinková je kľúčovou svedkyňou vyšetrovania Epsteina. Podľa FBI ju sexuálne vykorisťoval
Go to previous page
Go to previous page
Go to page 1
Go to page 1
Go to page 2
Go to page 2
Go to page 3
Go to page 3
Go to page 4
Go to page 4
Go to next page
Go to next page
Show side panel
Show side panel
Toggle Contacts app
Toggle Contacts app
Toggle Calendar app
Toggle Calendar app
Toggle security center app
Toggle security center app
Proton VPN Toggle VPN dashboard
Toggle VPN dashboard
Toggle referral
Toggle referral
New
Share your availability with booking pages in Proton Calendar, and host secure meetings with Proton Meet.
Close
Close...
|
NULL
|
|
31771
|
644
|
30
|
2026-04-16T06:33:56.521149+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776321236521_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
SlackFileEditViewGoHistoryWindowHelp• 0-zshDOCKER• SlackFileEditViewGoHistoryWindowHelp• 0-zshDOCKER• 881DEV (-zsh)О ₴2APP (-zsh)• хзec2-user@ip-10-...0 84-zsh• 85lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny~/.screenpipe/data/data/2026-04-15 $ sqlite3 ~/.screenpipe/db.sqliteSELECTMIN(f.timestamp) ASstart_time,MAX(f.timestamp) ASend_timeFROM framesfJOINvideo_chunks v ON f.video_chunk_id = v.idWHERE v.file_path LIKE*%compact_monitor_2_1776148769344.mp4%';2026-04-14T06:24:34.056771+00:0012026-04-14T06:28:52.607990+00:00lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny~/.screenpipe/data/data/2026-04-15 $ sqlite3 ~/.screenpipe/db.sqlite"SELECT DISTINCT v.file_pathFROM frames fJOIN video_chunks v ON f.video_chunk_id = v.idWHEREf.timestamp >='2026-04-15T07:00:00+00:00'AND f.timestamp <=' 2026-04-15T07:05:00+00:00' ;/Users/lukas/.screenpipe/data/data/2026-04-15/compact_monitor_1_1776237284692.mp4/Users/lukas/.screenpipe/data/data/2026-04-15/compact_monitor_2_1776237291726.mp4/Users/lukas/.screenpipe/data/data/2026-04-15/compact_monitor_1_1776237595554.mp4/Users/lukas/.screenpipe/data/data/2026-04-15/compact_monitor_2_1776237598300.mp4lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe/data/data/2026-04-15 $ld6]Daily - Platform • in 12 mA100% <478-zsh886-zshO &7Thu 16 Apr 9:33:56T81* Unable to acce...O 88...
|
NULL
|
-8550036831846507323
|
NULL
|
click
|
ocr
|
NULL
|
SlackFileEditViewGoHistoryWindowHelp• 0-zshDOCKER• SlackFileEditViewGoHistoryWindowHelp• 0-zshDOCKER• 881DEV (-zsh)О ₴2APP (-zsh)• хзec2-user@ip-10-...0 84-zsh• 85lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny~/.screenpipe/data/data/2026-04-15 $ sqlite3 ~/.screenpipe/db.sqliteSELECTMIN(f.timestamp) ASstart_time,MAX(f.timestamp) ASend_timeFROM framesfJOINvideo_chunks v ON f.video_chunk_id = v.idWHERE v.file_path LIKE*%compact_monitor_2_1776148769344.mp4%';2026-04-14T06:24:34.056771+00:0012026-04-14T06:28:52.607990+00:00lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny~/.screenpipe/data/data/2026-04-15 $ sqlite3 ~/.screenpipe/db.sqlite"SELECT DISTINCT v.file_pathFROM frames fJOIN video_chunks v ON f.video_chunk_id = v.idWHEREf.timestamp >='2026-04-15T07:00:00+00:00'AND f.timestamp <=' 2026-04-15T07:05:00+00:00' ;/Users/lukas/.screenpipe/data/data/2026-04-15/compact_monitor_1_1776237284692.mp4/Users/lukas/.screenpipe/data/data/2026-04-15/compact_monitor_2_1776237291726.mp4/Users/lukas/.screenpipe/data/data/2026-04-15/compact_monitor_1_1776237595554.mp4/Users/lukas/.screenpipe/data/data/2026-04-15/compact_monitor_2_1776237598300.mp4lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe/data/data/2026-04-15 $ld6]Daily - Platform • in 12 mA100% <478-zsh886-zshO &7Thu 16 Apr 9:33:56T81* Unable to acce...O 88...
|
31770
|
|
550
|
14
|
2
|
2026-04-11T11:54:05.389541+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775908445389_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp-zshDOCKERO ₴1DEV (-zsh)O $2APP (-zsh)• ₴з-zshsettingvalueaudio chunk durationportaudio disabledvision disabledpause on DRM contentaudio enginevad enginedata directorydebug modetelemetryuse pii removaluse all monitors2026-04-11T14:52:51.448625Zignored windowsincluded windowscloud syncauto-destruct piddeepgram key30 seconds3030truefalsefalseParakeetSilero/Users/lukas/.screenpipefalsetruetruetrueINFO screenpipe_core::pipes: pipe scheduler started(generation 2)disablednot setlanguagesall languagesmonitorsno monitors availableaudio devices• 84-zsh$0(ahl• ₴5-zsh100% <7186Sat 11 Apr 14:54:04-zsh181• ₴7|disabledyou are using local processing. all your data stays on your computer.warning: telemetry is enabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T14:52:51.449041ZINFO screenpipe: starting UIevent capture2026-04-11T14:52:51.452565ZWARN screenpipe: piagent install failed: bun not found - install from https://bun.sh2026-04-11714:52:51.45487422026-04-11T14:52:51.462971ZINFO screenpipe_engine::power::manager: initial power profile: Performance(on_ac=true, battery=Some(100))INFO screenpipe_engine::ui_recorder: Starting UI event capture2026-04-11T14:52:51.476861ZINFO screenpipe_engine::ui_recorder: UI recording session started: 3c87e288-1128-414e-a08e-08b8350399412026-04-11714:52:51.4769432INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warming from DB (2026-04-10 11:52:51.476940 UTC to 2026-04-11 11:52:51.476940 UTC)2026-04-11T14:52:51.476899ZINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)...
|
NULL
|
-8549910951610062812
|
NULL
|
visual_change
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp-zshDOCKERO ₴1DEV (-zsh)O $2APP (-zsh)• ₴з-zshsettingvalueaudio chunk durationportaudio disabledvision disabledpause on DRM contentaudio enginevad enginedata directorydebug modetelemetryuse pii removaluse all monitors2026-04-11T14:52:51.448625Zignored windowsincluded windowscloud syncauto-destruct piddeepgram key30 seconds3030truefalsefalseParakeetSilero/Users/lukas/.screenpipefalsetruetruetrueINFO screenpipe_core::pipes: pipe scheduler started(generation 2)disablednot setlanguagesall languagesmonitorsno monitors availableaudio devices• 84-zsh$0(ahl• ₴5-zsh100% <7186Sat 11 Apr 14:54:04-zsh181• ₴7|disabledyou are using local processing. all your data stays on your computer.warning: telemetry is enabled. only error-level data will be sent.to disable, use the --disable-telemetry flag.check latest changes here: https://github.com/screenpipe/screenpipe/releases2026-04-11T14:52:51.449041ZINFO screenpipe: starting UIevent capture2026-04-11T14:52:51.452565ZWARN screenpipe: piagent install failed: bun not found - install from https://bun.sh2026-04-11714:52:51.45487422026-04-11T14:52:51.462971ZINFO screenpipe_engine::power::manager: initial power profile: Performance(on_ac=true, battery=Some(100))INFO screenpipe_engine::ui_recorder: Starting UI event capture2026-04-11T14:52:51.476861ZINFO screenpipe_engine::ui_recorder: UI recording session started: 3c87e288-1128-414e-a08e-08b8350399412026-04-11714:52:51.4769432INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warming from DB (2026-04-10 11:52:51.476940 UTC to 2026-04-11 11:52:51.476940 UTC)2026-04-11T14:52:51.476899ZINFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)...
|
549
|
|
26527
|
562
|
6
|
2026-04-15T13:25:04.488682+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-15/1776 /Users/lukas/.screenpipe/data/data/2026-04-15/1776259504488_m1.jpg...
|
Boosteroid
|
Boosteroid
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
+SlackFileEditViewGoEDHomeActivity..•More+HistoryW +SlackFileEditViewGoEDHomeActivity..•More+HistoryWindowHelp→Search Jiminny IncJiminny ...sos+# general# infra-changes# jiminny-bg# platform-tickets# product _launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi…..Direct messagesStoyan TanevVesGalya DimitrovaAneliya Angelova, ...Vasil VasilevSteliyan GeorgievAdelina Petrova, Ili...P. Adelina Petrova0. Nikolay Nikolov2 Galya Dimitrova, Ni...ii: AppsToastJira CloudGoogle Cale...# releases8 22Messagesnewdou+@ Files• Bookmarksv 2 new messagesGitHub APP3:28 PM7 new commits pushed tomaster by nikolay-yankovNew24b989ee - Enhance SECFIXdocumentation and policiesa3a0a742 - Update SECFIX Slack channelreference in documentation and workflowfiles071c999d - Merge branch 'master' intoimprove-secfix-bot-15-04-2026981e9a1a - Update SECFIX_PROMPT.mdto enhance clarity on upgrade safety andchangelog reviews6e938e53 - Enhance SECFIX workflow withSlack notification optionsShow more( jiminny/app Added by GitHubCircleCl APP3:53 PMDeployment Successful!Project: appWhen:04/15/202612:53:30Tag:View JobMessage #releases+Sprint Review • 5 m leftRActivity MonitorAll ProcessesProcess NameBoosteroidWindowServerFirefoxFirefoxCP Isolated Web ContentFirefoxCursorUlViewService (Not Responding)FirefoxCP Isolated Web ContentFirefox GPU HelperFirefoxCP Isolated Web ContentFirefox GPU HelperVTDecoderXPCServiceFirefoxCP Isolated Web ContentSlack Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentClaude Helper (Renderer)Notion Calendar Helper (Renderer)Notion Helper (Renderer)claudeFirefoxCP Isolated Web ContentiTerm2FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentMem...2,03 GB1,18 GB977,8 MB962,6 MB846,7 MB793,6 MB777,4 MB554,1 MB547,2 MB543,9 MB516,2 MB463,4 MB461,8 MB424,3 MB412,0 MB390,0 MB388,4 MB388,1 MB372,4 MB350,6 MB342,9 MB329,7 MB326,2 MB291,4 MB252,2 MB239,5 MB236,2 MB216,3 MBMEMORY PRESSUREPhysical Memory:Memory Used:Cached Files:Swap Used:100% C78Wed 15 Apr 16:25:04CPUMemoryDiskThreads39237525852829242611251526242623262215152113277272825EnergyPorts61319 7547261241 20320 023128242126250167123185124124127119124119220172328721241 834124128124PID93892407801442974146648424203080193671314673938993548041863358313527643016368984365248173605192654811485091035833487856138482987429516,00 GB14,14 GB <1,81 GB2,93 GBApp Memory:Wired Memory:Compressed:NetworkUserlukas_windowserverlukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukas3,66 GB2,87 GB7,06 GB...
|
NULL
|
-8549320042548188097
|
NULL
|
click
|
ocr
|
NULL
|
+SlackFileEditViewGoEDHomeActivity..•More+HistoryW +SlackFileEditViewGoEDHomeActivity..•More+HistoryWindowHelp→Search Jiminny IncJiminny ...sos+# general# infra-changes# jiminny-bg# platform-tickets# product _launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi…..Direct messagesStoyan TanevVesGalya DimitrovaAneliya Angelova, ...Vasil VasilevSteliyan GeorgievAdelina Petrova, Ili...P. Adelina Petrova0. Nikolay Nikolov2 Galya Dimitrova, Ni...ii: AppsToastJira CloudGoogle Cale...# releases8 22Messagesnewdou+@ Files• Bookmarksv 2 new messagesGitHub APP3:28 PM7 new commits pushed tomaster by nikolay-yankovNew24b989ee - Enhance SECFIXdocumentation and policiesa3a0a742 - Update SECFIX Slack channelreference in documentation and workflowfiles071c999d - Merge branch 'master' intoimprove-secfix-bot-15-04-2026981e9a1a - Update SECFIX_PROMPT.mdto enhance clarity on upgrade safety andchangelog reviews6e938e53 - Enhance SECFIX workflow withSlack notification optionsShow more( jiminny/app Added by GitHubCircleCl APP3:53 PMDeployment Successful!Project: appWhen:04/15/202612:53:30Tag:View JobMessage #releases+Sprint Review • 5 m leftRActivity MonitorAll ProcessesProcess NameBoosteroidWindowServerFirefoxFirefoxCP Isolated Web ContentFirefoxCursorUlViewService (Not Responding)FirefoxCP Isolated Web ContentFirefox GPU HelperFirefoxCP Isolated Web ContentFirefox GPU HelperVTDecoderXPCServiceFirefoxCP Isolated Web ContentSlack Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentClaude Helper (Renderer)Notion Calendar Helper (Renderer)Notion Helper (Renderer)claudeFirefoxCP Isolated Web ContentiTerm2FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentMem...2,03 GB1,18 GB977,8 MB962,6 MB846,7 MB793,6 MB777,4 MB554,1 MB547,2 MB543,9 MB516,2 MB463,4 MB461,8 MB424,3 MB412,0 MB390,0 MB388,4 MB388,1 MB372,4 MB350,6 MB342,9 MB329,7 MB326,2 MB291,4 MB252,2 MB239,5 MB236,2 MB216,3 MBMEMORY PRESSUREPhysical Memory:Memory Used:Cached Files:Swap Used:100% C78Wed 15 Apr 16:25:04CPUMemoryDiskThreads39237525852829242611251526242623262215152113277272825EnergyPorts61319 7547261241 20320 023128242126250167123185124124127119124119220172328721241 834124128124PID93892407801442974146648424203080193671314673938993548041863358313527643016368984365248173605192654811485091035833487856138482987429516,00 GB14,14 GB <1,81 GB2,93 GBApp Memory:Wired Memory:Compressed:NetworkUserlukas_windowserverlukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukas3,66 GB2,87 GB7,06 GB...
|
NULL
|
|
42128
|
892
|
60
|
2026-04-17T06:47:46.121819+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-17/1776 /Users/lukas/.screenpipe/data/data/2026-04-17/1776408466121_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
FirefoxFileEditViewHistoryBookmarksProfilesToolsWi FirefoxFileEditViewHistoryBookmarksProfilesToolsWindowHelp01meet.google.com/mie-gawc-dsi?authuser=lukas.kovalik%40jiminny.comDaily - Platform - now100% 128 • Fri 17 Apr 9:47:45,Nikolay Yankov (Presenting, annotating)ChromeFileViewHistoryProfilesHelpIntPla9 ServiciJ Font AlCost|cF Project• Fix Dep|[JY-97https://jiminny.atlassian.net/jira/software/c/projects/JY/boards/37?selectedissue=JY-1979888MIHome | SalesforceEa Datadog•$Platform Team88Q SearJY-18679 / JY-19798READY FEvaluation for AI Activity TypesReworkPhase:Nudge!COST-EDackicy5 .0v DescriptionA few months ago we launch a new feature which uses Al to detect the activity type of a call. This is done by adding adescription to each activity type in the Playbook page. We still don't have automatic evaluation for this feature.A JY-2|Investicwhy exFontawMAINT,Ready fe1 n• create automatic evaluation for AI Activity Type feature• use data from Jiminny and other customers as a data set|• make sure we test with different teams - sales/cs and with different call types (conference, dialer calls) |• use a smart model to determine whether the data set can be set as a source of truth• then choose other potentials models and run it with them so we can see which is performing better• make sure the evaluation is accurate and the results are good and reliableJY-2SubtasksAI Reppage dipromotAJ REPSBacklogAdd subtask|Linked work itemsrelates to• JY-20510 Combine AI requests for summaries, action items, key points, activi…..BACKLOG=A JY-2|Send emailIssue with9:47 AM | Daily - PlatformSộ3* Project|* ClatJiminn*dev.apOCTLLY• dev.ap:|Q8• Fri 17 Apr 9:47dev.apl0 All Bookmarks|Steliyan GeorgievNikolay Ivanov2 othersNikolay Yankov+Code Review v* Improve Technical StoryDetailsAssigneeNikolay IvanovAssign to meReporterGalya DimitrovaDevelopment@ Open with VS Codk3meet.google.com2 branchesa Nikolay Yankov (You, presenti.26 commits2 pull requests2 buildsComponentsPlatformSub-ProductLukas Kovalik1:53=...
|
NULL
|
-8549292458349511428
|
NULL
|
visual_change
|
ocr
|
NULL
|
FirefoxFileEditViewHistoryBookmarksProfilesToolsWi FirefoxFileEditViewHistoryBookmarksProfilesToolsWindowHelp01meet.google.com/mie-gawc-dsi?authuser=lukas.kovalik%40jiminny.comDaily - Platform - now100% 128 • Fri 17 Apr 9:47:45,Nikolay Yankov (Presenting, annotating)ChromeFileViewHistoryProfilesHelpIntPla9 ServiciJ Font AlCost|cF Project• Fix Dep|[JY-97https://jiminny.atlassian.net/jira/software/c/projects/JY/boards/37?selectedissue=JY-1979888MIHome | SalesforceEa Datadog•$Platform Team88Q SearJY-18679 / JY-19798READY FEvaluation for AI Activity TypesReworkPhase:Nudge!COST-EDackicy5 .0v DescriptionA few months ago we launch a new feature which uses Al to detect the activity type of a call. This is done by adding adescription to each activity type in the Playbook page. We still don't have automatic evaluation for this feature.A JY-2|Investicwhy exFontawMAINT,Ready fe1 n• create automatic evaluation for AI Activity Type feature• use data from Jiminny and other customers as a data set|• make sure we test with different teams - sales/cs and with different call types (conference, dialer calls) |• use a smart model to determine whether the data set can be set as a source of truth• then choose other potentials models and run it with them so we can see which is performing better• make sure the evaluation is accurate and the results are good and reliableJY-2SubtasksAI Reppage dipromotAJ REPSBacklogAdd subtask|Linked work itemsrelates to• JY-20510 Combine AI requests for summaries, action items, key points, activi…..BACKLOG=A JY-2|Send emailIssue with9:47 AM | Daily - PlatformSộ3* Project|* ClatJiminn*dev.apOCTLLY• dev.ap:|Q8• Fri 17 Apr 9:47dev.apl0 All Bookmarks|Steliyan GeorgievNikolay Ivanov2 othersNikolay Yankov+Code Review v* Improve Technical StoryDetailsAssigneeNikolay IvanovAssign to meReporterGalya DimitrovaDevelopment@ Open with VS Codk3meet.google.com2 branchesa Nikolay Yankov (You, presenti.26 commits2 pull requests2 buildsComponentsPlatformSub-ProductLukas Kovalik1:53=...
|
42126
|
|
18437
|
391
|
83
|
2026-04-14T16:16:53.832387+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-14/1776 /Users/lukas/.screenpipe/data/data/2026-04-14/1776183413832_m2.jpg...
|
Boosteroid
|
Boosteroid
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
35931/40Castle AgeHold down ALT and right-click to 35931/40Castle AgeHold down ALT and right-click to garrisoninside this building for protection andhealing.4 Siddhraj Jaisingh: 1676/16766 Mindaugas: 1662/16625 Honorius: 1609/16098 Ashikaga Takauji: 1596/15961 kovaliklukas: 1507/15073 Bird Jaguar: 1496/1496 ®II7 Basil the Macedonian: 1424/14242 Anccu Hualloc: 1366/1366Builderkovalfklukas (Britons))...
|
NULL
|
-8549185847643960017
|
NULL
|
click
|
ocr
|
NULL
|
35931/40Castle AgeHold down ALT and right-click to 35931/40Castle AgeHold down ALT and right-click to garrisoninside this building for protection andhealing.4 Siddhraj Jaisingh: 1676/16766 Mindaugas: 1662/16625 Honorius: 1609/16098 Ashikaga Takauji: 1596/15961 kovaliklukas: 1507/15073 Bird Jaguar: 1496/1496 ®II7 Basil the Macedonian: 1424/14242 Anccu Hualloc: 1366/1366Builderkovalfklukas (Britons))...
|
NULL
|
|
73980
|
1838
|
16
|
2026-04-23T08:56:12.760610+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-23/1776 /Users/lukas/.screenpipe/data/data/2026-04-23/1776934572760_m1.jpg...
|
Firefox
|
Userpilot | QuickSearch-Searched — Work
|
True
|
run.userpilot.io/events/labeled_event/83
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
New Tab
New Tab
AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app
AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
Jiminny
Jiminny
Userpilot | QuickSearch-Searched
Userpilot | QuickSearch-Searched
Close tab
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Userpilot
Dashboards
Dashboards
People
People
Data
Data
Analytics
Analytics
Sessions
Sessions
Workflows
Workflows
Engagement
Engagement
Feedback
Feedback
Events
Events
Search engagement, feedback, reports, users and more
⌘K
Notifications
Help
Settings
Jiminny
Production
:r4l3: Active
QuickSearch-Searched
Active
more
Segment
Segment All users
All users
Company
Company All companies
All companies
Page
Page All pages
All pages
Time period
Time period Last 90 days
Last 90 days
Active users
5,345
/
8177
0%
Active companies
301
/
346
15.77%
Total events occurred
161,832
283.71%
Avg. occurrences per user
30
130.77%
Overview
Overview
:r4l5:
D
:r4l7:
W
:r4l9:
M
:r4lb:
:r4ld:
Key stat
Jan 2026 (BST)
Total events occurred
11,827
Unique users
1,932
Unique companies
226
Key stat
Total events occurred
Unique users
Unique companies
Jan 2026 (BST)
11,827
1,932
226
Starting from Jan 24, 2026
Click data point to view event properties
Last 90 days
Last 90 days
Previous Period
Previous Period
User activity
Search...
User
Total occurrences
First occurred
Last occurred
Zakari Hessas
954eeb0d-dd4f-41bd-bac7-515252f7a645
10
April 9, 2026 - 10:22 AM
April 23, 2026 - 09:54 AM
Matthew Hinawski
e423ddad-9cae-4385-b8e0-e51a6458f07a
98
January 26, 2026 - 09:44 AM
April 23, 2026 - 09:54 AM
Leone McEwan
d133f179-7d18-4f82-8371-390bba11ba18
66
January 26, 2026 - 02:07 PM
April 23, 2026 - 09:54 AM
Scarlett Fahey
778bf294-1468-4bea-bdb2-c26283f82983
91
January 26, 2026 - 08:58 AM
April 23, 2026 - 09:54 AM
Lewis Pamphilon
6977ef50-8b6d-46f6-b137-3690571e3df6
83
January 30, 2026 - 01:34 PM
April 23, 2026 - 09:53 AM
Sara Howe
b63514df-7a8e-4810-9267-cf637720db9f
52
January 27, 2026 - 07:52 AM
April 23, 2026 - 09:53 AM
Katie Kirtley-Jones
567a470b-6751-47b2-902d-eb9b59075251
69
January 26, 2026 - 05:58 PM
April 23, 2026 - 09:52 AM
Mandy Metzing
4e2aa0aa-225c-4bbd-887b-35cf703acba6
3
March 12, 2026 - 07:17 AM
April 23, 2026 - 09:52 AM
Alex Roberts
2d1af0e3-02ff-45a8-8e2e-bd7a547f1812
46
January 26, 2026 - 09:36 AM
April 23, 2026 - 09:52 AM...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"New Tab","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"New Tab","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Userpilot | QuickSearch-Searched","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Userpilot | QuickSearch-Searched","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"bounds":{"left":0.0,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.0,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.0,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.017361112,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Userpilot","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXMenuButton","text":"Dashboards","depth":9,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Dashboards","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"People","depth":9,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"People","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Data","depth":9,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Data","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Analytics","depth":9,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Analytics","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Sessions","depth":9,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Sessions","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Workflows","depth":9,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Workflows","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Engagement","depth":9,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Engagement","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Feedback","depth":9,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feedback","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Events","depth":10,"help_text":"Back","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Events","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Search engagement, feedback, reports, users and more","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"⌘K","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Notifications","depth":12,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":true,"is_selected":false},{"role":"AXButton","text":"Help","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXMenuButton","text":"Settings","depth":11,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Production","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":":r4l3: Active","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"QuickSearch-Searched","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Active","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"more","depth":12,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Segment","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Segment All users","depth":15,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"All users","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Company","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Company All companies","depth":14,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"All companies","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Page","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Page All pages","depth":15,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"All pages","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Time period","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Time period Last 90 days","depth":15,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Last 90 days","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Active users","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"5,345","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"/","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"8177","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0%","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Active companies","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"301","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"/","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"346","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"15.77%","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Total events occurred","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"161,832","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"283.71%","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Avg. occurrences per user","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"30","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"130.77%","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Overview","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Overview","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":":r4l5:","depth":12,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"D","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":":r4l7:","depth":12,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"W","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":":r4l9:","depth":12,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"M","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":":r4lb:","depth":12,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":":r4ld:","depth":12,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Key stat","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jan 2026 (BST)","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Total events occurred","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"11,827","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unique users","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1,932","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unique companies","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"226","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Key stat","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Total events occurred","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unique users","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Unique companies","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jan 2026 (BST)","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"11,827","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1,932","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"226","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Starting from Jan 24, 2026","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Click data point to view event properties","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Last 90 days","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Last 90 days","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Previous Period","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Previous Period","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User activity","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXTextField","text":"Search...","depth":13,"help_text":"","role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"User","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Total occurrences","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"First occurred","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Last occurred","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Zakari Hessas","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"954eeb0d-dd4f-41bd-bac7-515252f7a645","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"10","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"April 9, 2026 - 10:22 AM","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"April 23, 2026 - 09:54 AM","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Matthew Hinawski","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"e423ddad-9cae-4385-b8e0-e51a6458f07a","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"98","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"January 26, 2026 - 09:44 AM","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"April 23, 2026 - 09:54 AM","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Leone McEwan","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"d133f179-7d18-4f82-8371-390bba11ba18","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"66","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"January 26, 2026 - 02:07 PM","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"April 23, 2026 - 09:54 AM","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Scarlett Fahey","depth":23,"bounds":{"left":0.21284722,"top":0.0,"width":0.06631944,"height":0.021666666},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"778bf294-1468-4bea-bdb2-c26283f82983","depth":21,"bounds":{"left":0.21284722,"top":0.0,"width":0.16006945,"height":0.018888889},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"91","depth":19,"bounds":{"left":0.48715279,"top":0.0,"width":0.010416667,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"January 26, 2026 - 08:58 AM","depth":19,"bounds":{"left":0.8753472,"top":0.0,"width":0.11631945,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"April 23, 2026 - 09:54 AM","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Lewis Pamphilon","depth":23,"bounds":{"left":0.21284722,"top":0.0,"width":0.07951389,"height":0.021666666},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"6977ef50-8b6d-46f6-b137-3690571e3df6","depth":21,"bounds":{"left":0.21284722,"top":0.0,"width":0.15868056,"height":0.018888889},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"83","depth":19,"bounds":{"left":0.48715279,"top":0.0,"width":0.010416667,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"January 30, 2026 - 01:34 PM","depth":19,"bounds":{"left":0.8753472,"top":0.0,"width":0.11597222,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"April 23, 2026 - 09:53 AM","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sara Howe","depth":23,"bounds":{"left":0.21284722,"top":0.030555556,"width":0.05,"height":0.021666666},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"b63514df-7a8e-4810-9267-cf637720db9f","depth":21,"bounds":{"left":0.21284722,"top":0.052222222,"width":0.15763889,"height":0.018888889},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"52","depth":19,"bounds":{"left":0.48715279,"top":0.040555555,"width":0.010416667,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"January 27, 2026 - 07:52 AM","depth":19,"bounds":{"left":0.8753472,"top":0.040555555,"width":0.11631945,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"April 23, 2026 - 09:53 AM","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Katie Kirtley-Jones","depth":23,"bounds":{"left":0.21284722,"top":0.09111111,"width":0.08541667,"height":0.021666666},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"567a470b-6751-47b2-902d-eb9b59075251","depth":21,"bounds":{"left":0.21284722,"top":0.112222224,"width":0.16493055,"height":0.018888889},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"69","depth":19,"bounds":{"left":0.48715279,"top":0.100555554,"width":0.010416667,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"January 26, 2026 - 05:58 PM","depth":19,"bounds":{"left":0.8753472,"top":0.100555554,"width":0.11597222,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"April 23, 2026 - 09:52 AM","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Mandy Metzing","depth":23,"bounds":{"left":0.21284722,"top":0.15111111,"width":0.071875,"height":0.021666666},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"4e2aa0aa-225c-4bbd-887b-35cf703acba6","depth":21,"bounds":{"left":0.21284722,"top":0.17277777,"width":0.15972222,"height":0.018888889},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"3","depth":19,"bounds":{"left":0.48715279,"top":0.16111112,"width":0.0052083335,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"March 12, 2026 - 07:17 AM","depth":19,"bounds":{"left":0.8753472,"top":0.16111112,"width":0.11145833,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"April 23, 2026 - 09:52 AM","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Alex Roberts","depth":23,"bounds":{"left":0.21284722,"top":0.21166667,"width":0.059027776,"height":0.021666666},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2d1af0e3-02ff-45a8-8e2e-bd7a547f1812","depth":21,"bounds":{"left":0.21284722,"top":0.23277777,"width":0.15590277,"height":0.018888889},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"46","depth":19,"bounds":{"left":0.48715279,"top":0.2211111,"width":0.010416667,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"January 26, 2026 - 09:36 AM","depth":19,"bounds":{"left":0.8753472,"top":0.2211111,"width":0.11631945,"height":0.02},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"April 23, 2026 - 09:52 AM","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
-8549153948277976439
|
-9009657863416601885
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
New Tab
New Tab
AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app
AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
Jiminny
Jiminny
Userpilot | QuickSearch-Searched
Userpilot | QuickSearch-Searched
Close tab
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Userpilot
Dashboards
Dashboards
People
People
Data
Data
Analytics
Analytics
Sessions
Sessions
Workflows
Workflows
Engagement
Engagement
Feedback
Feedback
Events
Events
Search engagement, feedback, reports, users and more
⌘K
Notifications
Help
Settings
Jiminny
Production
:r4l3: Active
QuickSearch-Searched
Active
more
Segment
Segment All users
All users
Company
Company All companies
All companies
Page
Page All pages
All pages
Time period
Time period Last 90 days
Last 90 days
Active users
5,345
/
8177
0%
Active companies
301
/
346
15.77%
Total events occurred
161,832
283.71%
Avg. occurrences per user
30
130.77%
Overview
Overview
:r4l5:
D
:r4l7:
W
:r4l9:
M
:r4lb:
:r4ld:
Key stat
Jan 2026 (BST)
Total events occurred
11,827
Unique users
1,932
Unique companies
226
Key stat
Total events occurred
Unique users
Unique companies
Jan 2026 (BST)
11,827
1,932
226
Starting from Jan 24, 2026
Click data point to view event properties
Last 90 days
Last 90 days
Previous Period
Previous Period
User activity
Search...
User
Total occurrences
First occurred
Last occurred
Zakari Hessas
954eeb0d-dd4f-41bd-bac7-515252f7a645
10
April 9, 2026 - 10:22 AM
April 23, 2026 - 09:54 AM
Matthew Hinawski
e423ddad-9cae-4385-b8e0-e51a6458f07a
98
January 26, 2026 - 09:44 AM
April 23, 2026 - 09:54 AM
Leone McEwan
d133f179-7d18-4f82-8371-390bba11ba18
66
January 26, 2026 - 02:07 PM
April 23, 2026 - 09:54 AM
Scarlett Fahey
778bf294-1468-4bea-bdb2-c26283f82983
91
January 26, 2026 - 08:58 AM
April 23, 2026 - 09:54 AM
Lewis Pamphilon
6977ef50-8b6d-46f6-b137-3690571e3df6
83
January 30, 2026 - 01:34 PM
April 23, 2026 - 09:53 AM
Sara Howe
b63514df-7a8e-4810-9267-cf637720db9f
52
January 27, 2026 - 07:52 AM
April 23, 2026 - 09:53 AM
Katie Kirtley-Jones
567a470b-6751-47b2-902d-eb9b59075251
69
January 26, 2026 - 05:58 PM
April 23, 2026 - 09:52 AM
Mandy Metzing
4e2aa0aa-225c-4bbd-887b-35cf703acba6
3
March 12, 2026 - 07:17 AM
April 23, 2026 - 09:52 AM
Alex Roberts
2d1af0e3-02ff-45a8-8e2e-bd7a547f1812
46
January 26, 2026 - 09:36 AM
April 23, 2026 - 09:52 AM...
|
NULL
|
|
12029
|
257
|
22
|
2026-04-14T10:47:06.478105+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-14/1776 /Users/lukas/.screenpipe/data/data/2026-04-14/1776163626478_m2.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
FirefoxFileEoitViewHistoryBookmarksProfilesToolsWi FirefoxFileEoitViewHistoryBookmarksProfilesToolsWindowHelpSupport Daily - in 1h 13mA100% CS•Tue 14 Apr 13:47:06us-east-2.console.aws.amazon.com/cloudwatch/home?region=us-east-2#logsV2:logs-insights$3FqueryDetailS3D~(end~O~start~-3600~timeType~'RELATIVE~tz~'UTC~unit~'seconds~editorString~'fields*20*40timestamp*2c*20*40message*2c*20*40logStream*2c*20*40log*0a*7c*20fil=awsSearch[Option+S]©United States (Ohio) *Account ID: 4387-4037-0364STAGE]0 EC2@ Elastic Container ServiceG 53* CodeDeployCa CloudWatchElasticache (03) Aurora and RDS iil Amazon OpenSearch Ser... ®) CloudFront ll MedialiveCloudWatch Logs Insights• Query definition Info5m30m1h3h12hCustomCompare (Off))UTC timezone?12 Start tallingQuery scopeLog groupsrroperty selectorLoc classAccountis.All log groupsQ All log groupsSTANDARDChange Accoun….All accounts X→ Wfields @timestamp, @message, @logStream, @log•muter chessade ulke:""Askyiminnvrevort:veneratel'tiiter emessage not ike /Anolyek/ ilte mnessage nt lik /Traangrip/Ilimit 10000Platform Sprint 1 Q2 - Platform TeaJY-20543 add AJ reports User piloZ Configure SSH access to multiple@ Console Home | Console Home | u:SecurityGroup | EC2 |us-east-2JY-20543 add AJ reports User piloSRD-6779 | JY-20632 | Unable toJy 19798 evaluation for ai activity(8) Jiminny8) Ask Jiminny test report - 8 Apr 20:- Service-Desk - Queues - PlatformC JY-20543 add AJ reports User pilc(x) Configure SSH access to multipleNew TabCa CloudWatch | us-east-2+ New TabLogs Insights QL Y72 Query generator@ Fields[ Saved and sample queries® Query commandsRun queryCancelSave)© Completed. Query executed for 147 log groups. ©Schedule queryHistoryLogs (-)Patterns (-)VisualizationLogs (-)# Summarize results) ( # Investigate ~Share resultsExport results Add to dashboardU9.oUUy.os10:0010:05Showing O of O records matched ®60,023 records (16.9 MB) scanned in 3.5s @ 17,243 records/s (4.9 MB/s)10:1010:1510.20No resultsRun a query to see related eventsHide histogram10:2510.3010.3010.4010:45CloudShellreedback© 2026, Amazon Web Services, Inc. or its affiliates.PrivacyTerms Cookie preferences...
|
NULL
|
-8549118175881791478
|
NULL
|
visual_change
|
ocr
|
NULL
|
FirefoxFileEoitViewHistoryBookmarksProfilesToolsWi FirefoxFileEoitViewHistoryBookmarksProfilesToolsWindowHelpSupport Daily - in 1h 13mA100% CS•Tue 14 Apr 13:47:06us-east-2.console.aws.amazon.com/cloudwatch/home?region=us-east-2#logsV2:logs-insights$3FqueryDetailS3D~(end~O~start~-3600~timeType~'RELATIVE~tz~'UTC~unit~'seconds~editorString~'fields*20*40timestamp*2c*20*40message*2c*20*40logStream*2c*20*40log*0a*7c*20fil=awsSearch[Option+S]©United States (Ohio) *Account ID: 4387-4037-0364STAGE]0 EC2@ Elastic Container ServiceG 53* CodeDeployCa CloudWatchElasticache (03) Aurora and RDS iil Amazon OpenSearch Ser... ®) CloudFront ll MedialiveCloudWatch Logs Insights• Query definition Info5m30m1h3h12hCustomCompare (Off))UTC timezone?12 Start tallingQuery scopeLog groupsrroperty selectorLoc classAccountis.All log groupsQ All log groupsSTANDARDChange Accoun….All accounts X→ Wfields @timestamp, @message, @logStream, @log•muter chessade ulke:""Askyiminnvrevort:veneratel'tiiter emessage not ike /Anolyek/ ilte mnessage nt lik /Traangrip/Ilimit 10000Platform Sprint 1 Q2 - Platform TeaJY-20543 add AJ reports User piloZ Configure SSH access to multiple@ Console Home | Console Home | u:SecurityGroup | EC2 |us-east-2JY-20543 add AJ reports User piloSRD-6779 | JY-20632 | Unable toJy 19798 evaluation for ai activity(8) Jiminny8) Ask Jiminny test report - 8 Apr 20:- Service-Desk - Queues - PlatformC JY-20543 add AJ reports User pilc(x) Configure SSH access to multipleNew TabCa CloudWatch | us-east-2+ New TabLogs Insights QL Y72 Query generator@ Fields[ Saved and sample queries® Query commandsRun queryCancelSave)© Completed. Query executed for 147 log groups. ©Schedule queryHistoryLogs (-)Patterns (-)VisualizationLogs (-)# Summarize results) ( # Investigate ~Share resultsExport results Add to dashboardU9.oUUy.os10:0010:05Showing O of O records matched ®60,023 records (16.9 MB) scanned in 3.5s @ 17,243 records/s (4.9 MB/s)10:1010:1510.20No resultsRun a query to see related eventsHide histogram10:2510.3010.3010.4010:45CloudShellreedback© 2026, Amazon Web Services, Inc. or its affiliates.PrivacyTerms Cookie preferences...
|
12028
|
|
57667
|
1239
|
66
|
2026-04-20T12:01:50.792633+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776686510792_m2.jpg...
|
Firefox
|
Firefox
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormVIewINavicatecodeFV faVsco.js?9 master~Pro PhostormVIewINavicatecodeFV faVsco.js?9 master~Proleteyphp artisan* comnoser ison# composer.lock# dependency-checker.jsonJ dev.jsonE ids.txtEinfection.json.distMLINSTAlI mdM+ INTERNAL_WEBHOOK_SETUP.mdEjiminny storageM+licenses.mom Makerileраскаqе-lock. sonE phostan.neon.dist= phostan-baseline.neon<› phounit.xmliTe raw sal querv.saM+ README.mdso sonar-oroiect.oroperties= testou<> Untited Diadram.xmlus vetur.config.jsMJ WEBHOOK FILTERING IMPLEMENTATION.mo› ib External Librariesv = Scratches and Consolesv M Database ConsolesA console (EU)A DEAL RISKS (EU]A DI (EU]A EU (EU]vAjiminny@localhostA console (jiminny@localhost]A DI jiminny@localhost]A HS local fiiminny@localhosti&sr liminnyolocalnost& zoho dev liminny@localhostV APROD& console PRODII& console 1 PRODIL DI PRODIIA QAI> A OAIPRODSTAGINGA console [STAGINGIA console 1 STAGiNG)# uranus STAGINGI>• Extensions) M ScratchesKeтactolC) ActivityController.pnpJiminnybeougcommana.onpC) AutomatedReportsRepository.pnp© AutomatedReportsCommand.phpUpaatencuviyclasticsearenDocumentcommana.onpUpdaterlasticsearch.phpx© TrackProviderInstalledEvent.phpC) AutomatedReportkesult.png37 €394651 6iclass Updatezlasticsearch extends Iterateactivitles5761s58 0 >7e 6 >--activitvid=0?'"--FromActivitvideds'--toActivitvidear--usenTd=0}'S--teamTd=0}|S--fromTime=A},S--toTime=0}"s--fromlindateTime=,ce-tolindatoTimo=Seconden=ASC}:{--chunk-size=}':* Include sort-delered entries. We need those to make sure theu are not found in elastzcsearch.* avar boolprotected nool swithirashed = true:* Render a progress bar. so we know what the current progress is.* avar boolno usadesorotected SwithProaressBar = true.* The console command descrintion.* Ovar stringprotected $description = 'Update activities in ES':protected function with: array{...;protected function onActivities(Collection Sactivities): void{...}protected function onActivity(Activity Sactivity): void{...}=custom.logElaravel.log SF [jiminny@localhost]« HS_local [jiminny@localhost]& console [PROD] X# console leu)|p)|PlavaroundSELECT * FROM activities WHERE 10 = 16822967:SELECT * FROM crm orofiles WHERE user 1d = 15440%SELECT * FROM crm_profiles WHERE crm_configuration_id = 555;SELECT * FROM crm confiqurations WHERE 1d = 555:1SELECT * FROM users WHERE id = 15440; # team. 581, gr. 15440, pl. 3911, act. field 162182SELECTICONCAT(u.id, CASE WHEN V.id = t.owner_id THEN' (owner)' ELSE '' END) AS user_id,u.email,sd.xt.owner_id FROM social_accounts saJOIN users u on u.id = sa.sociable_idJOIN teams t 1.n<->1: on t.id = u.team_idWHERE u.team_id = 581 and sa.provider = 'salesforce':SELECT * FROM automated report results order by id desc:select * from teaturesselect * trom team teatures where teature 10 = 401select * trom teams whereselect * from automated_reports where 1d = 54; # 4tdd41f6-act0-50a0-[CREDIT_CARD],"odt", "podcast"SELECT * FROM automated report results WHERE UUid to bin 822f2410-af05-4529-2248-8600e36f3131') = uuid:select * from automated_report_results order by id desc;SELECT * FROM automated report results WHERE id = 1919:select * from automated_report_results WHERE report id = 54select * from onnortunities where id = 7594349•SELECT * FROM teams WHERE name LTKE 1%Les%'• # 711. 692. 160678-TiminnvintearationGlesmills.comselect * from playbooks where team_id = 711; # event 226147SELECT * SROM nlavbook cateaories WHERE nlavbook 1d = 5515÷SELECT * FROM crm_fields WHERE id = 226147;IcElECT * CP0M eom fiold valuec WHEPE com fiold 1d = 22/147.SELSCT * CP0M eom confiaunatione wHEPE id = 402.seiseCONCAT(u.id, CASE WHEN u.id = t.owner id THEN ' (owner)' ELSE '* END) AS user idU.emall,• sa.*t.owner id FROM social accounts saJOIN users u on u.id = sa.sociable_ idJOIN teams t 1.n<->1: on t.id = u.team_idWHERE u.team_ id = 711 and sa.provider = 'salesforce':« console [STAGING]100% 52• Mon ZU AOr 10.01:00So jiminny034 A1 A34 V 62 ^NN Windeurf Toam...
|
NULL
|
-8548344084020329819
|
NULL
|
click
|
ocr
|
NULL
|
PhostormVIewINavicatecodeFV faVsco.js?9 master~Pro PhostormVIewINavicatecodeFV faVsco.js?9 master~Proleteyphp artisan* comnoser ison# composer.lock# dependency-checker.jsonJ dev.jsonE ids.txtEinfection.json.distMLINSTAlI mdM+ INTERNAL_WEBHOOK_SETUP.mdEjiminny storageM+licenses.mom Makerileраскаqе-lock. sonE phostan.neon.dist= phostan-baseline.neon<› phounit.xmliTe raw sal querv.saM+ README.mdso sonar-oroiect.oroperties= testou<> Untited Diadram.xmlus vetur.config.jsMJ WEBHOOK FILTERING IMPLEMENTATION.mo› ib External Librariesv = Scratches and Consolesv M Database ConsolesA console (EU)A DEAL RISKS (EU]A DI (EU]A EU (EU]vAjiminny@localhostA console (jiminny@localhost]A DI jiminny@localhost]A HS local fiiminny@localhosti&sr liminnyolocalnost& zoho dev liminny@localhostV APROD& console PRODII& console 1 PRODIL DI PRODIIA QAI> A OAIPRODSTAGINGA console [STAGINGIA console 1 STAGiNG)# uranus STAGINGI>• Extensions) M ScratchesKeтactolC) ActivityController.pnpJiminnybeougcommana.onpC) AutomatedReportsRepository.pnp© AutomatedReportsCommand.phpUpaatencuviyclasticsearenDocumentcommana.onpUpdaterlasticsearch.phpx© TrackProviderInstalledEvent.phpC) AutomatedReportkesult.png37 €394651 6iclass Updatezlasticsearch extends Iterateactivitles5761s58 0 >7e 6 >--activitvid=0?'"--FromActivitvideds'--toActivitvidear--usenTd=0}'S--teamTd=0}|S--fromTime=A},S--toTime=0}"s--fromlindateTime=,ce-tolindatoTimo=Seconden=ASC}:{--chunk-size=}':* Include sort-delered entries. We need those to make sure theu are not found in elastzcsearch.* avar boolprotected nool swithirashed = true:* Render a progress bar. so we know what the current progress is.* avar boolno usadesorotected SwithProaressBar = true.* The console command descrintion.* Ovar stringprotected $description = 'Update activities in ES':protected function with: array{...;protected function onActivities(Collection Sactivities): void{...}protected function onActivity(Activity Sactivity): void{...}=custom.logElaravel.log SF [jiminny@localhost]« HS_local [jiminny@localhost]& console [PROD] X# console leu)|p)|PlavaroundSELECT * FROM activities WHERE 10 = 16822967:SELECT * FROM crm orofiles WHERE user 1d = 15440%SELECT * FROM crm_profiles WHERE crm_configuration_id = 555;SELECT * FROM crm confiqurations WHERE 1d = 555:1SELECT * FROM users WHERE id = 15440; # team. 581, gr. 15440, pl. 3911, act. field 162182SELECTICONCAT(u.id, CASE WHEN V.id = t.owner_id THEN' (owner)' ELSE '' END) AS user_id,u.email,sd.xt.owner_id FROM social_accounts saJOIN users u on u.id = sa.sociable_idJOIN teams t 1.n<->1: on t.id = u.team_idWHERE u.team_id = 581 and sa.provider = 'salesforce':SELECT * FROM automated report results order by id desc:select * from teaturesselect * trom team teatures where teature 10 = 401select * trom teams whereselect * from automated_reports where 1d = 54; # 4tdd41f6-act0-50a0-[CREDIT_CARD],"odt", "podcast"SELECT * FROM automated report results WHERE UUid to bin 822f2410-af05-4529-2248-8600e36f3131') = uuid:select * from automated_report_results order by id desc;SELECT * FROM automated report results WHERE id = 1919:select * from automated_report_results WHERE report id = 54select * from onnortunities where id = 7594349•SELECT * FROM teams WHERE name LTKE 1%Les%'• # 711. 692. 160678-TiminnvintearationGlesmills.comselect * from playbooks where team_id = 711; # event 226147SELECT * SROM nlavbook cateaories WHERE nlavbook 1d = 5515÷SELECT * FROM crm_fields WHERE id = 226147;IcElECT * CP0M eom fiold valuec WHEPE com fiold 1d = 22/147.SELSCT * CP0M eom confiaunatione wHEPE id = 402.seiseCONCAT(u.id, CASE WHEN u.id = t.owner id THEN ' (owner)' ELSE '* END) AS user idU.emall,• sa.*t.owner id FROM social accounts saJOIN users u on u.id = sa.sociable_ idJOIN teams t 1.n<->1: on t.id = u.team_idWHERE u.team_ id = 711 and sa.provider = 'salesforce':« console [STAGING]100% 52• Mon ZU AOr 10.01:00So jiminny034 A1 A34 V 62 ^NN Windeurf Toam...
|
57660
|
|
52238
|
1128
|
47
|
2026-04-20T06:46:19.402307+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776667579402_m1.jpg...
|
Firefox
|
Meet - Daily - Platform — Work
|
True
|
meet.google.com/agt-teir-cwt?authuser=lukas.kovali meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.com...
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Meet - Daily - Platform
Close tab
New Tab
Open Goo Meet - Daily - Platform
Close tab
New Tab
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Customize sidebar
People
6
Take notes with Gemini
Take notes with Gemini
Gemini
Gemini
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
Pin Nikolay Yankov to your main screen
Mute Nikolay Yankov's microphone
More options for Nikolay Yankov
Nikolay Yankov
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
Pin Steliyan Georgiev to your main screen
You can't unmute someone else
More options for Steliyan Georgiev
Steliyan Georgiev
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
Pin Nikolay Nikolov to your main screen
You can't unmute someone else
More options for Nikolay Nikolov
Nikolay Nikolov
Pin Galya Dimitrova to your main screen...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Meet - Daily - Platform","depth":4,"bounds":{"left":0.0,"top":0.072222225,"width":0.033680554,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.0013888889,"top":0.072222225,"width":0.010416667,"height":0.016666668},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.005902778,"top":0.12,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"bounds":{"left":0.0,"top":0.7977778,"width":0.033680554,"height":0.043333333},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.0,"top":0.8411111,"width":0.033680554,"height":0.038333334},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.0,"top":0.8794444,"width":0.033680554,"height":0.03888889},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.0,"top":0.91833335,"width":0.033680554,"height":0.038333334},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0,"top":0.95666665,"width":0.033680554,"height":0.043333333},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"People","depth":15,"bounds":{"left":0.88680553,"top":0.08722222,"width":0.04097222,"height":0.04},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6","depth":22,"bounds":{"left":0.9145833,"top":0.09888889,"width":0.0048611113,"height":0.017222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Take notes with Gemini","depth":14,"bounds":{"left":0.93333334,"top":0.08722222,"width":0.025,"height":0.04},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Take notes with Gemini","depth":17,"bounds":{"left":0.9361111,"top":0.09888889,"width":0.06388891,"height":0.017222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini","depth":22,"bounds":{"left":0.96666664,"top":0.09888889,"width":0.028125,"height":0.017222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Gemini","depth":21,"bounds":{"left":0.96458334,"top":0.08833333,"width":0.023611112,"height":0.037777778},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Pop out this video More screens are more fun. Play this video while you do other things.","depth":15,"bounds":{"left":0.25416666,"top":0.3638889,"width":0.14652778,"height":0.07722222},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pop out this video","depth":17,"bounds":{"left":0.3982639,"top":0.37888888,"width":0.07569444,"height":0.017777778},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"More screens are more fun. Play this video while you do other things.","depth":16,"bounds":{"left":0.37604168,"top":0.37444445,"width":0.11736111,"height":0.045},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Pin Nikolay Yankov to your main screen","depth":13,"bounds":{"left":0.15729167,"top":0.3122222,"width":0.027777778,"height":0.044444446},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Mute Nikolay Yankov's microphone","depth":13,"bounds":{"left":0.18506944,"top":0.31,"width":0.030555556,"height":0.04888889},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"More options for Nikolay Yankov","depth":13,"bounds":{"left":0.215625,"top":0.3122222,"width":0.027777778,"height":0.044444446},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Nikolay Yankov","depth":17,"bounds":{"left":0.057291668,"top":0.48277777,"width":0.07673611,"height":0.022777777},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Pop out this video More screens are more fun. Play this video while you do other things.","depth":15,"bounds":{"left":0.57361114,"top":0.3638889,"width":0.14652778,"height":0.07722222},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pop out this video","depth":17,"bounds":{"left":0.71770835,"top":0.37888888,"width":0.07569444,"height":0.017777778},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"More screens are more fun. Play this video while you do other things.","depth":16,"bounds":{"left":0.6954861,"top":0.37444445,"width":0.11736111,"height":0.045},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Pin Steliyan Georgiev to your main screen","depth":13,"bounds":{"left":0.47430557,"top":0.3122222,"width":0.027777778,"height":0.044444446},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"You can't unmute someone else","depth":13,"bounds":{"left":0.50208336,"top":0.31,"width":0.030555556,"height":0.04888889},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"More options for Steliyan Georgiev","depth":13,"bounds":{"left":0.5326389,"top":0.3122222,"width":0.027777778,"height":0.044444446},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Steliyan Georgiev","depth":17,"bounds":{"left":0.37395832,"top":0.48277777,"width":0.090625,"height":0.022777777},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Pop out this video More screens are more fun. Play this video while you do other things.","depth":15,"bounds":{"left":0.88819444,"top":0.3638889,"width":0.11180556,"height":0.07722222},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pop out this video","depth":17,"bounds":{"left":1.0,"top":0.37888888,"width":-0.03229165,"height":0.017777778},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"More screens are more fun. Play this video while you do other things.","depth":16,"bounds":{"left":1.0,"top":0.37444445,"width":-0.0100694895,"height":0.045},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Pin Nikolay Nikolov to your main screen","depth":13,"bounds":{"left":0.79131943,"top":0.3122222,"width":0.027777778,"height":0.044444446},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"You can't unmute someone else","depth":13,"bounds":{"left":0.8190972,"top":0.31,"width":0.030555556,"height":0.04888889},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"More options for Nikolay Nikolov","depth":13,"bounds":{"left":0.84965277,"top":0.3122222,"width":0.027777778,"height":0.044444446},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Nikolay Nikolov","depth":17,"bounds":{"left":0.69131947,"top":0.48277777,"width":0.07847222,"height":0.022777777},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Pin Galya Dimitrova to your main screen","depth":13,"bounds":{"left":0.15729167,"top":0.70111114,"width":0.027777778,"height":0.044444446},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-8548015033268573994
|
-6605688573680485616
|
click
|
hybrid
|
NULL
|
Meet - Daily - Platform
Close tab
New Tab
Open Goo Meet - Daily - Platform
Close tab
New Tab
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Customize sidebar
People
6
Take notes with Gemini
Take notes with Gemini
Gemini
Gemini
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
Pin Nikolay Yankov to your main screen
Mute Nikolay Yankov's microphone
More options for Nikolay Yankov
Nikolay Yankov
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
Pin Steliyan Georgiev to your main screen
You can't unmute someone else
More options for Steliyan Georgiev
Steliyan Georgiev
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
Pin Nikolay Nikolov to your main screen
You can't unmute someone else
More options for Nikolay Nikolov
Nikolay Nikolov
Pin Galya Dimitrova to your main screen
Firefox•FileEditViewHistoryBookmarksProfiles→CToolsWindowHelp<lahlmeet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.comDaily - Platform • now100% <478•Mon 20 Apr 9:46:206+Nikolay YankovSteliyan GeorgievNikolay Nikolov*Galya DimitrovaU Permission needed4 Permission neededs Kovalik9:46 AM | Daily - Platform1:01...
|
NULL
|
|
54193
|
1169
|
8
|
2026-04-20T08:43:28.610478+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776674608610_m1.jpg...
|
Slack
|
Huddle: @Aneliya Angelova - Jiminny Inc - Slack
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Aneliya Angelova
Aneliya Angelova, Screen share
An Aneliya Angelova
Aneliya Angelova, Screen share
Aneliya Angelova
Screen share
Turn on drawing
Fullscreen
Back to grid
View Lukas Kovalik's profile
video is off, audio is on
More actions
Audio
View Aneliya Angelova's profile
Aneliya Angelova
More actions
Audio
View Galya Dimitrova's profile
Galya Dimitrova
More actions
Audio
Aneliya Angelova is in the huddle.: .
Share your screen...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Aneliya Angelova","depth":14,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Aneliya Angelova, Screen share","depth":14,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Aneliya Angelova","depth":16,"role_description":"text"},{"role":"AXStaticText","text":"Screen share","depth":15,"role_description":"text"},{"role":"AXButton","text":"Turn on drawing","depth":15,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Fullscreen","depth":15,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Back to grid","depth":15,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCell","text":"View Lukas Kovalik's profile","depth":11,"role_description":"cell"},{"role":"AXButton","text":"video is off, audio is on","depth":12,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More actions","depth":12,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"role_description":"text"},{"role":"AXCell","text":"View Aneliya Angelova's profile","depth":11,"role_description":"cell"},{"role":"AXStaticText","text":"Aneliya Angelova","depth":13,"role_description":"text"},{"role":"AXPopUpButton","text":"More actions","depth":12,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"role_description":"text"},{"role":"AXCell","text":"View Galya Dimitrova's profile","depth":11,"role_description":"cell"},{"role":"AXStaticText","text":"Galya Dimitrova","depth":13,"role_description":"text"},{"role":"AXPopUpButton","text":"More actions","depth":12,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"role_description":"text"},{"role":"AXStaticText","text":"Aneliya Angelova is in the huddle.: .","depth":10,"bounds":{"left":0.4798611,"top":0.0,"width":0.04097222,"height":0.0011111111},"role_description":"text"},{"role":"AXStaticText","text":"Share your screen","depth":12,"bounds":{"left":0.92430556,"top":0.0,"width":0.07152778,"height":0.017777778},"role_description":"text"}]...
|
-8547944451951971235
|
-5968586307401395313
|
app_switch
|
hybrid
|
NULL
|
Aneliya Angelova
Aneliya Angelova, Screen share
An Aneliya Angelova
Aneliya Angelova, Screen share
Aneliya Angelova
Screen share
Turn on drawing
Fullscreen
Back to grid
View Lukas Kovalik's profile
video is off, audio is on
More actions
Audio
View Aneliya Angelova's profile
Aneliya Angelova
More actions
Audio
View Galya Dimitrova's profile
Galya Dimitrova
More actions
Audio
Aneliya Angelova is in the huddle.: .
Share your screen
FirefoxFileEditViewHistoryBookmarksProfilesTools Window Help→app.staging.jiminny.com/ai-reportsAl ReportsReport namePeriodJY-20553 | Improve crm-sync dela[SRD-6793] Les Mills activity typeJY-20698 handle failed field syncJY-20692 change confirmation pal[JY-20543] AJ Reports > Tracking(JY-18909) [Part2) Automated repAsk Jiminny Reports by nikolay-yarNew TabProduct Growth Platform | UserpiloUserpilot | Logged-activityfix(security): composer dependencPipelines - jiminny/appFeed - jiminny - Sentryfix(security): composer dependencJiminnyJiminny+New TabNAME105Health - 9 - 15 Apr 2026Tuesday Report - 15 Apr 2026Ask Jiminny Test Report - 15 Apr 2026Eastern Summary - 7 - 13 Apr 2026Tuesday Report - 13 Apr 2026Ask Jiminny Test Report - 13 Apr 2026Ask Jiminny Test Report - 13 Apr 2026JY-18909-automated-reports-ask-jiminny = 873114Report TypeFREQUENCYWeeklyDailyDailyWeeklyDailyDailyDaily‹$0100% C8• Mon 20 Apr 11:43:28{03 Ask Jiminny reports© Clear all |SHARED|Kamі +1SchuSchu-LamLKanyDATE Y16/04/202616/04/202616/04/202614/04/202614/04/202614/04/202614/04/2026ACTIONSCooFLeorLeor• Сorш• СоШ• C ш...
|
NULL
|
|
54195
|
1170
|
13
|
2026-04-20T08:43:30.246953+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776674610246_m2.jpg...
|
Slack
|
Huddle: @Aneliya Angelova - Jiminny Inc - Slack
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Aneliya Angelova
Aneliya Angelova, Screen share
An Aneliya Angelova
Aneliya Angelova, Screen share
Aneliya Angelova
Screen share
Turn on drawing
Fullscreen
Back to grid
View Lukas Kovalik's profile
video is off, audio is on
More actions
Audio
View Aneliya Angelova's profile
Aneliya Angelova
More actions
Audio
View Galya Dimitrova's profile
Galya Dimitrova
More actions
Audio
Aneliya Angelova is in the huddle.: .
Share your screen...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Aneliya Angelova","depth":14,"bounds":{"left":0.50398934,"top":0.049481247,"width":0.011968086,"height":0.033519555},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Aneliya Angelova, Screen share","depth":14,"bounds":{"left":0.5199468,"top":0.049481247,"width":0.03856383,"height":0.017557861},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Aneliya Angelova","depth":16,"bounds":{"left":0.5199468,"top":0.051077414,"width":0.03856383,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Screen share","depth":15,"bounds":{"left":0.5199468,"top":0.06783719,"width":0.024268618,"height":0.012769354},"role_description":"text"},{"role":"AXButton","text":"Turn on drawing","depth":15,"bounds":{"left":0.90791225,"top":0.0518755,"width":0.011968086,"height":0.028731046},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Fullscreen","depth":15,"bounds":{"left":0.91988033,"top":0.0518755,"width":0.011968086,"height":0.028731046},"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Back to grid","depth":15,"bounds":{"left":0.9318484,"top":0.0518755,"width":0.011968086,"height":0.028731046},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCell","text":"View Lukas Kovalik's profile","depth":11,"bounds":{"left":0.9478058,"top":0.31683958,"width":0.049867023,"height":0.11971269},"role_description":"cell"},{"role":"AXButton","text":"video is off, audio is on","depth":12,"bounds":{"left":0.95046544,"top":0.40782124,"width":0.03158245,"height":0.022346368},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More actions","depth":12,"bounds":{"left":0.984375,"top":0.32322428,"width":0.010638298,"height":0.025538707},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"bounds":{"left":0.97273934,"top":0.31444532,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXCell","text":"View Aneliya Angelova's profile","depth":11,"bounds":{"left":0.9478058,"top":0.44134077,"width":0.049867023,"height":0.11971269},"role_description":"cell"},{"role":"AXStaticText","text":"Aneliya Angelova","depth":13,"bounds":{"left":0.953125,"top":0.5371109,"width":0.03324468,"height":0.012769354},"role_description":"text"},{"role":"AXPopUpButton","text":"More actions","depth":12,"bounds":{"left":0.984375,"top":0.44772545,"width":0.010638298,"height":0.025538707},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"bounds":{"left":0.97273934,"top":0.43894652,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXCell","text":"View Galya Dimitrova's profile","depth":11,"bounds":{"left":0.9478058,"top":0.565842,"width":0.049867023,"height":0.11971269},"role_description":"cell"},{"role":"AXStaticText","text":"Galya Dimitrova","depth":13,"bounds":{"left":0.96110374,"top":0.66161215,"width":0.029920213,"height":0.027134877},"role_description":"text"},{"role":"AXPopUpButton","text":"More actions","depth":12,"bounds":{"left":0.984375,"top":0.57222664,"width":0.010638298,"height":0.025538707},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"bounds":{"left":0.97273934,"top":0.5634477,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Aneliya Angelova is in the huddle.: .","depth":10,"bounds":{"left":0.5,"top":0.9992019,"width":0.019614361,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Share your screen","depth":12,"bounds":{"left":0.71276593,"top":0.9353551,"width":0.034242023,"height":0.012769354},"role_description":"text"}]...
|
-8547944451951971235
|
-5968586307401395313
|
visual_change
|
hybrid
|
NULL
|
Aneliya Angelova
Aneliya Angelova, Screen share
An Aneliya Angelova
Aneliya Angelova, Screen share
Aneliya Angelova
Screen share
Turn on drawing
Fullscreen
Back to grid
View Lukas Kovalik's profile
video is off, audio is on
More actions
Audio
View Aneliya Angelova's profile
Aneliya Angelova
More actions
Audio
View Galya Dimitrova's profile
Galya Dimitrova
More actions
Audio
Aneliya Angelova is in the huddle.: .
Share your screen
slackActivityLaterVIewmistonWindowJiminny ...& Starred& jiminny-x-integrati...8 platform-inner-team• Channels*al-chapter¿ 8. Aneliya Angelova @• MessagesLo Add canvasr Filesi alerts# backendcontusion-cllnie# curiosity_lab# engineerins# frontend# general# infra-changes# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the people of jimi..A Direct meccagpcP. A...М9 д3P. Galya Dimitrova. Stefka StoyanovaR?. Stoyan Tomov3 Aneliya Angelova,..P. Nikolay Nikolov&. Stoyan Tanev€. Vasil Vasilev. Nikolay Ivanov8. Ves:: AppsS Jira CloudLukas Kovallik 9:36 AMThursdav. April 16thvAneliya Angelova 10:00 AMЛукаш кога искаш да се чуемза командитеLukas Kovalik 10:01 AMайле слел 15 мин че се зарових в зохоLukas Kovalik • 10:32 AMAneliya Angelova 10:33 AMA huddle happened 10:33 AMukas Kovalk ' 10-41AMphp artisan automated-reports --report-id 39php artisan automated-reports:send --result-id 64oday"Aneliya Angelova 9:41 AMLukas Kovalik M 10:31 AMАни след малко може да се чуемAneliva Angelova M 11.27 AMнаплаво зеьнни когато можешLukas Kovalik & 11:35 AMЗдрасти Лукаш, струва ми се, че репортите се генерират върху всички активитита със съответния Saved search bez da e dobawen filtyr za dataYou ioined the huddle LIVE 11:35 AMAneliva Angelova and Galva Dimitrova are here too.Galva Dimitrova won't be able to see messages sent in thread because this huddle began in a direct message. You can start a new huddle instead.Message Aneliva Angeloval+ Aae6 Huddle with Aneliya AngelovaAl Notes: OffLeaveAneliva AngelovaScreen shareSlack File Edit View*E AlNotes: O#fAneliya AngelovaGo History Window Help100% Lz6• Mon ZU A0r 11.43.496 Huddle with Aneliya Angelova6 Huddle with Lukas Kovalil8• © Mon 20 Apr 11:43Thread@ Every huddle has a threadsend messages. nies, and links to everyone in themessage with @lukas Kovalik, so you can access.& Lukas Kovalik has paused their notificationsReplly...O Aso send as direct message+ Aа @@ ••.Shars vour ceroonLeave...
|
54194
|
|
54197
|
1170
|
15
|
2026-04-20T08:43:54.488152+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776674634488_m2.jpg...
|
Slack
|
Huddle: @Aneliya Angelova - Jiminny Inc - Slack
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Aneliya Angelova
Aneliya Angelova, Screen share
An Aneliya Angelova
Aneliya Angelova, Screen share
Aneliya Angelova
Screen share
Turn on drawing
Fullscreen
Back to grid
View Lukas Kovalik's profile
video is off, audio is on
More actions
Audio
View Aneliya Angelova's profile
Aneliya Angelova
More actions
Audio
View Galya Dimitrova's profile
Galya Dimitrova
More actions
Audio
Aneliya Angelova is in the huddle.: .
Share your screen...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Aneliya Angelova","depth":14,"bounds":{"left":0.50398934,"top":0.049481247,"width":0.011968086,"height":0.033519555},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Aneliya Angelova, Screen share","depth":14,"bounds":{"left":0.5199468,"top":0.049481247,"width":0.03856383,"height":0.017557861},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Aneliya Angelova","depth":16,"bounds":{"left":0.5199468,"top":0.051077414,"width":0.03856383,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Screen share","depth":15,"bounds":{"left":0.5199468,"top":0.06783719,"width":0.024268618,"height":0.012769354},"role_description":"text"},{"role":"AXButton","text":"Turn on drawing","depth":15,"bounds":{"left":0.90791225,"top":0.0518755,"width":0.011968086,"height":0.028731046},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Fullscreen","depth":15,"bounds":{"left":0.91988033,"top":0.0518755,"width":0.011968086,"height":0.028731046},"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Back to grid","depth":15,"bounds":{"left":0.9318484,"top":0.0518755,"width":0.011968086,"height":0.028731046},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCell","text":"View Lukas Kovalik's profile","depth":11,"bounds":{"left":0.9478058,"top":0.31683958,"width":0.049867023,"height":0.11971269},"role_description":"cell"},{"role":"AXButton","text":"video is off, audio is on","depth":12,"bounds":{"left":0.95046544,"top":0.40782124,"width":0.03158245,"height":0.022346368},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More actions","depth":12,"bounds":{"left":0.984375,"top":0.32322428,"width":0.010638298,"height":0.025538707},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"bounds":{"left":0.97273934,"top":0.31444532,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXCell","text":"View Aneliya Angelova's profile","depth":11,"bounds":{"left":0.9478058,"top":0.44134077,"width":0.049867023,"height":0.11971269},"role_description":"cell"},{"role":"AXStaticText","text":"Aneliya Angelova","depth":13,"bounds":{"left":0.953125,"top":0.5371109,"width":0.03324468,"height":0.012769354},"role_description":"text"},{"role":"AXPopUpButton","text":"More actions","depth":12,"bounds":{"left":0.984375,"top":0.44772545,"width":0.010638298,"height":0.025538707},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"bounds":{"left":0.97273934,"top":0.43894652,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXCell","text":"View Galya Dimitrova's profile","depth":11,"bounds":{"left":0.9478058,"top":0.565842,"width":0.049867023,"height":0.11971269},"role_description":"cell"},{"role":"AXStaticText","text":"Galya Dimitrova","depth":13,"bounds":{"left":0.96110374,"top":0.66161215,"width":0.029920213,"height":0.027134877},"role_description":"text"},{"role":"AXPopUpButton","text":"More actions","depth":12,"bounds":{"left":0.984375,"top":0.57222664,"width":0.010638298,"height":0.025538707},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"bounds":{"left":0.97273934,"top":0.5634477,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Aneliya Angelova is in the huddle.: .","depth":10,"bounds":{"left":0.5,"top":0.9992019,"width":0.019614361,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Share your screen","depth":12,"bounds":{"left":0.71276593,"top":0.9353551,"width":0.034242023,"height":0.012769354},"role_description":"text"}]...
|
-8547944451951971235
|
-5968586307401395313
|
visual_change
|
hybrid
|
NULL
|
Aneliya Angelova
Aneliya Angelova, Screen share
An Aneliya Angelova
Aneliya Angelova, Screen share
Aneliya Angelova
Screen share
Turn on drawing
Fullscreen
Back to grid
View Lukas Kovalik's profile
video is off, audio is on
More actions
Audio
View Aneliya Angelova's profile
Aneliya Angelova
More actions
Audio
View Galya Dimitrova's profile
Galya Dimitrova
More actions
Audio
Aneliya Angelova is in the huddle.: .
Share your screen
slackActivityLaterVIewJiminny ...& Starred& jiminny-x-integrati...8 platform-inner-team• Channels*al-chapteri alerts# backendcontusion-cllnie# curiosity_lab# engineerins# frontend# general# infra-changes# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the people of jimi..A Direct meccagpcP. A...М9 д3P. Galya Dimitrova. Stefka StoyanovaR?. Stoyan Tomov3 Aneliya Angelova,..P. Nikolay Nikolov&. Stoyan Tanev€. Vasil Vasilev. Nikolay Ivanov8. Ves:: AppsS Jira Cloud6 Huddle with Aneliya AngelovaMistonWindow¿ 8. Aneliya Angelova @• MessagesLo Add canvasr FilesLukas Kovallik 9:36 AM100% C28• Mon 20 Apr 11:43:546 Huddle with Aneliya AngelovaAneliya AngelovaScreen shareThursdav. April 16thvHistoryBookmarks ProfilesTab Window8•• Mon 20 Apr 11:43Expires On - xE Expires OnExpires Onexpires OnP Expires On - x |expires OnP Expires On -Circleclapp.staging.jiminny.com/ondemand?topic_id%5B%5D=054ecf28-f103-433f-а69c-fce7d0677f5b&tоpic_jd%5B%5D=43b58441-а208-4cda-bbff-39f4d9548cf1&…• WorkAneliya Angelova ® 10:00 AMЛукаш кога искаш да се чуемза командитеLukas Kovalik 10:01 AMайле слел 15 мин че се зарових в зохоLukas Kovalik • 10:32 AMAneliya Angelova 10:33 AMA huddle happened 10:33 AMukas Kovalk ' 10-41AMphp artisan automated-reports --report-id 39php artisan automated-reports:send --result-id 64Todav vAneliya Angelova 9:41 AMLukas Kovalik 10.31 AMАни след малко може да се чуемЗдрасти Лукаш, струва ми се, че репортите се генерират върху всички активитита със съответния Saved search bez da e dobawen filtyr za dataAneliva Angelova M 11.27 AMнапрaво зеьнни когато можешіLukas Kovalik 11:35 AMYou ioined the hudcle LIVE 11:35 AMAneliva Angelova and Galva Dimitrova are here too.Galva Dimitrova won't be able to see messages sent in thread because this huddle began in a direct message. You can start a new huddle instead.Message Aneliva Angeloval+ Aae10Al Notes: OffLeave&Shars vour ceroonLeave...
|
54196
|
|
54198
|
1169
|
9
|
2026-04-20T08:43:59.345069+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776674639345_m1.jpg...
|
Slack
|
Huddle: @Aneliya Angelova - Jiminny Inc - Slack
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Aneliya Angelova
Aneliya Angelova, Screen share
An Aneliya Angelova
Aneliya Angelova, Screen share
Aneliya Angelova
Screen share
Turn on drawing
Fullscreen
Back to grid
View Lukas Kovalik's profile
video is off, audio is on
More actions
Audio
View Aneliya Angelova's profile
Aneliya Angelova
More actions
Audio
View Galya Dimitrova's profile
Galya Dimitrova
More actions
Audio
Aneliya Angelova is in the huddle.: .
Share your screen...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Aneliya Angelova","depth":14,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Aneliya Angelova, Screen share","depth":14,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Aneliya Angelova","depth":16,"role_description":"text"},{"role":"AXStaticText","text":"Screen share","depth":15,"role_description":"text"},{"role":"AXButton","text":"Turn on drawing","depth":15,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Fullscreen","depth":15,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Back to grid","depth":15,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCell","text":"View Lukas Kovalik's profile","depth":11,"role_description":"cell"},{"role":"AXButton","text":"video is off, audio is on","depth":12,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More actions","depth":12,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"role_description":"text"},{"role":"AXCell","text":"View Aneliya Angelova's profile","depth":11,"role_description":"cell"},{"role":"AXStaticText","text":"Aneliya Angelova","depth":13,"role_description":"text"},{"role":"AXPopUpButton","text":"More actions","depth":12,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"role_description":"text"},{"role":"AXCell","text":"View Galya Dimitrova's profile","depth":11,"role_description":"cell"},{"role":"AXStaticText","text":"Galya Dimitrova","depth":13,"role_description":"text"},{"role":"AXPopUpButton","text":"More actions","depth":12,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"role_description":"text"},{"role":"AXStaticText","text":"Aneliya Angelova is in the huddle.: .","depth":10,"bounds":{"left":0.4798611,"top":0.0,"width":0.04097222,"height":0.0011111111},"role_description":"text"},{"role":"AXStaticText","text":"Share your screen","depth":12,"bounds":{"left":0.92430556,"top":0.0,"width":0.07152778,"height":0.017777778},"role_description":"text"}]...
|
-8547944451951971235
|
-5968586307401395313
|
idle
|
hybrid
|
NULL
|
Aneliya Angelova
Aneliya Angelova, Screen share
An Aneliya Angelova
Aneliya Angelova, Screen share
Aneliya Angelova
Screen share
Turn on drawing
Fullscreen
Back to grid
View Lukas Kovalik's profile
video is off, audio is on
More actions
Audio
View Aneliya Angelova's profile
Aneliya Angelova
More actions
Audio
View Galya Dimitrova's profile
Galya Dimitrova
More actions
Audio
Aneliya Angelova is in the huddle.: .
Share your screen
FirefoxFileEditViewHistoryBookmarksProfilesTools Window Help→app.staging.jiminny.com/ai-reportsAl ReportsReport namePeriodJY-20553 | Improve crm-sync dela[SRD-6793] Les Mills activity typeJY-20698 handle failed field syncJY-20692 change confirmation pal[JY-20543] AJ Reports > Tracking(JY-18909) [Part2) Automated repAsk Jiminny Reports by nikolay-yarNew TabProduct Growth Platform | UserpiloUserpilot | Logged-activityfix(security): composer dependencPipelines - jiminny/appFeed - jiminny - Sentryfix(security): composer dependencJiminnyJiminny+New TabNAME105Health - 9 - 15 Apr 2026Tuesday Report - 15 Apr 2026Ask Jiminny Test Report - 15 Apr 2026Eastern Summary - 7 - 13 Apr 2026Tuesday Report - 13 Apr 2026Ask Jiminny Test Report - 13 Apr 2026Ask Jiminny Test Report - 13 Apr 2026JY-18909-automated-reports-ask-jiminny = 873114Report TypeFREQUENCYWeeklyDailyDailyWeeklyDailyDailyDaily‹$0100% C8• Mon 20 Apr 11:43:59{03 Ask Jiminny reports© Clear all |SHARED|Kamі +1SchuSchu-LamLKanyDATE Y16/04/202616/04/202616/04/202614/04/202614/04/202614/04/202614/04/2026ACTIONSCooFLeorLeor• Сorш• СоШ• C ш...
|
54193
|
|
54199
|
1169
|
10
|
2026-04-20T08:44:30.043915+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776674670043_m1.jpg...
|
Slack
|
Huddle: @Aneliya Angelova - Jiminny Inc - Slack
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Aneliya Angelova
Aneliya Angelova, Screen share
An Aneliya Angelova
Aneliya Angelova, Screen share
Aneliya Angelova
Screen share
Turn on drawing
Fullscreen
Back to grid
View Lukas Kovalik's profile
video is off, audio is on
More actions
Audio
View Aneliya Angelova's profile
Aneliya Angelova
More actions
Audio
View Galya Dimitrova's profile
Galya Dimitrova
More actions
Audio
Aneliya Angelova is in the huddle.: .
Share your screen...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Aneliya Angelova","depth":14,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Aneliya Angelova, Screen share","depth":14,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Aneliya Angelova","depth":16,"role_description":"text"},{"role":"AXStaticText","text":"Screen share","depth":15,"role_description":"text"},{"role":"AXButton","text":"Turn on drawing","depth":15,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Fullscreen","depth":15,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Back to grid","depth":15,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCell","text":"View Lukas Kovalik's profile","depth":11,"role_description":"cell"},{"role":"AXButton","text":"video is off, audio is on","depth":12,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More actions","depth":12,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"role_description":"text"},{"role":"AXCell","text":"View Aneliya Angelova's profile","depth":11,"role_description":"cell"},{"role":"AXStaticText","text":"Aneliya Angelova","depth":13,"role_description":"text"},{"role":"AXPopUpButton","text":"More actions","depth":12,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"role_description":"text"},{"role":"AXCell","text":"View Galya Dimitrova's profile","depth":11,"role_description":"cell"},{"role":"AXStaticText","text":"Galya Dimitrova","depth":13,"role_description":"text"},{"role":"AXPopUpButton","text":"More actions","depth":12,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"role_description":"text"},{"role":"AXStaticText","text":"Aneliya Angelova is in the huddle.: .","depth":10,"bounds":{"left":0.4798611,"top":0.0,"width":0.04097222,"height":0.0011111111},"role_description":"text"},{"role":"AXStaticText","text":"Share your screen","depth":12,"bounds":{"left":0.92430556,"top":0.0,"width":0.07152778,"height":0.017777778},"role_description":"text"}]...
|
-8547944451951971235
|
-5968586307401395313
|
idle
|
hybrid
|
NULL
|
Aneliya Angelova
Aneliya Angelova, Screen share
An Aneliya Angelova
Aneliya Angelova, Screen share
Aneliya Angelova
Screen share
Turn on drawing
Fullscreen
Back to grid
View Lukas Kovalik's profile
video is off, audio is on
More actions
Audio
View Aneliya Angelova's profile
Aneliya Angelova
More actions
Audio
View Galya Dimitrova's profile
Galya Dimitrova
More actions
Audio
Aneliya Angelova is in the huddle.: .
Share your screen
FirefoxFileEditViewHistoryBookmarksProfilesTools Window Help→app.staging.jiminny.com/ai-reportsAl ReportsReport namePeriodJY-20553 | Improve crm-sync dela[SRD-6793] Les Mills activity typeJY-20698 handle failed field syncJY-20692 change confirmation pal[JY-20543] AJ Reports > Tracking(JY-18909) [Part2) Automated repAsk Jiminny Reports by nikolay-yarNew TabProduct Growth Platform | UserpiloUserpilot | Logged-activityfix(security): composer dependencPipelines - jiminny/appFeed - jiminny - Sentryfix(security): composer dependencJiminnyJiminny+New TabNAME105Health - 9 - 15 Apr 2026Tuesday Report - 15 Apr 2026Ask Jiminny Test Report - 15 Apr 2026Eastern Summary - 7 - 13 Apr 2026Tuesday Report - 13 Apr 2026Ask Jiminny Test Report - 13 Apr 2026Ask Jiminny Test Report - 13 Apr 2026JY-18909-automated-reports-ask-jiminny = 873114Report TypeFREQUENCYWeeklyDailyDailyWeeklyDailyDailyDaily‹$0100% C8• Mon 20 Apr 11:44:29{03 Ask Jiminny reports© Clear all |SHARED|Kamі +1SchuSchu-LamLKamyDATEY16/04/202616/04/202616/04/202614/04/202614/04/202614/04/202614/04/2026ACTIONSCooF• PoFLeor.• Сorш• СоШ• C ш...
|
54193
|
|
54207
|
1170
|
20
|
2026-04-20T08:45:11.659718+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776674711659_m2.jpg...
|
Slack
|
Huddle: @Aneliya Angelova - Jiminny Inc - Slack
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Aneliya Angelova
Aneliya Angelova, Screen share
An Aneliya Angelova
Aneliya Angelova, Screen share
Aneliya Angelova
Screen share
Turn on drawing
Fullscreen
Back to grid
View Lukas Kovalik's profile
video is off, audio is on
More actions
Audio
View Aneliya Angelova's profile
Aneliya Angelova
More actions
Audio
View Galya Dimitrova's profile
Galya Dimitrova
More actions
Audio
Aneliya Angelova is in the huddle.: .
Share your screen...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Aneliya Angelova","depth":14,"bounds":{"left":0.50398934,"top":0.049481247,"width":0.011968086,"height":0.033519555},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Aneliya Angelova, Screen share","depth":14,"bounds":{"left":0.5199468,"top":0.049481247,"width":0.03856383,"height":0.017557861},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Aneliya Angelova","depth":16,"bounds":{"left":0.5199468,"top":0.051077414,"width":0.03856383,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Screen share","depth":15,"bounds":{"left":0.5199468,"top":0.06783719,"width":0.024268618,"height":0.012769354},"role_description":"text"},{"role":"AXButton","text":"Turn on drawing","depth":15,"bounds":{"left":0.90791225,"top":0.0518755,"width":0.011968086,"height":0.028731046},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Fullscreen","depth":15,"bounds":{"left":0.91988033,"top":0.0518755,"width":0.011968086,"height":0.028731046},"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Back to grid","depth":15,"bounds":{"left":0.9318484,"top":0.0518755,"width":0.011968086,"height":0.028731046},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCell","text":"View Lukas Kovalik's profile","depth":11,"bounds":{"left":0.9478058,"top":0.31683958,"width":0.049867023,"height":0.11971269},"role_description":"cell"},{"role":"AXButton","text":"video is off, audio is on","depth":12,"bounds":{"left":0.95046544,"top":0.40782124,"width":0.03158245,"height":0.022346368},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More actions","depth":12,"bounds":{"left":0.984375,"top":0.32322428,"width":0.010638298,"height":0.025538707},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"bounds":{"left":0.97273934,"top":0.31444532,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXCell","text":"View Aneliya Angelova's profile","depth":11,"bounds":{"left":0.9478058,"top":0.44134077,"width":0.049867023,"height":0.11971269},"role_description":"cell"},{"role":"AXStaticText","text":"Aneliya Angelova","depth":13,"bounds":{"left":0.953125,"top":0.5371109,"width":0.03324468,"height":0.012769354},"role_description":"text"},{"role":"AXPopUpButton","text":"More actions","depth":12,"bounds":{"left":0.984375,"top":0.44772545,"width":0.010638298,"height":0.025538707},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"bounds":{"left":0.97273934,"top":0.43894652,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXCell","text":"View Galya Dimitrova's profile","depth":11,"bounds":{"left":0.9478058,"top":0.565842,"width":0.049867023,"height":0.11971269},"role_description":"cell"},{"role":"AXStaticText","text":"Galya Dimitrova","depth":13,"bounds":{"left":0.96110374,"top":0.66161215,"width":0.029920213,"height":0.027134877},"role_description":"text"},{"role":"AXPopUpButton","text":"More actions","depth":12,"bounds":{"left":0.984375,"top":0.57222664,"width":0.010638298,"height":0.025538707},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"bounds":{"left":0.97273934,"top":0.5634477,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Aneliya Angelova is in the huddle.: .","depth":10,"bounds":{"left":0.5,"top":0.9992019,"width":0.019614361,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Share your screen","depth":12,"bounds":{"left":0.71276593,"top":0.9353551,"width":0.034242023,"height":0.012769354},"role_description":"text"}]...
|
-8547944451951971235
|
-5968586307401395313
|
visual_change
|
hybrid
|
NULL
|
Aneliya Angelova
Aneliya Angelova, Screen share
An Aneliya Angelova
Aneliya Angelova, Screen share
Aneliya Angelova
Screen share
Turn on drawing
Fullscreen
Back to grid
View Lukas Kovalik's profile
video is off, audio is on
More actions
Audio
View Aneliya Angelova's profile
Aneliya Angelova
More actions
Audio
View Galya Dimitrova's profile
Galya Dimitrova
More actions
Audio
Aneliya Angelova is in the huddle.: .
Share your screen
slackActivityLaterVIewMistonWindowJiminny ...& Starred& jiminny-x-integrati...8 platform-inner-team• Channels*al-chapteri alerts# backendcontusion-cllnie# curiosity_lab# engineerins# frontend# general# infra-changes# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the people of jimi..¿ 8. Aneliya Angelova @• MessagesLo Add canvasr FilesA Direct meccagpcP. A...М9 д3P. Galya Dimitrova. Stefka StoyanovaR?. Stoyan Tomov3 Aneliya Angelova,..P. Nikolay Nikolov&. Stoyan Tanev€. Vasil Vasilev. Nikolay Ivanov®. Ves:: AppsS Jira CloudLukas Kovallik 9:36 AMThursdav. April 16thvAneliya Angelova 10:00 AMЛукаш кога искаш да се чуемза командитеLukas Kovalik 10:01 AMайле слел 15 мин че се зарових в зохоLukas Kovalik • 10:32 AMAneliya Angelova 10:33 AMA huddle happened 10:33 AMJukas Kovalk ' 10-41AMphp artisan automated-reports --report-id 39php artisan automated-reports:send --result-id 64Todav vAneliya Angelova 9:41 AMLukas Kovalik 10.31 AMАни след малко може да се чуемAneliva Angelova M 11.27 AMнаплаво зеьнни когато можешLukas Kovalik 11:35 AMЗдрасти Лукаш, струва ми се, че репортите се генерират върху всички активитита със съответния Saved search bez da e dobawen filtyr za dataYou ioined the huddle LIVE 11:35 AMAneliva Angelova and Galva Dimitrova are here too.Galva Dimitrova won't be able to see messages sent in thread because this huddle began in a direct message. You canMessage Aneliva Angeloval+ Aae6 Huddle with Aneliya AngelovaAl Notes: OffLeaveAneliya AngelovaScreen share10HistoryBookmarkstxpires On -Expires Onapp.staging.jiminny.com/ai-reportsAl ReportsNAMEExpires On - 20 April - New - 13 - 19 Apr 2026@) Expires On - 20 April - New - 13 - 19 Apr 2026© Expires On - 20 April - New - 13 - 19 Apr 2026•) Expires On - 20 April - New - 13 - 19 Apr 2026Shared With Group - 1 Jul 2025 - 15 Apr 20260) Product Feedback - 1Feb - 31 Mar 2026 - AllJiminny Recipient - 1 Dec 2025 - 14 May 2026liminnv Recinient. 1 Dee 2025. 14 Mav 2024Jiminny Recipient - 1 Dec 2025 - 14 May 2026Exec Summary - 9 Nov 2024 - 12 Mar 2026 - AllJY-18909-&6 Huddle with Aneliya AngelovaE Expires On -P Expires On•Clear all|FREQUENCYWeeklyWeeklyWeeklyWeeklyOne-OffOne-OffOne-OffOne-OffOne-OffMonthlvShare your screenP Expires On - x@ Expires OnSHAREDewsQws@ws@wsS0Otxpires OnDATE20/04/202620/04/202620/04/202620/04/202631/03/202631/03/202631/03/202631/03/202431/02/202426/03/2026100% Lz8• Mon 20 Apr 11:45:118• © Mon 20 Apr 11:45O CircleciS3 Ask Jiminny reportsACTIONSLeave...
|
54206
|
|
54209
|
1170
|
22
|
2026-04-20T08:45:35.854407+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776674735854_m2.jpg...
|
Slack
|
Huddle: @Aneliya Angelova - Jiminny Inc - Slack
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Aneliya Angelova
Aneliya Angelova, Screen share
An Aneliya Angelova
Aneliya Angelova, Screen share
Aneliya Angelova
Screen share
Turn on drawing
Fullscreen
Back to grid
View Lukas Kovalik's profile
video is off, audio is on
More actions
Audio
View Aneliya Angelova's profile
Aneliya Angelova
More actions
Audio
View Galya Dimitrova's profile
Galya Dimitrova
More actions
Audio
Aneliya Angelova is in the huddle.: .
Share your screen...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Aneliya Angelova","depth":14,"bounds":{"left":0.50398934,"top":0.049481247,"width":0.011968086,"height":0.033519555},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Aneliya Angelova, Screen share","depth":14,"bounds":{"left":0.5199468,"top":0.049481247,"width":0.03856383,"height":0.017557861},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Aneliya Angelova","depth":16,"bounds":{"left":0.5199468,"top":0.051077414,"width":0.03856383,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Screen share","depth":15,"bounds":{"left":0.5199468,"top":0.06783719,"width":0.024268618,"height":0.012769354},"role_description":"text"},{"role":"AXButton","text":"Turn on drawing","depth":15,"bounds":{"left":0.90791225,"top":0.0518755,"width":0.011968086,"height":0.028731046},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Fullscreen","depth":15,"bounds":{"left":0.91988033,"top":0.0518755,"width":0.011968086,"height":0.028731046},"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Back to grid","depth":15,"bounds":{"left":0.9318484,"top":0.0518755,"width":0.011968086,"height":0.028731046},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCell","text":"View Lukas Kovalik's profile","depth":11,"bounds":{"left":0.9478058,"top":0.31683958,"width":0.049867023,"height":0.11971269},"role_description":"cell"},{"role":"AXButton","text":"video is off, audio is on","depth":12,"bounds":{"left":0.95046544,"top":0.40782124,"width":0.03158245,"height":0.022346368},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More actions","depth":12,"bounds":{"left":0.984375,"top":0.32322428,"width":0.010638298,"height":0.025538707},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"bounds":{"left":0.97273934,"top":0.31444532,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXCell","text":"View Aneliya Angelova's profile","depth":11,"bounds":{"left":0.9478058,"top":0.44134077,"width":0.049867023,"height":0.11971269},"role_description":"cell"},{"role":"AXStaticText","text":"Aneliya Angelova","depth":13,"bounds":{"left":0.953125,"top":0.5371109,"width":0.03324468,"height":0.012769354},"role_description":"text"},{"role":"AXPopUpButton","text":"More actions","depth":12,"bounds":{"left":0.984375,"top":0.44772545,"width":0.010638298,"height":0.025538707},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"bounds":{"left":0.97273934,"top":0.43894652,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXCell","text":"View Galya Dimitrova's profile","depth":11,"bounds":{"left":0.9478058,"top":0.565842,"width":0.049867023,"height":0.11971269},"role_description":"cell"},{"role":"AXStaticText","text":"Galya Dimitrova","depth":13,"bounds":{"left":0.953125,"top":0.66161215,"width":0.03125,"height":0.012769354},"role_description":"text"},{"role":"AXPopUpButton","text":"More actions","depth":12,"bounds":{"left":0.984375,"top":0.57222664,"width":0.010638298,"height":0.025538707},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"bounds":{"left":0.97273934,"top":0.5634477,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Aneliya Angelova is in the huddle.: .","depth":10,"bounds":{"left":0.5,"top":0.9992019,"width":0.019614361,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Share your screen","depth":12,"bounds":{"left":0.71276593,"top":0.9353551,"width":0.034242023,"height":0.012769354},"role_description":"text"}]...
|
-8547944451951971235
|
-5968586307401395313
|
visual_change
|
hybrid
|
NULL
|
Aneliya Angelova
Aneliya Angelova, Screen share
An Aneliya Angelova
Aneliya Angelova, Screen share
Aneliya Angelova
Screen share
Turn on drawing
Fullscreen
Back to grid
View Lukas Kovalik's profile
video is off, audio is on
More actions
Audio
View Aneliya Angelova's profile
Aneliya Angelova
More actions
Audio
View Galya Dimitrova's profile
Galya Dimitrova
More actions
Audio
Aneliya Angelova is in the huddle.: .
Share your screen
slackActivityLateVIewmistoryWindowHeldJiminny ...* Aneliya Angelova C* Starred |• MessagesLo Add canvasur Files• jiminny-x-integrat...platform-inner-team@ Channels*al-chapteri alerts# backendcontusion-cllnie# curiosity lab# engineerings trontendi# generalac intra-changes#jiminny-bg# platform-tickets# product_launches# randomi releases# sofia-office# supporti# thank-yous# the people of iimi..A Direct meccagpcP. A...М9 д3• Galva Dimitrova2 Stefka StoyanovaStovan Tomov3 Aneliya Angelova, ..o. Nikolav NikolovRo Stoyan TanevVasil VasilevO Nikolav Ivanov8. VesWednesday, March 25th ~Sample: 16697304759leal:ids:986:all (ConvertToNewAccount • 1058Sorpte: 23230075812Aneliya Angelova 4:27 PMМдаLukas Kovallik 4.27 PMдали не се е обработило веднагаИ ZaTORA ИRUeзeHeAneliva Angelova 4-28 PMАми да го качваме тогава на продLukas Kovalik 4-28 PMАми утре сутринта преллагамче след малко ще ходя да взимам малките и тряова и да изчистя събран кешThursday, March 26thAneliya Angelova 3:24 PMЛукаш всичко наред ли е след като качи синка на контакти и акаунтиpunay тoги ПP hitns./oithuh.com/iiminnv/ann/null/11855Lukas Kovalik M 3.24 PMла имаше елин бъг. от послелния code smeliFriday. March 27thAneliiva A ngelova 0.22 AMlох ясно защо не съм видяла този проблем след като ретествах след като оправи код смелапеално нишо не се е леплойвало - в Circle Cl всичко е зелено. а нишо не се е качвало на ОДІ.irealno sum si testwala s поомените от прели послелния къмитGalva Dimitrova won't be able to see messages sent in thread because this huddle began in a direct message. You can start a new huddle instead.::: Anns#7 Jira CloudMessage Aneliva Angeloval+ Aae6d Huddle with Aneliva Angelova67 Huddle with Aneliya AngelovaAneliya AngelovaScreen shareHistory Bookmarks ProfilesTabP Expires On -Expires OnExpires OnExpires On* app.staging.jiminny.com/ai-reports/manageAsk Jiminny Reports 3txpires OrEdit report1. GeneralExpires On - 2 april• Daily• WeeklyMonthlv2. Report parametersLong User Name For Testing 656Y - Expires On - 1 ApnilShars vour ceroonAl Notes: OffLeaveDo8.0o Circiect16 Aor. 20271ninnv Mobile SA xASK JIMINNY PROMPT|100% Lz.• Mon ZU A0r 11:40.30Mon 20 Aor 11:45Leave...
|
54208
|
|
54214
|
NULL
|
0
|
2026-04-20T08:45:57.066309+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776674757066_m2.jpg...
|
Slack
|
Huddle: @Aneliya Angelova - Jiminny Inc - Slack
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Aneliya Angelova
Aneliya Angelova, Screen share
An Aneliya Angelova
Aneliya Angelova, Screen share
Aneliya Angelova
Screen share
Turn on drawing
Fullscreen
Back to grid
View Lukas Kovalik's profile
video is off, audio is on
More actions
Audio
View Aneliya Angelova's profile
Aneliya Angelova
More actions
Audio
View Galya Dimitrova's profile
Galya Dimitrova
More actions
Audio
Aneliya Angelova is in the huddle.: .
Share your screen...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Aneliya Angelova","depth":14,"bounds":{"left":0.50398934,"top":0.049481247,"width":0.011968086,"height":0.033519555},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Aneliya Angelova, Screen share","depth":14,"bounds":{"left":0.5199468,"top":0.049481247,"width":0.03856383,"height":0.017557861},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Aneliya Angelova","depth":16,"bounds":{"left":0.5199468,"top":0.051077414,"width":0.03856383,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Screen share","depth":15,"bounds":{"left":0.5199468,"top":0.06783719,"width":0.024268618,"height":0.012769354},"role_description":"text"},{"role":"AXButton","text":"Turn on drawing","depth":15,"bounds":{"left":0.90791225,"top":0.0518755,"width":0.011968086,"height":0.028731046},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Fullscreen","depth":15,"bounds":{"left":0.91988033,"top":0.0518755,"width":0.011968086,"height":0.028731046},"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Back to grid","depth":15,"bounds":{"left":0.9318484,"top":0.0518755,"width":0.011968086,"height":0.028731046},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCell","text":"View Lukas Kovalik's profile","depth":11,"bounds":{"left":0.9478058,"top":0.31683958,"width":0.049867023,"height":0.11971269},"role_description":"cell"},{"role":"AXButton","text":"video is off, audio is on","depth":12,"bounds":{"left":0.95046544,"top":0.40782124,"width":0.03158245,"height":0.022346368},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More actions","depth":12,"bounds":{"left":0.984375,"top":0.32322428,"width":0.010638298,"height":0.025538707},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"bounds":{"left":0.97273934,"top":0.31444532,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXCell","text":"View Aneliya Angelova's profile","depth":11,"bounds":{"left":0.9478058,"top":0.44134077,"width":0.049867023,"height":0.11971269},"role_description":"cell"},{"role":"AXStaticText","text":"Aneliya Angelova","depth":13,"bounds":{"left":0.953125,"top":0.5371109,"width":0.03324468,"height":0.012769354},"role_description":"text"},{"role":"AXPopUpButton","text":"More actions","depth":12,"bounds":{"left":0.984375,"top":0.44772545,"width":0.010638298,"height":0.025538707},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"bounds":{"left":0.97273934,"top":0.43894652,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXCell","text":"View Galya Dimitrova's profile","depth":11,"bounds":{"left":0.9478058,"top":0.565842,"width":0.049867023,"height":0.11971269},"role_description":"cell"},{"role":"AXStaticText","text":"Galya Dimitrova","depth":13,"bounds":{"left":0.96110374,"top":0.66161215,"width":0.029920213,"height":0.027134877},"role_description":"text"},{"role":"AXPopUpButton","text":"More actions","depth":12,"bounds":{"left":0.984375,"top":0.57222664,"width":0.010638298,"height":0.025538707},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"bounds":{"left":0.97273934,"top":0.5634477,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Aneliya Angelova is in the huddle.: .","depth":10,"bounds":{"left":0.5,"top":0.9992019,"width":0.019614361,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Share your screen","depth":12,"bounds":{"left":0.71276593,"top":0.9353551,"width":0.034242023,"height":0.012769354},"role_description":"text"}]...
|
-8547944451951971235
|
-5968586307401395313
|
visual_change
|
hybrid
|
NULL
|
Aneliya Angelova
Aneliya Angelova, Screen share
An Aneliya Angelova
Aneliya Angelova, Screen share
Aneliya Angelova
Screen share
Turn on drawing
Fullscreen
Back to grid
View Lukas Kovalik's profile
video is off, audio is on
More actions
Audio
View Aneliya Angelova's profile
Aneliya Angelova
More actions
Audio
View Galya Dimitrova's profile
Galya Dimitrova
More actions
Audio
Aneliya Angelova is in the huddle.: .
Share your screen
slackActivityLaterVIewMistonWindowJiminny ...& Starred& jiminny-x-integrati...8 platform-inner-team• Channels*al-chaptera alerts# backendcontusion-cllnie# curiosity_lab# engineerins# frontend# general# infra-changes# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the people of jimi..¿ 8. Aneliya Angelova @• MessagesLo Add canvasr FilesA Direct meccagpcP. A...М9 д3P. Galya Dimitrova. Stefka StoyanovaR?. Stoyan Tomov3 Aneliya Angelova,..P. Nikolay Nikolov&. Stoyan Tanev€. Vasil Vasilev. Nikolay Ivanov8. Ves:: AppsS Jira CloudLukas Kovallik 9:36 AMThursdav. April 16thvAneliya Angelova ® 10:00 AMЛукаш кога искаш да се чуемза командитеLukas Kovalik 10:01 AMайле слел 15 мин че се зарових в зохоLukas Kovalik • 10:32 AMAneliya Angelova 10:33 AMA huddle happened 10:33 AMukas Kovallk 10-41 AMIphp artisan automated-reports --report-id 39php artisan automated-reports:send --result-id 64Todav vAneliya Angelova 9:41 AMLukas Kovalik 10.31 AMАни след малко може да се чуемAneliva Angelova M 11.27 AMнаплaво зеьнни когато можешLukas Kovalik 11:35 AMЗдрасти Лукаш, струва ми се, че репортите се генерират върху всички активитита със съответния Saved search bez da e dobawen filtyr za dataYou ioined the huddle LIVE 11:35 AMAneliva Angelova and Galva Dimitrova are here too.Galva Dimitrova won't be able to see messages sent in thread because this huddle began in a direct message. You can start a new huddle instead.Message Aneliva Angeloval+ Aae6 Huddle with Aneliya AngelovaAl Notes: OffLeave6 Huddle with Aneliya Angelova10Aneliya AngelovaScreen shareChromeHistory Bookmarks Profilestxpires On -Expires Onapp.staging.jiminny.com/ai-reports/manageAsk Jiminny Reports ®NAME AExpires On - 2 AprilExpires On - 20 April - NewExoires On - 3 April |Expires On - 4 AprilExpires On - 5 AprilExoires On - 6 April |Y - Expires On - 1 AprilExpires OnP Expires OnP Expires On -lcatircoeSHAREDFREQUENCYWeeklyWeeklyWeeklvMonthlyDailyMonthlvMonthlyexpires On.P Expires Or(-) Clear all |EXPIRING16/04/202721/04/20264 Expiring in 1 day03/04/202604/04/2024A ExpiredA Expired06/04/2026/Exoired01/04/20264y ExpiredQShare your screen100% S28• Mon 20 Apr 11:45:578• © Mon 20 Apr 11:45O Circleci, WorkACTIONSLeave...
|
54212
|
|
54217
|
1171
|
0
|
2026-04-20T08:46:57.534853+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776674817534_m1.jpg...
|
Slack
|
Huddle: @Aneliya Angelova - Jiminny Inc - Slack
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Aneliya Angelova
Aneliya Angelova, Screen share
An Aneliya Angelova
Aneliya Angelova, Screen share
Aneliya Angelova
Screen share
Turn on drawing
Fullscreen
Back to grid
View Lukas Kovalik's profile
video is off, audio is on
More actions
Audio
View Aneliya Angelova's profile
Aneliya Angelova
More actions
Audio
View Galya Dimitrova's profile
Galya Dimitrova
More actions
Audio
Aneliya Angelova is in the huddle.: .
Share your screen...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Aneliya Angelova","depth":14,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Aneliya Angelova, Screen share","depth":14,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Aneliya Angelova","depth":16,"role_description":"text"},{"role":"AXStaticText","text":"Screen share","depth":15,"role_description":"text"},{"role":"AXButton","text":"Turn on drawing","depth":15,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Fullscreen","depth":15,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Back to grid","depth":15,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCell","text":"View Lukas Kovalik's profile","depth":11,"role_description":"cell"},{"role":"AXButton","text":"video is off, audio is on","depth":12,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More actions","depth":12,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"role_description":"text"},{"role":"AXCell","text":"View Aneliya Angelova's profile","depth":11,"role_description":"cell"},{"role":"AXStaticText","text":"Aneliya Angelova","depth":13,"role_description":"text"},{"role":"AXPopUpButton","text":"More actions","depth":12,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"role_description":"text"},{"role":"AXCell","text":"View Galya Dimitrova's profile","depth":11,"role_description":"cell"},{"role":"AXStaticText","text":"Galya Dimitrova","depth":13,"role_description":"text"},{"role":"AXPopUpButton","text":"More actions","depth":12,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"role_description":"text"},{"role":"AXStaticText","text":"Aneliya Angelova is in the huddle.: .","depth":10,"bounds":{"left":0.4798611,"top":0.0,"width":0.04097222,"height":0.0011111111},"role_description":"text"},{"role":"AXStaticText","text":"Share your screen","depth":12,"bounds":{"left":0.92430556,"top":0.0,"width":0.07152778,"height":0.017777778},"role_description":"text"}]...
|
-8547944451951971235
|
-5968586307401395313
|
idle
|
hybrid
|
NULL
|
Aneliya Angelova
Aneliya Angelova, Screen share
An Aneliya Angelova
Aneliya Angelova, Screen share
Aneliya Angelova
Screen share
Turn on drawing
Fullscreen
Back to grid
View Lukas Kovalik's profile
video is off, audio is on
More actions
Audio
View Aneliya Angelova's profile
Aneliya Angelova
More actions
Audio
View Galya Dimitrova's profile
Galya Dimitrova
More actions
Audio
Aneliya Angelova is in the huddle.: .
Share your screen
FirefoxFileEditViewHistoryBookmarksProfilesTools Window Help→app.staging.jiminny.com/ai-reportsAl ReportsReport namePeriodJY-20553 | Improve crm-sync dela[SRD-6793] Les Mills activity typeJY-20698 handle failed field syncJY-20692 change confirmation pal[JY-20543] AJ Reports > Tracking(JY-18909) [Part2) Automated repAsk Jiminny Reports by nikolay-yarNew TabProduct Growth Platform | UserpiloUserpilot | Logged-activityfix(security): composer dependencPipelines - jiminny/appFeed - jiminny - Sentryfix(security): composer dependencJiminnyJiminny+New TabNAME105Health - 9 - 15 Apr 2026Tuesday Report - 15 Apr 2026Ask Jiminny Test Report - 15 Apr 2026Eastern Summary - 7 - 13 Apr 2026Tuesday Report - 13 Apr 2026Ask Jiminny Test Report - 13 Apr 2026Ask Jiminny Test Report - 13 Apr 2026JY-18909-automated-reports-ask-jiminny = 873114Report TypeFREQUENCYWeeklyDailyDailyWeeklyDailyDailyDaily‹$0100% C8• Mon 20 Apr 11:46:57{03 Ask Jiminny reports© Clear all |SHARED|Kamі +1SchuSchu-LamLKanyDATE Y16/04/202616/04/202616/04/202614/04/202614/04/202614/04/202614/04/2026ACTIONSCooFLeorLeor• Сorш• СоШ• C ш...
|
54213
|
|
54218
|
1172
|
1
|
2026-04-20T08:47:12.244190+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776674832244_m2.jpg...
|
Slack
|
Huddle: @Aneliya Angelova - Jiminny Inc - Slack
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Aneliya Angelova
Aneliya Angelova, Screen share
An Aneliya Angelova
Aneliya Angelova, Screen share
Aneliya Angelova
Screen share
Turn on drawing
Fullscreen
Back to grid
View Lukas Kovalik's profile
video is off, audio is on
More actions
Audio
View Aneliya Angelova's profile
Aneliya Angelova
More actions
Audio
View Galya Dimitrova's profile
Galya Dimitrova
More actions
Audio
Aneliya Angelova is in the huddle.: .
Share your screen...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Aneliya Angelova","depth":14,"bounds":{"left":0.50398934,"top":0.049481247,"width":0.011968086,"height":0.033519555},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Aneliya Angelova, Screen share","depth":14,"bounds":{"left":0.5199468,"top":0.049481247,"width":0.03856383,"height":0.017557861},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Aneliya Angelova","depth":16,"bounds":{"left":0.5199468,"top":0.051077414,"width":0.03856383,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Screen share","depth":15,"bounds":{"left":0.5199468,"top":0.06783719,"width":0.024268618,"height":0.012769354},"role_description":"text"},{"role":"AXButton","text":"Turn on drawing","depth":15,"bounds":{"left":0.90791225,"top":0.0518755,"width":0.011968086,"height":0.028731046},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Fullscreen","depth":15,"bounds":{"left":0.91988033,"top":0.0518755,"width":0.011968086,"height":0.028731046},"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Back to grid","depth":15,"bounds":{"left":0.9318484,"top":0.0518755,"width":0.011968086,"height":0.028731046},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCell","text":"View Lukas Kovalik's profile","depth":11,"bounds":{"left":0.9478058,"top":0.31683958,"width":0.049867023,"height":0.11971269},"role_description":"cell"},{"role":"AXButton","text":"video is off, audio is on","depth":12,"bounds":{"left":0.95046544,"top":0.40782124,"width":0.03158245,"height":0.022346368},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More actions","depth":12,"bounds":{"left":0.984375,"top":0.32322428,"width":0.010638298,"height":0.025538707},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"bounds":{"left":0.97273934,"top":0.31444532,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXCell","text":"View Aneliya Angelova's profile","depth":11,"bounds":{"left":0.9478058,"top":0.44134077,"width":0.049867023,"height":0.11971269},"role_description":"cell"},{"role":"AXStaticText","text":"Aneliya Angelova","depth":13,"bounds":{"left":0.953125,"top":0.5371109,"width":0.03324468,"height":0.012769354},"role_description":"text"},{"role":"AXPopUpButton","text":"More actions","depth":12,"bounds":{"left":0.984375,"top":0.44772545,"width":0.010638298,"height":0.025538707},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"bounds":{"left":0.97273934,"top":0.43894652,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXCell","text":"View Galya Dimitrova's profile","depth":11,"bounds":{"left":0.9478058,"top":0.565842,"width":0.049867023,"height":0.11971269},"role_description":"cell"},{"role":"AXStaticText","text":"Galya Dimitrova","depth":13,"bounds":{"left":0.96110374,"top":0.66161215,"width":0.029920213,"height":0.027134877},"role_description":"text"},{"role":"AXPopUpButton","text":"More actions","depth":12,"bounds":{"left":0.984375,"top":0.57222664,"width":0.010638298,"height":0.025538707},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Audio","depth":12,"bounds":{"left":0.97273934,"top":0.5634477,"width":0.0003324468,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Aneliya Angelova is in the huddle.: .","depth":10,"bounds":{"left":0.5,"top":0.9992019,"width":0.019614361,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Share your screen","depth":12,"bounds":{"left":0.71276593,"top":0.9353551,"width":0.034242023,"height":0.012769354},"role_description":"text"}]...
|
-8547944451951971235
|
-5968586307401395313
|
idle
|
hybrid
|
NULL
|
Aneliya Angelova
Aneliya Angelova, Screen share
An Aneliya Angelova
Aneliya Angelova, Screen share
Aneliya Angelova
Screen share
Turn on drawing
Fullscreen
Back to grid
View Lukas Kovalik's profile
video is off, audio is on
More actions
Audio
View Aneliya Angelova's profile
Aneliya Angelova
More actions
Audio
View Galya Dimitrova's profile
Galya Dimitrova
More actions
Audio
Aneliya Angelova is in the huddle.: .
Share your screen
slackActivityLaterVIewMistonWindowJiminny ...& Starred& jiminny-x-integrati...8 platform-inner-team• Channels*al-chapteri alerts# backendcontusion-cllnie# curiosity_lab# engineerins# frontend# general# infra-changes# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the people of jimi..¿ 8. Aneliya Angelova @• MessagesLo Add canvasr FilesA Direct meccagpcP. A...М9 д3P. Galya Dimitrova. Stefka StoyanovaR?. Stoyan Tomov3 Aneliya Angelova,..P. Nikolay Nikolov&. Stoyan Tanev€. Vasil Vasilev. Nikolay Ivanov8. Ves:: AppsS Jira CloudLukas Kovallik 9:36 AMThursdav. April 16th vAneliya Angelova ® 10:00 AMЛукаш кога искаш да се чуемза командитеLukas Kovalik 10:01 AMайле слел 15 мин че се зарових в зохоLukas Kovalik • 10:32 AMAneliya Angelova 10:33 AMA huddle happened 10:33 AMukas Kovalk ' 10-41AMphp artisan automated-reports --report-id 39php artisan automated-reports:send --result-id 64Todav vAneliya Angelova 9:41 AMLukas Kovalik 10.31 AMАни след малко може да се чуемAneliva Angelova M 11.27 AMнапрaво зеьнни когато можешіLukas Kovalik 11:35 AMЗдрасти Лукаш, струва ми се, че репортите се генерират върху всички активитита със съответния Saved search bez da e dobawen filtyr za dataYou ioined the hudcle LIVE 11:35 AMAneliva Angelova and Galva Dimitrova are here too.Galva Dimitrova won't be able to see messages sent in thread because this huddle began in a direct message. You can start a new huddle instead.Message Aneliva Angeloval+ Aae6 Huddle with Aneliya AngelovaAl Notes: OffLeave100% Lz8• Mon 20 Apr 11:47:116 Huddle with Aneliya AngelovaQ.10Aneliya AngelovaScreen shareHistory Bookmarks Profiles8•0 Mon 20 Apr 11:47txpires On -Expires OnExpires On-P Expires OnP Expires OnE Expires OnO Circleciapp.staging.jiminny.com/ondemand?topic_id%5B%5D=054ec28-f103-433f-а69c-fce7d0677f5b&tоpic_jd%5B%5D=43b58441-а208-4cda-bbff-39f4d9548cf1&t.CustomerTranscript ©Said bylAnyonePeriodAll timeYesterdayThis weekLast weekLast 30 daysLast 90 days|This quarterThis vearAl call scoreKey Words ScoreCoachine score17 activitiesSort by: Most recent& Save Search @ Clear allActivityont?etRobinsons Land CorpApr 2026Tu Welnielle Grossine DabertChris Farace at Opentrons W-Chris Faraceg)SportFIVE #linny <> SportFIVE P - Next stepsQShare your screen© Add RecordingA Get NotifiedCurrent StageDuration DateContract SentAdd Recording03/02/2025.7:21PMUS$8,70007/10/2024,6:21 PMLikely to Renew€138.75911/09/2024, 6:27 PM35m01/08/2024. 1:35 PMIVerbal26/07/2024. 1:40 PMIUS$59.40015m24/07/2024.5:15 PMIUsSO27/06/2024, 5:26Leave...
|
54216
|
|
19789
|
423
|
5
|
2026-04-15T08:01:52.390454+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-15/1776 /Users/lukas/.screenpipe/data/data/2026-04-15/1776240112390_m1.jpg...
|
Firefox
|
Meet - [Platform] Planning I Session 📅 — Work
|
True
|
meet.google.com/tgb-pyuf-dri?authuser=lukas.kovali meet.google.com/tgb-pyuf-dri?authuser=lukas.kovalik%40jiminny.com...
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Meet - [Platform] Planning I Session 📅
Close tab
N Meet - [Platform] Planning I Session 📅
Close tab
New Tab
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Customize sidebar
Galya Dimitrova (Presenting, annotating)
Galya Dimitrova (Presenting, annotating)
People
9
Take notes with Gemini
Take notes with Gemini
Gemini
Gemini
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
Unpin Galya Dimitrova's presentation from your main screen
You can't unmute someone else's presentation
More options for Galya Dimitrova
Zoom in
Open in new window
Enter Full Screen
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
Pin Nikolay Yankov to your main screen
Mute Nikolay Yankov's microphone
More options for Nikolay Yankov
Nikolay Yankov
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
Pin Galya Dimitrova to your main screen
Mute Galya Dimitrova's microphone
More options for Galya Dimitrova
Galya Dimitrova
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
Pin Steliyan Georgiev to your main screen
You can't unmute someone else
More options for Steliyan Georgiev
Steliyan Georgiev
4 others
4 others
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
You’re continuously framed
Backgrounds and effects
More options for Lukas Kovalik
Lukas Kovalik
Others might see more of your background. Click to view your full video.
11:01
AM
[Platform] Planning I Session 📅
[Platform] Planning I Session 📅
Audio settings
Turn on microphone
Video settings
Turn off camera...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Meet - [Platform] Planning I Session 📅","depth":4,"bounds":{"left":0.0,"top":0.072222225,"width":0.033680554,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.0013888889,"top":0.072222225,"width":0.010416667,"height":0.016666668},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.005902778,"top":0.12,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"bounds":{"left":0.0,"top":0.7977778,"width":0.033680554,"height":0.043333333},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.0,"top":0.8411111,"width":0.033680554,"height":0.038333334},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.0,"top":0.8794444,"width":0.033680554,"height":0.03888889},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.0,"top":0.91833335,"width":0.033680554,"height":0.038333334},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0,"top":0.95666665,"width":0.033680554,"height":0.043333333},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Galya Dimitrova (Presenting, annotating)","depth":12,"bounds":{"left":0.07534722,"top":0.101111114,"width":0.18333334,"height":0.022222223},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Galya Dimitrova (Presenting, annotating)","depth":13,"bounds":{"left":0.07534722,"top":0.10222222,"width":0.18333334,"height":0.020555556},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"People","depth":15,"bounds":{"left":0.88680553,"top":0.08944444,"width":0.04097222,"height":0.04},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":22,"bounds":{"left":0.9145833,"top":0.101111114,"width":0.0048611113,"height":0.017222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Take notes with Gemini","depth":14,"bounds":{"left":0.93333334,"top":0.08944444,"width":0.025,"height":0.04},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Take notes with Gemini","depth":17,"bounds":{"left":0.9361111,"top":0.101111114,"width":0.06388891,"height":0.017222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini","depth":22,"bounds":{"left":0.96666664,"top":0.101111114,"width":0.028125,"height":0.017222222},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Gemini","depth":22,"bounds":{"left":0.96458334,"top":0.090555556,"width":0.023611112,"height":0.037777778},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Pop out this video More screens are more fun. Play this video while you do other things.","depth":15,"bounds":{"left":0.5798611,"top":0.62833333,"width":0.14652778,"height":0.08888889},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pop out this video","depth":17,"bounds":{"left":0.7239583,"top":0.6427778,"width":0.08090278,"height":0.018888889},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"More screens are more fun. Play this video while you do other things.","depth":16,"bounds":{"left":0.7017361,"top":0.6388889,"width":0.11076389,"height":0.05666667},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Unpin Galya Dimitrova's presentation from your main screen","depth":13,"bounds":{"left":0.346875,"top":0.5083333,"width":0.027777778,"height":0.044444446},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"You can't unmute someone else's presentation","depth":13,"bounds":{"left":0.37465277,"top":0.5061111,"width":0.030555556,"height":0.04888889},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"More options for Galya Dimitrova","depth":13,"bounds":{"left":0.40520832,"top":0.5083333,"width":0.027777778,"height":0.044444446},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Zoom in","depth":13,"bounds":{"left":0.6315972,"top":0.83111113,"width":0.027777778,"height":0.044444446},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open in new window","depth":13,"bounds":{"left":0.6649306,"top":0.83111113,"width":0.027777778,"height":0.044444446},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Enter Full Screen","depth":13,"bounds":{"left":0.6982639,"top":0.83111113,"width":0.027777778,"height":0.044444446},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Pop out this video More screens are more fun. Play this video while you do other things.","depth":15,"bounds":{"left":0.7861111,"top":0.27611113,"width":0.14652778,"height":0.07722222},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pop out this video","depth":17,"bounds":{"left":0.9302083,"top":0.2911111,"width":0.069791675,"height":0.017777778},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"More screens are more fun. Play this video while you do other things.","depth":16,"bounds":{"left":0.9079861,"top":0.28666666,"width":0.092013896,"height":0.045},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Pin Nikolay Yankov to your main screen","depth":13,"bounds":{"left":0.76180553,"top":0.25111112,"width":0.027777778,"height":0.044444446},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Mute Nikolay Yankov's microphone","depth":13,"bounds":{"left":0.7895833,"top":0.2488889,"width":0.030555556,"height":0.04888889},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"More options for Nikolay Yankov","depth":13,"bounds":{"left":0.8201389,"top":0.25111112,"width":0.027777778,"height":0.044444446},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Nikolay Yankov","depth":17,"bounds":{"left":0.75451386,"top":0.36277777,"width":0.07673611,"height":0.022777777},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Pop out this video More screens are more fun. Play this video while you do other things.","depth":15,"bounds":{"left":0.925,"top":0.27722222,"width":0.07499999,"height":0.07722222},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pop out this video","depth":17,"bounds":{"left":1.0,"top":0.29222223,"width":-0.06909728,"height":0.017777778},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"More screens are more fun. Play this video while you do other things.","depth":16,"bounds":{"left":1.0,"top":0.28777778,"width":-0.046875,"height":0.045},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Pin Galya Dimitrova to your main screen","depth":13,"bounds":{"left":0.8875,"top":0.25111112,"width":0.027777778,"height":0.044444446},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Mute Galya Dimitrova's microphone","depth":13,"bounds":{"left":0.9152778,"top":0.2488889,"width":0.030555556,"height":0.04888889},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"More options for Galya Dimitrova","depth":13,"bounds":{"left":0.9458333,"top":0.25111112,"width":0.027777778,"height":0.044444446},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Galya Dimitrova","depth":17,"bounds":{"left":0.8802083,"top":0.36277777,"width":0.08229167,"height":0.022777777},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Pop out this video More screens are more fun. Play this video while you do other things.","depth":15,"bounds":{"left":0.80069447,"top":0.5338889,"width":0.14652778,"height":0.07722222},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pop out this video","depth":17,"bounds":{"left":0.9447917,"top":0.54888886,"width":0.055208325,"height":0.017777778},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"More screens are more fun. Play this video while you do other things.","depth":16,"bounds":{"left":0.92256945,"top":0.54444444,"width":0.07743055,"height":0.045},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Pin Steliyan Georgiev to your main screen","depth":13,"bounds":{"left":0.76180553,"top":0.5088889,"width":0.027777778,"height":0.044444446},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"You can't unmute someone else","depth":13,"bounds":{"left":0.7895833,"top":0.50666666,"width":0.030555556,"height":0.04888889},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"More options for Steliyan Georgiev","depth":13,"bounds":{"left":0.8201389,"top":0.5088889,"width":0.027777778,"height":0.044444446},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Steliyan Georgiev","depth":17,"bounds":{"left":0.75451386,"top":0.6205556,"width":0.090625,"height":0.022777777},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"4 others","depth":11,"bounds":{"left":0.871875,"top":0.40888888,"width":0.11736111,"height":0.24444444},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"4 others","depth":13,"bounds":{"left":0.90902776,"top":0.5566667,"width":0.043055557,"height":0.022777777},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Pop out this video More screens are more fun. Play this video while you do other things.","depth":15,"bounds":{"left":0.8680556,"top":0.7916667,"width":0.13194442,"height":0.07722222},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pop out this video","depth":17,"bounds":{"left":0.79479164,"top":0.8066667,"width":0.07569444,"height":0.017777778},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"More screens are more fun. Play this video while you do other things.","depth":16,"bounds":{"left":0.77534723,"top":0.80222225,"width":0.11736111,"height":0.045},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"You’re continuously framed","depth":13,"bounds":{"left":0.8229167,"top":0.7644445,"width":0.030555556,"height":0.04888889},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Backgrounds and effects","depth":13,"bounds":{"left":0.85347223,"top":0.7644445,"width":0.030555556,"height":0.04888889},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"More options for Lukas Kovalik","depth":13,"bounds":{"left":0.8840278,"top":0.76666665,"width":0.027777778,"height":0.044444446},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Lukas Kovalik","depth":17,"bounds":{"left":0.75381947,"top":0.87833333,"width":0.06909722,"height":0.022777777},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Others might see more of your background. Click to view your full video.","depth":14,"bounds":{"left":0.96631944,"top":0.875,"width":0.018055556,"height":0.028888889},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"11:01","depth":12,"bounds":{"left":0.050347224,"top":0.9444444,"width":0.023958333,"height":0.022777777},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"AM","depth":12,"bounds":{"left":0.07777778,"top":0.9444444,"width":0.017708333,"height":0.022777777},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"[Platform] Planning I Session 📅","depth":12,"bounds":{"left":0.112847224,"top":0.9111111,"width":0.16215278,"height":0.08888888},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"[Platform] Planning I Session 📅","depth":15,"bounds":{"left":0.112847224,"top":0.9438889,"width":0.16215278,"height":0.023333333},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Audio settings","depth":13,"bounds":{"left":0.32118055,"top":0.9288889,"width":0.06111111,"height":0.053333335},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Turn on microphone","depth":13,"bounds":{"left":0.34895834,"top":0.9288889,"width":0.033333335,"height":0.053333335},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":true,"is_selected":false},{"role":"AXButton","text":"Video settings","depth":13,"bounds":{"left":0.38784721,"top":0.9288889,"width":0.06111111,"height":0.053333335},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Turn off camera","depth":13,"bounds":{"left":0.415625,"top":0.9288889,"width":0.033333335,"height":0.053333335},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
-8547832233323486872
|
-6606247125593621700
|
click
|
accessibility
|
NULL
|
Meet - [Platform] Planning I Session 📅
Close tab
N Meet - [Platform] Planning I Session 📅
Close tab
New Tab
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Customize sidebar
Galya Dimitrova (Presenting, annotating)
Galya Dimitrova (Presenting, annotating)
People
9
Take notes with Gemini
Take notes with Gemini
Gemini
Gemini
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
Unpin Galya Dimitrova's presentation from your main screen
You can't unmute someone else's presentation
More options for Galya Dimitrova
Zoom in
Open in new window
Enter Full Screen
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
Pin Nikolay Yankov to your main screen
Mute Nikolay Yankov's microphone
More options for Nikolay Yankov
Nikolay Yankov
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
Pin Galya Dimitrova to your main screen
Mute Galya Dimitrova's microphone
More options for Galya Dimitrova
Galya Dimitrova
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
Pin Steliyan Georgiev to your main screen
You can't unmute someone else
More options for Steliyan Georgiev
Steliyan Georgiev
4 others
4 others
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
You’re continuously framed
Backgrounds and effects
More options for Lukas Kovalik
Lukas Kovalik
Others might see more of your background. Click to view your full video.
11:01
AM
[Platform] Planning I Session 📅
[Platform] Planning I Session 📅
Audio settings
Turn on microphone
Video settings
Turn off camera...
|
19787
|
|
55933
|
1208
|
28
|
2026-04-20T10:44:12.259420+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776681852259_m2.jpg...
|
Firefox
|
Firefox
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
FirefoxEditVIewQMOA 04 8 Mon20 Apr 13:44:1288 Open FirefoxEditVIewQMOA 04 8 Mon20 Apr 13:44:1288 OpenJy 19798 evaluation for ai activityJY-20553 | Improve crm-sync dela19 ISRD-67931 Les Mills activity typeJY-20698 handle failed field syncJY-20692 change confirmation *JY-205431 AJ Reports > Tracking(UY-18909) (Part2) Automated rep/Ask Jiminny Reports by nikolay-ya- New Tabu Product Growth Platform | UserpiloU Useroilot I Loaaed-activity(fix(security): composer depender• test (873154) - jiminny/app) Feed - jiminny - Sentry8 Jiminnyg Jiminny8 Jiminny(JY-20692] Issue with reconnect• Jy-20692 change confirmation pa# (UY-20692] Issue with reconnectin_ New TalMistorbooKmarksProtllesToolsWindowmelp• github.com/jiminny/app/pull/11986RookmarksSearch bookmarksv a bookmarks loolba• Sorint Boardi# SRD QueueGithubJiminny DEVAsk Jiminny Reports by nikolay-yankov • ...© Circle CI& PROD USv E Bookmarks Menu> → Mozilla FirefoxD Other BookmarksJY-20692 change confirmation parameter #11986LakyLak wants to merge 1 commit into master from JY-20692-fix-integration-app-[API_KEY]) 1150 +if (connection && connection.disconnected === false && connection.connected ===true){Commit suggestion - Add suggestion to batch@ LakyLak 4 hours agoAuthor ...It is a loose checks that treats undefined or null as acceptable states.& nikolay-yankov 4 hours agoMy suggestion doesn't remove this. It simplifies it by removing double negation checksWritePreview•It is a loose checks that treats undetined or nuli as accentable states.IM+ Markdown is supported |° Paste. droo. or click to add files ICancelCommentResolve conversationyalokin-jiminny commented 2 hours ago@claudeclaude bot commented 2 hours ago • edited-Claude finished @yalokin-jiminny's task in 52s — View jobAnalvsis of the condition discussionRead changed files and analyze the condition...
|
NULL
|
-8547091664392803652
|
NULL
|
visual_change
|
ocr
|
NULL
|
FirefoxEditVIewQMOA 04 8 Mon20 Apr 13:44:1288 Open FirefoxEditVIewQMOA 04 8 Mon20 Apr 13:44:1288 OpenJy 19798 evaluation for ai activityJY-20553 | Improve crm-sync dela19 ISRD-67931 Les Mills activity typeJY-20698 handle failed field syncJY-20692 change confirmation *JY-205431 AJ Reports > Tracking(UY-18909) (Part2) Automated rep/Ask Jiminny Reports by nikolay-ya- New Tabu Product Growth Platform | UserpiloU Useroilot I Loaaed-activity(fix(security): composer depender• test (873154) - jiminny/app) Feed - jiminny - Sentry8 Jiminnyg Jiminny8 Jiminny(JY-20692] Issue with reconnect• Jy-20692 change confirmation pa# (UY-20692] Issue with reconnectin_ New TalMistorbooKmarksProtllesToolsWindowmelp• github.com/jiminny/app/pull/11986RookmarksSearch bookmarksv a bookmarks loolba• Sorint Boardi# SRD QueueGithubJiminny DEVAsk Jiminny Reports by nikolay-yankov • ...© Circle CI& PROD USv E Bookmarks Menu> → Mozilla FirefoxD Other BookmarksJY-20692 change confirmation parameter #11986LakyLak wants to merge 1 commit into master from JY-20692-fix-integration-app-[API_KEY]) 1150 +if (connection && connection.disconnected === false && connection.connected ===true){Commit suggestion - Add suggestion to batch@ LakyLak 4 hours agoAuthor ...It is a loose checks that treats undefined or null as acceptable states.& nikolay-yankov 4 hours agoMy suggestion doesn't remove this. It simplifies it by removing double negation checksWritePreview•It is a loose checks that treats undetined or nuli as accentable states.IM+ Markdown is supported |° Paste. droo. or click to add files ICancelCommentResolve conversationyalokin-jiminny commented 2 hours ago@claudeclaude bot commented 2 hours ago • edited-Claude finished @yalokin-jiminny's task in 52s — View jobAnalvsis of the condition discussionRead changed files and analyze the condition...
|
NULL
|
|
27527
|
575
|
27
|
2026-04-15T13:50:46.852502+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-15/1776 /Users/lukas/.screenpipe/data/data/2026-04-15/1776261046852_m2.jpg...
|
Boosteroid
|
Boosteroid
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
8525907073271955152/185Imperial AgeNPlayer 8 Almis 8525907073271955152/185Imperial AgeNPlayer 8 Almish Yiltawar!!!--Scout Cavalry Created---Halberdier Created---Warning: You are being attacked byPlayer 2 Rajyapala!!!-Click to select this military unit.5 Magnus Olafsson: 26427/264271 kovaliklukas: 21653/21653Rajyapala: 19795/197958 Almish Yiltawar: 19621/196216 László I: 12327/123277 Maximilian of Habobung: 6503/6502 14 Louis VI: 6209/6209...
|
NULL
|
-8546695960125892201
|
NULL
|
click
|
ocr
|
NULL
|
8525907073271955152/185Imperial AgeNPlayer 8 Almis 8525907073271955152/185Imperial AgeNPlayer 8 Almish Yiltawar!!!--Scout Cavalry Created---Halberdier Created---Warning: You are being attacked byPlayer 2 Rajyapala!!!-Click to select this military unit.5 Magnus Olafsson: 26427/264271 kovaliklukas: 21653/21653Rajyapala: 19795/197958 Almish Yiltawar: 19621/196216 László I: 12327/123277 Maximilian of Habobung: 6503/6502 14 Louis VI: 6209/6209...
|
27525
|
|
47424
|
1003
|
13
|
2026-04-17T11:38:18.948879+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-17/1776 /Users/lukas/.screenpipe/data/data/2026-04-17/1776425898948_m1.jpg...
|
Firefox
|
Dashboard · Jiminny · Membrane — Work
|
True
|
console.getmembrane.com/w/66fd5a6e813fde5d1b8aa505 console.getmembrane.com/w/66fd5a6e813fde5d1b8aa505?element=%2Fconnectors%2F64a158e7d2605720d232e07b&aside=%2Fagent%2F69e21b571e4d92fd3b379d4c...
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Workers | Datadog
Developers | HubSpot
Developers Workers | Datadog
Developers | HubSpot
Developers | HubSpot
Inbox (1,576) - [EMAIL] - Jiminny Mail
Inbox (1,576) - [EMAIL] - Jiminny Mail
120216 is your HubSpot Log In Code - [EMAIL] - Jiminny Mail
120216 is your HubSpot Log In Code - [EMAIL] - Jiminny Mail
CloudWatch | eu-west-1
CloudWatch | eu-west-1
New Tab
New Tab
Configure SSH access to multiple environment - Engineering - Confluence
Configure SSH access to multiple environment - Engineering - Confluence
fix-cache-for-business-processes by ilian-jiminny · Pull Request #11985 · jiminny/app
fix-cache-for-business-processes by ilian-jiminny · Pull Request #11985 · jiminny/app
[JY-20692] Issue with reconnecting Zoho - Jira
[JY-20692] Issue with reconnecting Zoho - Jira
Jiminny
Jiminny
Auth Proxy
Auth Proxy
Dashboard · Jiminny · Membrane
Dashboard · Jiminny · Membrane
Close tab
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Workspace: Jiminny
Jiminny
Search
⌘K
Dashboard
Dashboard
Explore
Explore
Activity
Activity
Settings
Settings
Docs
Docs
Logout
Version
2026-04-16
Connectors
Connectors
Zoho CRM
Zoho CRM
Public
Development
Development
Test
Test
Overview
Overview
Connector Configuration
Connector Configuration
Operations
Operations
Data Collections
Data Collections
Universal Data Models
Universal Data Models
Global Webhooks
Global Webhooks
Events
Events
Basic Info
Basic Info
Name
Zoho CRM
Key
zoho-crm
App
Zoho CRM Zoho CRM
Zoho CRM
Logo
https://static.integration.app/connectors/zoho-crm/logo.png
Zoho CRM
Implementation
Implementation
Multiple options
Multiple options
Common
Common
?
Connect via Membrane
Connect via Membrane
OAuth 2.0
OAuth 2.0
Add implementation option
Agent / How to disable Connect via Membrane
Agent / How to disable Connect via Membrane
Beta
How to disable Connect via Membrane
I'd be happy to help you disable Connect via Membrane! However, I need a bit more context to give you the most accurate answer.
Could you clarify what you mean by "Connect via Membrane"? Are you referring to:
Disabling a specific connection
- removing a customer's connection to an external app (like Stripe, Salesforce, etc.)?
Disabling an integration
- turning off an entire integration in your workspace?
Disabling OAuth/authentication flow
- preventing users from connecting their accounts?
Disabling a specific action or flow
- turning off certain integration functionality?
Something else
- perhaps related to Membrane's connection management features?
Once you clarify, I can provide you with the specific steps or code needed to accomplish what you're looking for.
I want to see just connect to zoho and one connect button not OAuth 2.0 or Connect via Membrane
Working...
What would you like to do?
Open Iframe Session Screen
Open Iframe Session Screen...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Workers | Datadog","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Developers | HubSpot","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Developers | HubSpot","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Inbox (1,576) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Inbox (1,576) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"120216 is your HubSpot Log In Code - integration-account@jiminny.com - Jiminny Mail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"120216 is your HubSpot Log In Code - integration-account@jiminny.com - Jiminny Mail","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"CloudWatch | eu-west-1","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"CloudWatch | eu-west-1","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"New Tab","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"New Tab","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Configure SSH access to multiple environment - Engineering - Confluence","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Configure SSH access to multiple environment - Engineering - Confluence","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"fix-cache-for-business-processes by ilian-jiminny · Pull Request #11985 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"fix-cache-for-business-processes by ilian-jiminny · Pull Request #11985 · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20692] Issue with reconnecting Zoho - Jira","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20692] Issue with reconnecting Zoho - Jira","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Auth Proxy","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Auth Proxy","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Dashboard · Jiminny · Membrane","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Dashboard · Jiminny · Membrane","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Workspace: Jiminny","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Search","depth":10,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"⌘K","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Dashboard","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Dashboard","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Explore","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Explore","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Activity","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Activity","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Settings","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Settings","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Docs","depth":11,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Docs","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Logout","depth":10,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Version","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2026-04-16","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Connectors","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Connectors","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Zoho CRM","depth":18,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Zoho CRM","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Public","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXComboBox","text":"Development","depth":15,"value":"Development","help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Development","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Test","depth":15,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Test","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Overview","depth":20,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Overview","depth":22,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Connector Configuration","depth":18,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Connector Configuration","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Operations","depth":20,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Operations","depth":22,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Data Collections","depth":20,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Data Collections","depth":22,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Universal Data Models","depth":20,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Universal Data Models","depth":22,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Global Webhooks","depth":20,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Global Webhooks","depth":22,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Events","depth":20,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Events","depth":22,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Basic Info","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Basic Info","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Name","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXTextField","text":"Zoho CRM","depth":17,"value":"Zoho CRM","help_text":"","role_description":"text field","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Key","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXTextField","text":"zoho-crm","depth":17,"value":"zoho-crm","help_text":"","role_description":"text field","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"App","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXComboBox","text":"Zoho CRM Zoho CRM","depth":19,"value":"Zoho CRM Zoho CRM","help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Zoho CRM","depth":20,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Logo","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXTextField","text":"https://static.integration.app/connectors/zoho-crm/logo.png","depth":17,"value":"https://static.integration.app/connectors/zoho-crm/logo.png","help_text":"","role_description":"text field","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Zoho CRM","depth":18,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Implementation","depth":17,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Implementation","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Multiple options","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Multiple options","depth":18,"help_text":"","role_description":"checkbox","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Common","depth":18,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Common","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"?","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Connect via Membrane","depth":18,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Connect via Membrane","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"OAuth 2.0","depth":18,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"OAuth 2.0","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Add implementation option","depth":18,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Agent / How to disable Connect via Membrane","depth":12,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Agent / How to disable Connect via Membrane","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Beta","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"How to disable Connect via Membrane","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I'd be happy to help you disable Connect via Membrane! However, I need a bit more context to give you the most accurate answer.","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Could you clarify what you mean by \"Connect via Membrane\"? Are you referring to:","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Disabling a specific connection","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"- removing a customer's connection to an external app (like Stripe, Salesforce, etc.)?","depth":20,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Disabling an integration","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"- turning off an entire integration in your workspace?","depth":20,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Disabling OAuth/authentication flow","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"- preventing users from connecting their accounts?","depth":20,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Disabling a specific action or flow","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"- turning off certain integration functionality?","depth":20,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Something else","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"- perhaps related to Membrane's connection management features?","depth":20,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Once you clarify, I can provide you with the specific steps or code needed to accomplish what you're looking for.","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I want to see just connect to zoho and one connect button not OAuth 2.0 or Connect via Membrane","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Working...","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"What would you like to do?","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Open Iframe Session Screen","depth":15,"bounds":{"left":1.0,"top":0.0,"width":-0.013888836,"height":0.035555556},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Open Iframe Session Screen","depth":17,"bounds":{"left":1.0,"top":0.0,"width":-0.04444444,"height":0.016666668},"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
-8546436328686583715
|
-8423878606404867547
|
idle
|
accessibility
|
NULL
|
Workers | Datadog
Developers | HubSpot
Developers Workers | Datadog
Developers | HubSpot
Developers | HubSpot
Inbox (1,576) - [EMAIL] - Jiminny Mail
Inbox (1,576) - [EMAIL] - Jiminny Mail
120216 is your HubSpot Log In Code - [EMAIL] - Jiminny Mail
120216 is your HubSpot Log In Code - [EMAIL] - Jiminny Mail
CloudWatch | eu-west-1
CloudWatch | eu-west-1
New Tab
New Tab
Configure SSH access to multiple environment - Engineering - Confluence
Configure SSH access to multiple environment - Engineering - Confluence
fix-cache-for-business-processes by ilian-jiminny · Pull Request #11985 · jiminny/app
fix-cache-for-business-processes by ilian-jiminny · Pull Request #11985 · jiminny/app
[JY-20692] Issue with reconnecting Zoho - Jira
[JY-20692] Issue with reconnecting Zoho - Jira
Jiminny
Jiminny
Auth Proxy
Auth Proxy
Dashboard · Jiminny · Membrane
Dashboard · Jiminny · Membrane
Close tab
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Workspace: Jiminny
Jiminny
Search
⌘K
Dashboard
Dashboard
Explore
Explore
Activity
Activity
Settings
Settings
Docs
Docs
Logout
Version
2026-04-16
Connectors
Connectors
Zoho CRM
Zoho CRM
Public
Development
Development
Test
Test
Overview
Overview
Connector Configuration
Connector Configuration
Operations
Operations
Data Collections
Data Collections
Universal Data Models
Universal Data Models
Global Webhooks
Global Webhooks
Events
Events
Basic Info
Basic Info
Name
Zoho CRM
Key
zoho-crm
App
Zoho CRM Zoho CRM
Zoho CRM
Logo
https://static.integration.app/connectors/zoho-crm/logo.png
Zoho CRM
Implementation
Implementation
Multiple options
Multiple options
Common
Common
?
Connect via Membrane
Connect via Membrane
OAuth 2.0
OAuth 2.0
Add implementation option
Agent / How to disable Connect via Membrane
Agent / How to disable Connect via Membrane
Beta
How to disable Connect via Membrane
I'd be happy to help you disable Connect via Membrane! However, I need a bit more context to give you the most accurate answer.
Could you clarify what you mean by "Connect via Membrane"? Are you referring to:
Disabling a specific connection
- removing a customer's connection to an external app (like Stripe, Salesforce, etc.)?
Disabling an integration
- turning off an entire integration in your workspace?
Disabling OAuth/authentication flow
- preventing users from connecting their accounts?
Disabling a specific action or flow
- turning off certain integration functionality?
Something else
- perhaps related to Membrane's connection management features?
Once you clarify, I can provide you with the specific steps or code needed to accomplish what you're looking for.
I want to see just connect to zoho and one connect button not OAuth 2.0 or Connect via Membrane
Working...
What would you like to do?
Open Iframe Session Screen
Open Iframe Session Screen...
|
NULL
|
|
57926
|
1245
|
20
|
2026-04-20T12:17:44.400788+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776687464400_m2.jpg...
|
PhpStorm
|
PhpStorm
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormViewINavigarecodeLaravelKeractorWindowFV f PhostormViewINavigarecodeLaravelKeractorWindowFV faVsco.js°9 master kProject"C) ActivitvController.oho©AskAnythingController.php© AskAnythingPromptService.phpJiminnyDeougcommana.pnpM+ WEBHOOK-FILTERING_IMPLEMENTATION.moC) AutomatedReportsRepository.pnpAutomatedkeporsserwice.ongAutomatedReportsCommand.phpUpaatencuviyclastcsearenDocumentcommana.onp>0b External Librariesv E Scratches and Consolesv _ Database consolesV AEUconsole IEUlA DEAL RISKS (EU]ADITEUTA EU (EUv / lminnv@localhostconsole fiminnv@localhost)l4 D| lliminnv@localhost4 HS_local jiminny@localhost)4 SF jiminny@localhost]A zoho_dev [jiminny@localhost]V A PRODA console (PROD]A console_1 (PROD]A DI (PROD]Servicesv D DatabaseVLEUconsolev /iiminnv@localhost#HS localASF 978 msAPRODconsole 2s 371msASTAGINGIconsoleDocker© UpdateElasticSearch.php x© TrackProviderInstalledEvent.phpC) AutomatedReportkesult.pngC) Automatedkeport.pnpclass Updaterlasticsearch extends IterateActivitles37 €protected bool SwithTrashed = true40* Render a oroqress oor, so we know what the current orogress 1s.* @var bo0l44 0)orotected SwithProaressBar = true:* The console command description.OutputAR Result 4iit Result 5 Tx.Orows v of0+ G00MaaidTmuser idyI crm_configuration_id Ycrm_provider_id Y : crm_profile_id Y : edition Y : Dhas_external_cti Y100% Lz• Mon 20 Apr 15:17:44=custom.log= laravel.l0gselect * from playbooks where team_id = 711; # event 226147SELECT * FROM playbook categories WHERE playbook id = 5515:ScLECl * rkuM crm trelas whEkt 10 = 220147SELECT * FROM crm_field values WHERE crm_field id = 226147:598S S8 8A SF [jiminny@localhost]« HS_local [jiminny@localhost]& console [PROD] >« console (EU]SELECT * FROM crm_confiqurations WHERE id = 692:SELEC.CONCAT(u,id. CASE WHEN u.id = towner id THEN • (owner)' ELSE 1* END) AS user id..u.emanlsa.*,t.owner_id FROM social_accounts saJOIN users u on u.id = sa.sociable_idJJOIN teams + 1.n<->1:on t.id = u,team id/HERE u.team_id = 711 and sa.provider = 'salesfonce' ;690 *SELECT * FROM crm_profiles cp JOIN users u 1..n<->1: on u.id = cp.user_id WHERE u.team_id = 711; 2 s 328 ms« console [STAGING]So jiminny034 A1 A34 V 62 ^* Wlead_record_type_id Y÷ @lead_fields Y•account_record_type_id Y account_fields YM onportunity record tvne idYcsvu|d 1 →| 0,tộM onportunity fieldsW Windsurf Teams 609:22 UTF-8 4 spaces...
|
NULL
|
-8546434716451874135
|
NULL
|
visual_change
|
ocr
|
NULL
|
PhostormViewINavigarecodeLaravelKeractorWindowFV f PhostormViewINavigarecodeLaravelKeractorWindowFV faVsco.js°9 master kProject"C) ActivitvController.oho©AskAnythingController.php© AskAnythingPromptService.phpJiminnyDeougcommana.pnpM+ WEBHOOK-FILTERING_IMPLEMENTATION.moC) AutomatedReportsRepository.pnpAutomatedkeporsserwice.ongAutomatedReportsCommand.phpUpaatencuviyclastcsearenDocumentcommana.onp>0b External Librariesv E Scratches and Consolesv _ Database consolesV AEUconsole IEUlA DEAL RISKS (EU]ADITEUTA EU (EUv / lminnv@localhostconsole fiminnv@localhost)l4 D| lliminnv@localhost4 HS_local jiminny@localhost)4 SF jiminny@localhost]A zoho_dev [jiminny@localhost]V A PRODA console (PROD]A console_1 (PROD]A DI (PROD]Servicesv D DatabaseVLEUconsolev /iiminnv@localhost#HS localASF 978 msAPRODconsole 2s 371msASTAGINGIconsoleDocker© UpdateElasticSearch.php x© TrackProviderInstalledEvent.phpC) AutomatedReportkesult.pngC) Automatedkeport.pnpclass Updaterlasticsearch extends IterateActivitles37 €protected bool SwithTrashed = true40* Render a oroqress oor, so we know what the current orogress 1s.* @var bo0l44 0)orotected SwithProaressBar = true:* The console command description.OutputAR Result 4iit Result 5 Tx.Orows v of0+ G00MaaidTmuser idyI crm_configuration_id Ycrm_provider_id Y : crm_profile_id Y : edition Y : Dhas_external_cti Y100% Lz• Mon 20 Apr 15:17:44=custom.log= laravel.l0gselect * from playbooks where team_id = 711; # event 226147SELECT * FROM playbook categories WHERE playbook id = 5515:ScLECl * rkuM crm trelas whEkt 10 = 220147SELECT * FROM crm_field values WHERE crm_field id = 226147:598S S8 8A SF [jiminny@localhost]« HS_local [jiminny@localhost]& console [PROD] >« console (EU]SELECT * FROM crm_confiqurations WHERE id = 692:SELEC.CONCAT(u,id. CASE WHEN u.id = towner id THEN • (owner)' ELSE 1* END) AS user id..u.emanlsa.*,t.owner_id FROM social_accounts saJOIN users u on u.id = sa.sociable_idJJOIN teams + 1.n<->1:on t.id = u,team id/HERE u.team_id = 711 and sa.provider = 'salesfonce' ;690 *SELECT * FROM crm_profiles cp JOIN users u 1..n<->1: on u.id = cp.user_id WHERE u.team_id = 711; 2 s 328 ms« console [STAGING]So jiminny034 A1 A34 V 62 ^* Wlead_record_type_id Y÷ @lead_fields Y•account_record_type_id Y account_fields YM onportunity record tvne idYcsvu|d 1 →| 0,tộM onportunity fieldsW Windsurf Teams 609:22 UTF-8 4 spaces...
|
NULL
|
|
25789
|
549
|
70
|
2026-04-15T13:02:30.433842+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-15/1776 /Users/lukas/.screenpipe/data/data/2026-04-15/1776258150433_m1.jpg...
|
Boosteroid
|
Boosteroid
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
+SlackFileEditViewGoEDHomeActivity..•More+HistoryW +SlackFileEditViewGoEDHomeActivity..•More+HistoryWindowHelp→Search Jiminny IncJiminny ...sos+# general# infra-changes# jiminny-bg# platform-tickets# product _launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...Direct messagesStoyan TanevVesGalya DimitrovaAneliya Angelova, ...Vasil VasilevSteliyan GeorgievAdelina Petrova, Ili...P. Adelina Petrova0. Nikolay Nikolov2 Galya Dimitrova, Ni...ii: AppsToastJira CloudGoogle Cale...# releases8 22Messagesnewdou+O Files• Bookmarksv 2 new messagesGitHub APP3:28 PM7 new commits pushed tomaster by nikolay-yankovNew24b989ee - Enhance SECFIXdocumentation and policiesa3a0a742 - Update SECFIX Slack channelreference in documentation and workflowfiles071c999d - Merge branch 'master' intoimprove-secfix-bot-15-04-2026981e9a1a - Update SECFIX_PROMPT.mdto enhance clarity on upgrade safety andchangelog reviews6e938e53 - Enhance SECFIX workflow withSlack notification optionsShow more( jiminny/app Added by GitHubCircleCl APP 3:53 PMDeployment Successful!Project: appWhen:04/15/202612:53:30Tag:View JobMessage #releases+Aa...Sprint Review • nowActivity MonitorAll ProcessesProcess NameSprint Reviewnow • 16:00-16:30BoosteroidWindowServerFirefoxCP Isolated Web ContentFirefoxFirefoxCursorUlViewService (Not Responding)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefox GPU HelperFirefox GPU HelperVTDecoderXPCServiceFirefoxCP Isolated Web ContentSlack Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentNotion Calendar Helper (Renderer)Notion Helper (Renderer)claudeClaude Helper (Renderer)FirefoxCP Isolated Web ContentiTerm2FirefoxCP Isolated Web ContentFirefoxCP Isolated Web Contentffmpeg2,03 GB1,18 GB962,5 MB961,9 MB841,3 MB792,9 MB774,4 MB547,1 MB544,3 MB538,2 MB516,0 MB449,1 MB442,1 MB432,9 MB414,3 MB408,7 MB388,3 MB387,1 MB372,3 MB345,9 MB330,7 MB326,2 MB299,8 MB293,7 MB254,2 MB236,6 MB228,5 MB216,8 MBMEMORY PRESSUREPhysical Memory:Memory Used:Cached Files:Swap Used:3723257484252526301124152624272524231520131528262716,00 GB14,06 GB <1,88 GB2,95 GB100%8Wed 15 Apr 16:02:30Ci Join Google Meet59819 7601237271 20420 015125128251245165122185125124126124122121172313722161261 83512312727938924074297480141466484242030367131467380199389935480418633583135276436524301636898481732654811485091060519358334878561384829898383App Memory:Wired Memory:Compressed:lukas_windowserverlukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukas3,43 GB2,85 GB7,23 GB...
|
NULL
|
-8546422722068643009
|
NULL
|
click
|
ocr
|
NULL
|
+SlackFileEditViewGoEDHomeActivity..•More+HistoryW +SlackFileEditViewGoEDHomeActivity..•More+HistoryWindowHelp→Search Jiminny IncJiminny ...sos+# general# infra-changes# jiminny-bg# platform-tickets# product _launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...Direct messagesStoyan TanevVesGalya DimitrovaAneliya Angelova, ...Vasil VasilevSteliyan GeorgievAdelina Petrova, Ili...P. Adelina Petrova0. Nikolay Nikolov2 Galya Dimitrova, Ni...ii: AppsToastJira CloudGoogle Cale...# releases8 22Messagesnewdou+O Files• Bookmarksv 2 new messagesGitHub APP3:28 PM7 new commits pushed tomaster by nikolay-yankovNew24b989ee - Enhance SECFIXdocumentation and policiesa3a0a742 - Update SECFIX Slack channelreference in documentation and workflowfiles071c999d - Merge branch 'master' intoimprove-secfix-bot-15-04-2026981e9a1a - Update SECFIX_PROMPT.mdto enhance clarity on upgrade safety andchangelog reviews6e938e53 - Enhance SECFIX workflow withSlack notification optionsShow more( jiminny/app Added by GitHubCircleCl APP 3:53 PMDeployment Successful!Project: appWhen:04/15/202612:53:30Tag:View JobMessage #releases+Aa...Sprint Review • nowActivity MonitorAll ProcessesProcess NameSprint Reviewnow • 16:00-16:30BoosteroidWindowServerFirefoxCP Isolated Web ContentFirefoxFirefoxCursorUlViewService (Not Responding)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefox GPU HelperFirefox GPU HelperVTDecoderXPCServiceFirefoxCP Isolated Web ContentSlack Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentNotion Calendar Helper (Renderer)Notion Helper (Renderer)claudeClaude Helper (Renderer)FirefoxCP Isolated Web ContentiTerm2FirefoxCP Isolated Web ContentFirefoxCP Isolated Web Contentffmpeg2,03 GB1,18 GB962,5 MB961,9 MB841,3 MB792,9 MB774,4 MB547,1 MB544,3 MB538,2 MB516,0 MB449,1 MB442,1 MB432,9 MB414,3 MB408,7 MB388,3 MB387,1 MB372,3 MB345,9 MB330,7 MB326,2 MB299,8 MB293,7 MB254,2 MB236,6 MB228,5 MB216,8 MBMEMORY PRESSUREPhysical Memory:Memory Used:Cached Files:Swap Used:3723257484252526301124152624272524231520131528262716,00 GB14,06 GB <1,88 GB2,95 GB100%8Wed 15 Apr 16:02:30Ci Join Google Meet59819 7601237271 20420 015125128251245165122185125124126124122121172313722161261 83512312727938924074297480141466484242030367131467380199389935480418633583135276436524301636898481732654811485091060519358334878561384829898383App Memory:Wired Memory:Compressed:lukas_windowserverlukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukas3,43 GB2,85 GB7,23 GB...
|
NULL
|
|
53983
|
1168
|
0
|
2026-04-20T08:35:54.059067+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776674154059_m2.jpg...
|
Slack
|
Huddle: @Aneliya Angelova - Jiminny Inc - Slack
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
AI Notes: Off
Thread
Every huddle has a thread
Sen AI Notes: Off
Thread
Every huddle has a thread
Send messages, files, and links to everyone in the huddle. They’re saved as a thread in this direct message with
@Aneliya Angelova
@Aneliya Angelova
, so you can access it even after the huddle is done.
Also send as direct message
Also send as direct message
Hide thread
Aneliya Angelova is in the huddle.: .
Aneliya Angelova has declined the huddle invite.
Hide thread...
|
[{"role":"AXCheckBox","text [{"role":"AXCheckBox","text":"AI Notes: Off","depth":13,"bounds":{"left":0.50664896,"top":0.058260176,"width":0.050199468,"height":0.023942538},"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Thread","depth":13,"bounds":{"left":0.8856383,"top":0.045490824,"width":0.019281914,"height":0.0415004},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXStaticText","text":"Every huddle has a thread","depth":18,"bounds":{"left":0.8909575,"top":0.101356745,"width":0.057845745,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":"Send messages, files, and links to everyone in the huddle. They’re saved as a thread in this direct message with","depth":18,"bounds":{"left":0.8843085,"top":0.12210695,"width":0.10704787,"height":0.049481247},"role_description":"text"},{"role":"AXLink","text":"@Aneliya Angelova","depth":18,"bounds":{"left":0.91522604,"top":0.15642458,"width":0.043218084,"height":0.015961692},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"@Aneliya Angelova","depth":19,"bounds":{"left":0.91589093,"top":0.15722266,"width":0.041888297,"height":0.014365523},"role_description":"text"},{"role":"AXStaticText","text":", so you can access it even after the huddle is done.","depth":18,"bounds":{"left":0.8843085,"top":0.15722266,"width":0.099734046,"height":0.031923383},"role_description":"text"},{"role":"AXTextArea","text":"","depth":21,"bounds":{"left":0.88464093,"top":0.207502,"width":0.10837766,"height":0.030327214},"value":"","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Also send as direct message","depth":20,"bounds":{"left":0.8976064,"top":0.2442139,"width":0.048537236,"height":0.011971269},"role_description":"text"},{"role":"AXCheckBox","text":"Also send as direct message","depth":20,"bounds":{"left":0.8892952,"top":0.2442139,"width":0.0043218085,"height":0.0103751},"role_description":"Tick box","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide thread","depth":13,"bounds":{"left":0.9847075,"top":0.0518755,"width":0.011968086,"height":0.028731046},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Aneliya Angelova is in the huddle.: .","depth":10,"bounds":{"left":0.5,"top":0.9992019,"width":0.019614361,"height":0.0007980846},"role_description":"text"},{"role":"AXStaticText","text":"Aneliya Angelova has declined the huddle invite.","depth":12,"bounds":{"left":0.75598407,"top":0.9050279,"width":0.09075798,"height":0.031923383},"role_description":"text"},{"role":"AXStaticText","text":"Hide thread","depth":12,"bounds":{"left":0.9724069,"top":0.09417398,"width":0.022606382,"height":0.012769354},"role_description":"text"}]...
|
-8546260933936753372
|
-2864654691583438971
|
visual_change
|
hybrid
|
NULL
|
AI Notes: Off
Thread
Every huddle has a thread
Sen AI Notes: Off
Thread
Every huddle has a thread
Send messages, files, and links to everyone in the huddle. They’re saved as a thread in this direct message with
@Aneliya Angelova
@Aneliya Angelova
, so you can access it even after the huddle is done.
Also send as direct message
Also send as direct message
Hide thread
Aneliya Angelova is in the huddle.: .
Aneliya Angelova has declined the huddle invite.
Hide thread
slackActivityLateVIewmistonWindowJiminny ...* Starred |• jiminny-X-integrati...platform-inner-team@ Channels*al-chaptera alerts# backendcontusion-cllnie# curiosity lab# engineerins" trontendl# generalac intra-changes#jiminny-bg# platform-tickets# product_launches# randomi releases# sofia-office# supporti# thank-yous# the people of iimi..A Direct messagesf.A..• Galva Dimitrova2 Stefka StoyanovaStovan Tomov3 Aneliya Angelova, .o. Nikolav NikolovRo Stoyan TanevVasil VasilevO Nikolav Ivanov®. Ves::: AnnsEt Jira Cloud100% L2P• Mon 20 Apr 11:35:5467 Huddle with Aneliya Angelova* Aneliya Angelova C• Messagest Add canvasur FilesAneliya Angelova 9:35 AMFriday. March 27thvдаже не знам от кога вес е счупил деплоя и колко време все съм тествала едно и сьщо без да се усетя че деплойването не е работелоInkas Kovalik 0.24AMlY= Al Notes: Off vThursday, April 16th~Thread@ Every huddle has a threadSend messages, files, and links to everyone in thehuddle. They're saved as a thread in this directmessage with @Anellya Angelova, so you canaccess it even after the huddle is doneReply…• Also send as direct message+ Aа @Aneliya Angelova 10:00 AMЛукаш кога искаш да се чуемза команиитеLukas Kovalik 10:01 AMаиде след 15 мин че се зарових в зохоLukas Kovalik 10:32 AMако искаш ла се чуем сегаAneliva Angelova → 10:33 AMA huddle happened 10:33 AMInkac Kovalik 10.11 AMpho artisan automated-revorts --report-1d 39pho artisan automated-renorts:send --result-1d 64TodavAneliva Angelova 9:41 AMЗдрасти Лукаш, струва ми се, че репортите се генерират върху всички активитита сьс съответния Saved search bez da e dobawen filtyr za dataImkae Kouslil 40.01 AMАни след малко може да се чуемAneliya Angelova 11:27 AMJnkas Kovalik 11.26 AMтука сьмYou joined the huddle LIVE 11:35 AMAneliva Ancelova is here toalMessage Aneliva Angeloval+ AaeAneliva Angelova has declined the huddleinvite6d Huddle with Aneliva AngelovaAl Notes: OffLeaveLeave...
|
NULL
|
|
53984
|
1166
|
0
|
2026-04-20T08:35:54.613219+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776674154613_m1.jpg...
|
Slack
|
Huddle: @Aneliya Angelova - Jiminny Inc - Slack
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
AI Notes: Off
Thread
Every huddle has a thread
Sen AI Notes: Off
Thread
Every huddle has a thread
Send messages, files, and links to everyone in the huddle. They’re saved as a thread in this direct message with
@Aneliya Angelova
@Aneliya Angelova
, so you can access it even after the huddle is done.
Also send as direct message
Also send as direct message
Hide thread
Aneliya Angelova is in the huddle.: .
Aneliya Angelova has declined the huddle invite.
Hide thread...
|
[{"role":"AXCheckBox","text [{"role":"AXCheckBox","text":"AI Notes: Off","depth":13,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Thread","depth":13,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXStaticText","text":"Every huddle has a thread","depth":18,"role_description":"text"},{"role":"AXStaticText","text":"Send messages, files, and links to everyone in the huddle. They’re saved as a thread in this direct message with","depth":18,"role_description":"text"},{"role":"AXLink","text":"@Aneliya Angelova","depth":18,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"@Aneliya Angelova","depth":19,"role_description":"text"},{"role":"AXStaticText","text":", so you can access it even after the huddle is done.","depth":18,"role_description":"text"},{"role":"AXTextArea","text":"","depth":21,"value":"","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Also send as direct message","depth":20,"role_description":"text"},{"role":"AXCheckBox","text":"Also send as direct message","depth":20,"role_description":"Tick box","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide thread","depth":13,"role_description":"button","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Aneliya Angelova is in the huddle.: .","depth":10,"bounds":{"left":0.4798611,"top":0.0,"width":0.04097222,"height":0.0011111111},"role_description":"text"},{"role":"AXStaticText","text":"Aneliya Angelova has declined the huddle invite.","depth":12,"role_description":"text"},{"role":"AXStaticText","text":"Hide thread","depth":12,"role_description":"text"}]...
|
-8546260933936753372
|
-2864654691583438971
|
click
|
hybrid
|
NULL
|
AI Notes: Off
Thread
Every huddle has a thread
Sen AI Notes: Off
Thread
Every huddle has a thread
Send messages, files, and links to everyone in the huddle. They’re saved as a thread in this direct message with
@Aneliya Angelova
@Aneliya Angelova
, so you can access it even after the huddle is done.
Also send as direct message
Also send as direct message
Hide thread
Aneliya Angelova is in the huddle.: .
Aneliya Angelova has declined the huddle invite.
Hide thread
iTerm2ShelllEditViewSessionScriptsProfilesWindowHelp100% C8• Mon 20 Apr 11:35:54screenpipe"DOCKER2026-04-20111:28:15.09933522026-04-20T11:28:18.161712Z2026-04-20T11:28:24.420260Z2026-04-20T11:29:09.322794Z2026-04-20T11:29:12.232781Z2026-04-20T11:29:13.714491Z2026-04-20T11:29:15.29707822026-04-20T11:29:18.279439Z2026-04-20T11:29:21.288709Z2026-04-20T11:29:36.889190Z2026-04-20111:29:36.93591422026-04-20T11:29:44.265375Z2026-04-20T11:29:45.438891Z2026-04-20111:29:48.445512Z2026-04-20T11:30:00.582835Z2026-04-20T11:30:00.930875Z2026-04-20T11:30:03.983533Z2026-04-20T11:30:25.190331ZROM\nframes \nWHERE\n3845166S2026-04-20111:30:25.191014Z2026-04-20T11:30:28.644851Z2026-04-20T11:30:34.292740Z2026-04-20T11:30:49.731059Z2026-04-20T11:30:52.714807Z2026-04-20T11:30:52.764209Z2026-04-20T11:30:53.030418Z2026-04-20T11:30:56.085725Z2026-04-20T11:30:57.236790Z2026-04-20T11:31:57.345054Z2026-04-20111:32:34.844223Z2026-04-20T11:33:01.295072Z2026-04-20T11:33:02.667241Z2026-04-20T11:33:40.313987Z2026-04-20T11:33:40.621030Z2026-04-20T11:33:45.357469Z2026-04-20T11:33:45.361133Z2026-04-20T11:33:50.248270Z2026-04-20T11:33:55.493928Z2026-04-20T11:34:26.223427Z2026-04-20111:34:26.415026Z2026-04-20T11:35:35.008400Z2026-04-20T11:35:37.449048Z2026-04-20T11:35:40.752978ZO ₴1DEV (docker)₴2APP (-zsh)83-zsh• ₴4screenpipe"INFOscreenpipe_engine::event_driven_capture:contentdedup:skipping capture for monitor 2INFOChash=8004693622225847515,trigger=click)screenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 2 (hash=-3897675075138840599, trigger=visual_change)INFOscreenpipe_engine::event_driven_capture: contentdedup:skipping capture for monitor 1(hash=8004693622225847515, trigger=click)INFOscreenpipe_engine::event,_driven_capture:contentdedup:skipping capture for monitor 1(hash=6684718794164531158, trigger=visual_change)INFOscreenpipe_engine:: event,driven_capture:contentdedup:skipping capture for monitor 1 (hash=6684718794164531158, trigger=visual_change)INFOscreenpipe_engine::event_driven_capture:contentdedup: skippingcapture for monitor 1 (hash=6684718794164531158, trigger=click)INFOscreenpipe_engine::event_driven_capture:contentdedup: skippingINFOcapture for monitor 1 (hash=6684718794164531158, trigger=visual_change)screenpipe_engine::event_driven_capture: contentdedup: skipping capture for monitor 1 (hash=6684718794164531158, trigger=visual_change)INFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=6684718794164531158, trigger=visual_change)INFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-1183751968977138305, trigger=click)INFOscreenpipe_engine::event_driven_capture: contentINFOdedup: skipping capture for monitor 2 (hash=-1183751968977138305, trigger=click)screenpipe_engine::event_driven_capture: contentdedup: skipping capture for monitor 1 (hash=1744865349186139800, trigger=click)INFOscreenpipe_engine::event_driven_capture: contentdedup: skippingcapture for monitor 1 (hash=1744865349186139800, trigger=visual_change)INFOscreenpipe_engine::event_driven_capture:content dedup: skippingcapture for monitor 1 (hash=1744865349186139800, trigger=visual_change)INFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=214677970165296243, trigger=click)INFOscreenpipe_engine::event_driven_capture: content dedup:INFOskipping capture for monitor 1 (hash=214677970165296243, trigger=visual_change)screenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 1 (hash=214677970165296243, trigger=visual_change)WARNsqlx::query:summary="SELECT id, snapshot_path, device_name, ."db.statement="\n\nSELECT\nid, \nsnapshot_path, \ndevice_name, \nsnapshot_path IS NOT NULL\nAND timestamp < ?1\nORDER BY\ndevice_name, \ntimestamp ASC\nLIMIT\n5000\n"timestamp\nFrows_affected=0 rows_returned=144 elapsed=2.41INFOscreenpipe_engine::snapshot_compaction: snapshotcompaction: found 144 eligible framesINFOINFOscreenpipe_engine::snapshot_compaction: snapshot compaction: 59 frames, 8.9MB → 1.9MB (4.8x), 59 JPEGs deletedscreenpipe_engine::snapshot_compaction: snapshot compaction: 83 frames, 12.OMB → 6.2MB (1.9X), 83 JPEGs deletedINFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 Chash=-6771186432168714093,trigger=visual_change)INFOscreenpipe_engine::event_driven_capture: contentdedup:skipping capture for monitor 1 (hash=-6771186432168714093,INFOtrigger=click)screenpipe_engine::event_driven_capture: contentdedup: skippingcapture for monitor 2 Chash=-6771186432168714093,trigger=click)INFOscreenpipe_engine:: event._driven_capture: contentdedup:skippingcapture for monitor 1 (hash=-6771186432168714093,trigger=visual_change)INFOscreenpipe_engine::event_driven_capture: contentdedup: skipping capture for monitor 1 (hash=-6771186432168714093, trigger=visual_change)INFOscreenpipe_engine::event_driven_capture: content dedup:skipping capture for monitor 2 (hash=-6771186432168714093, trigger=visual_change)INFOscreenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-5799305003809379240, trigger=visual_change)INFOscreenpipe_engine::event,_driven_capture: contentdedup:skipping capture for monitor 2 (hash=-5799305003809379240, trigger=click)INFOINFOscreenpipe_engine::event_driven_capture: contentdedup:skipping capture for monitor 1 (hash=-2331312136567929026, trigger=visual_change)screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 2 (hash=-2331312136567929026, trigger=visual_change)INFOscreenpipe_engine: :meeting_detector: meeting v2: Idle -> Confirming (app=Slack, signals=2)INFOscreenpipe_engine::event_driven_capture:content dedup:skipping capture for monitor 1 (hash=6376510702070023487, trigger=visual_change)INFOscreenpipe_engine::meeting_detector: meeting v2: ConfirmingINFO-› Active (app=Slack, signals=2, browser=false)screenpipe_engine::meeting_detector: meeting v2:meeting started (id=4, app=Slack, title=None)INFOscreenpipe_engine::event_driven_capture:contentdedup:skipping capture for monitor 2 (hash=2305043043211837966, trigger=visual_change)INFOscreenpipe_engine: :meeting_detector: meeting v2:Active->Ending (nocontrols, app=Slack,id=4, grace=30s)INFOscreenpipe_engine::meeting_detector: meeting v2:Ending -> Idle (timeout=30s, app=Slack, id=4)INFOscreenpipe_engine::meeting_detector: meeting v2: meetingended (id=4)INFOINFOscreenpipe_engine::snapshot_compaction: snapshotcompaction: found 86eligible framesscreenpipe_engine::snapshot_compaction: snapshotcompaction: 35 frames,8.2MB → 2.2MB (3.8x), 35 JPEGs deletedINFOscreenpipe_engine::snapshot_compaction: snapshotcompaction: 49 frames, 5.9MB → 3.OMB (2.0x), 49 JPEGs deleted...
|
NULL
|
|
30048
|
610
|
53
|
2026-04-15T14:54:36.777115+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-15/1776 /Users/lukas/.screenpipe/data/data/2026-04-15/1776264876777_m1.jpg...
|
Boosteroid
|
Boosteroid
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
+SlackFileEditViewGoEDHomeDMSActivityFilesLater..• +SlackFileEditViewGoEDHomeDMSActivityFilesLater..•More+→Jiminny ...+CHISMICCIIS# frontend# general# infra-changes# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of _jimi...Direct messagesAneliya Angelova, ...Stoyan Tanev® Ves. Galya Dimitrova€. Vasil VasilevR. Steliyan GeorgievAdelina Petrova, Ili...P. Adelina PetrovaR. Nikolay Nikolovii: AppsJira CloudToastHistoryWindowHelpSearch Jiminny Inc# releases8 226 0MessagesO FilesBookmarks+6e938eS2ir- Enhance StaLiX workflow withSlack iv 2 new messagesShow morejiminny/app Added by GitHubNewCircleCl APP3:53 PMDeployment Successful!Project: appWhen:04/15/202612:53:30Tag:View JobGitHub APP5:54 PM10 new commits pushed to master by yalokin-jiminny630fd8f9 - SRD-6779 |JY-20632 | Unableto log in to Sidekick with SSO0f38589b - SRD-6779 |JY-20632 | Add log4dd5718e - SRD-6779 |JY-20632 | minorimprovementb1e544db - SRD-6779 |JY-20632 | addtests8bd0ef70 - SRD-6779 |JY-20632 | addtestsShow morejiminny/app| Added by GitHubMessage #releases+AaActivity MonitorAll ProcessesProcess NameBoosteroidWindowServerFirefoxFirefoxCP Isolated Web ContentFirefoxFirefoxCP Isolated Web ContentCursorUlViewService (Not Responding)FirefoxCP Isolated Web ContentFirefox GPU HelperFirefox GPU HelperVTDecoderXPCServiceFirefoxCP Isolated Web ContentSlack Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentNotion Calendar Helper (Renderer)Notion Helper (Renderer)Claude Helper (Renderer)claudeFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentiTerm2FirefoxCP Isolated Web ContentscreenpipeMEMORY PRESSUREMem...2,04 GB1,21 GB1,00 GB963,1 MB840,5 MB809,8 MB794,5 MB547,6 MB544,2 MB543,9 MB516,1 MB499,2 MB477,4 MB425,2 MB421,4 MB397,2 MB393,9 MB388,7 MB372,5 MB343,6 MB332,3 MB327,1 MB326,6 MB297,0 MB270,5 MB251,2 MB244,4 MB206,2 MBPhysical Memory:Memory Used:Cached Files:Swap Used:100% <478Wed 15 Apr 17:54:36CPUMemoryDiskThreads3923722687282429271223162427262327221520151328282760EnergyPorts60619 8087231241 20512920 047125242254168119199123125126119125118172313219721251281 835124519PID93892407801442974146644203084236713801914673938993548041863352763583143016368984365248173265481148605195091035833482984878561384287616,00 GB14,18 GB <1,77 GB3,13 GBApp Memory:Wired Memory:Compressed:NetworkUserlukas_windowserverlukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukas3,71 GB2,91 GB7,02 GB...
|
NULL
|
-8546028034288102907
|
NULL
|
click
|
ocr
|
NULL
|
+SlackFileEditViewGoEDHomeDMSActivityFilesLater..• +SlackFileEditViewGoEDHomeDMSActivityFilesLater..•More+→Jiminny ...+CHISMICCIIS# frontend# general# infra-changes# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of _jimi...Direct messagesAneliya Angelova, ...Stoyan Tanev® Ves. Galya Dimitrova€. Vasil VasilevR. Steliyan GeorgievAdelina Petrova, Ili...P. Adelina PetrovaR. Nikolay Nikolovii: AppsJira CloudToastHistoryWindowHelpSearch Jiminny Inc# releases8 226 0MessagesO FilesBookmarks+6e938eS2ir- Enhance StaLiX workflow withSlack iv 2 new messagesShow morejiminny/app Added by GitHubNewCircleCl APP3:53 PMDeployment Successful!Project: appWhen:04/15/202612:53:30Tag:View JobGitHub APP5:54 PM10 new commits pushed to master by yalokin-jiminny630fd8f9 - SRD-6779 |JY-20632 | Unableto log in to Sidekick with SSO0f38589b - SRD-6779 |JY-20632 | Add log4dd5718e - SRD-6779 |JY-20632 | minorimprovementb1e544db - SRD-6779 |JY-20632 | addtests8bd0ef70 - SRD-6779 |JY-20632 | addtestsShow morejiminny/app| Added by GitHubMessage #releases+AaActivity MonitorAll ProcessesProcess NameBoosteroidWindowServerFirefoxFirefoxCP Isolated Web ContentFirefoxFirefoxCP Isolated Web ContentCursorUlViewService (Not Responding)FirefoxCP Isolated Web ContentFirefox GPU HelperFirefox GPU HelperVTDecoderXPCServiceFirefoxCP Isolated Web ContentSlack Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentNotion Calendar Helper (Renderer)Notion Helper (Renderer)Claude Helper (Renderer)claudeFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentiTerm2FirefoxCP Isolated Web ContentscreenpipeMEMORY PRESSUREMem...2,04 GB1,21 GB1,00 GB963,1 MB840,5 MB809,8 MB794,5 MB547,6 MB544,2 MB543,9 MB516,1 MB499,2 MB477,4 MB425,2 MB421,4 MB397,2 MB393,9 MB388,7 MB372,5 MB343,6 MB332,3 MB327,1 MB326,6 MB297,0 MB270,5 MB251,2 MB244,4 MB206,2 MBPhysical Memory:Memory Used:Cached Files:Swap Used:100% <478Wed 15 Apr 17:54:36CPUMemoryDiskThreads3923722687282429271223162427262327221520151328282760EnergyPorts60619 8087231241 20512920 047125242254168119199123125126119125118172313219721251281 835124519PID93892407801442974146644203084236713801914673938993548041863352763583143016368984365248173265481148605195091035833482984878561384287616,00 GB14,18 GB <1,77 GB3,13 GBApp Memory:Wired Memory:Compressed:NetworkUserlukas_windowserverlukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukas3,71 GB2,91 GB7,02 GB...
|
30046
|
|
56542
|
1220
|
57
|
2026-04-20T11:14:48.617251+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776683688617_m2.jpg...
|
PhpStorm
|
PhpStorm
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormVIewcodeWindowmelpFV faVsco.js#11894 on JY PhostormVIewcodeWindowmelpFV faVsco.js#11894 on JY-18909-automated-reports-askProletey"C) ActivitvController.ohom DtocC) AutomatedReportsRepository.onpAutomatedkeportsservice.ongD Events©ASKAnytningPromptservice.or© AutomatedReportsController.phpnp apl_vz.ongc Historyservice.ongclass askanyuhinorromorservice_ ASKJIminnyAlWAWS0 BillingManagement* @param array<string> $shareUsersUvids* doaram arrau<string> SshareGroupsuuldsu cacheououc tunction createldw countryUser Suser.D CustomerApstrina StitlelDatabaseStrina Scontent.→ DatadogAskAnvthinaPromottarget StargetiDatettimearrav SsharelsersUuids.Dealinsiahtsarray $shareGroupsUuidsN DealRisks): AskAnythingPromptDto t...}1N GlasticSearchM EloquentEncoding* @param array<string> $shareUsersUuids• Methodl(m d create AskAnythingPromptService .../app/Component/AskAnythingv Usages in Project Files 2 resultsv Method call 2 resultsv Ca app 2 resultsv Mann/Httn/Controllerc/API/V2 1 resulv AskAnythingController.php 1 resultv C) AckAnvthinaController 1 recultv md crontodckAnvthinaDromnt 1rocult3z spromptato = Sthis->askAnythingPromptService->create(> @ tests/Unit/Component/AskAnything 1 result100% S2Mon 20 Apr 14:14:48© AskJiminnyReportActivityService.phpU AskJiminnyReportActivityServiceTest v« console [EU] X console [STAGING]© AutomatedReportsCommand.php© AutomatedReport.php= custom.log= laravel.log4 SF (jiminny@localhost]4 HS_local [jiminny@localhost]& console [Pkol)Tx: Auto vPlavaroundvJOIN business process stages bps 1<->1..n: ON bps.stage id = s.io161681 M2 ^ Y.1611whEkE Dos.DUS1ness orocess 10 = 0024AND S.crm provider 1d = "contractsent'*11618select * from stages where id IN (16352,20612,18281, 7344, 16378, 16309, 5036, 15223, 14535, 6293, 12098, 11607)_SELECT * FROM teams WHERE name LIKE "%PUlsar Groupx"; # 472, 580, 15158, [EMAIL] * +rom plavbooks where team 1d = 472: # event 226147SELECT * FROM playbook_categories WHERE playbook_id = 2288;SELEC * FROM com fields WHERE 1d = 226147÷1SELECT * FROM crm field values WHERE com field id = 226147:SELECT * FROM crm_configurations WHERE id = 380;SSLEMCONCAT(u.id, CASE WHEN U.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,sa.*,t.owner_id FROM social_accounts saSo jiminny027 A9 A23 У.3 У 105 ^= w=10 Ml WESe WPSelect item to previevCall HierarchyW Windsurf Teams 69:25 UTF-8 Po 4 spaces...
|
NULL
|
-8545933745760184132
|
NULL
|
click
|
ocr
|
NULL
|
PhostormVIewcodeWindowmelpFV faVsco.js#11894 on JY PhostormVIewcodeWindowmelpFV faVsco.js#11894 on JY-18909-automated-reports-askProletey"C) ActivitvController.ohom DtocC) AutomatedReportsRepository.onpAutomatedkeportsservice.ongD Events©ASKAnytningPromptservice.or© AutomatedReportsController.phpnp apl_vz.ongc Historyservice.ongclass askanyuhinorromorservice_ ASKJIminnyAlWAWS0 BillingManagement* @param array<string> $shareUsersUvids* doaram arrau<string> SshareGroupsuuldsu cacheououc tunction createldw countryUser Suser.D CustomerApstrina StitlelDatabaseStrina Scontent.→ DatadogAskAnvthinaPromottarget StargetiDatettimearrav SsharelsersUuids.Dealinsiahtsarray $shareGroupsUuidsN DealRisks): AskAnythingPromptDto t...}1N GlasticSearchM EloquentEncoding* @param array<string> $shareUsersUuids• Methodl(m d create AskAnythingPromptService .../app/Component/AskAnythingv Usages in Project Files 2 resultsv Method call 2 resultsv Ca app 2 resultsv Mann/Httn/Controllerc/API/V2 1 resulv AskAnythingController.php 1 resultv C) AckAnvthinaController 1 recultv md crontodckAnvthinaDromnt 1rocult3z spromptato = Sthis->askAnythingPromptService->create(> @ tests/Unit/Component/AskAnything 1 result100% S2Mon 20 Apr 14:14:48© AskJiminnyReportActivityService.phpU AskJiminnyReportActivityServiceTest v« console [EU] X console [STAGING]© AutomatedReportsCommand.php© AutomatedReport.php= custom.log= laravel.log4 SF (jiminny@localhost]4 HS_local [jiminny@localhost]& console [Pkol)Tx: Auto vPlavaroundvJOIN business process stages bps 1<->1..n: ON bps.stage id = s.io161681 M2 ^ Y.1611whEkE Dos.DUS1ness orocess 10 = 0024AND S.crm provider 1d = "contractsent'*11618select * from stages where id IN (16352,20612,18281, 7344, 16378, 16309, 5036, 15223, 14535, 6293, 12098, 11607)_SELECT * FROM teams WHERE name LIKE "%PUlsar Groupx"; # 472, 580, 15158, [EMAIL] * +rom plavbooks where team 1d = 472: # event 226147SELECT * FROM playbook_categories WHERE playbook_id = 2288;SELEC * FROM com fields WHERE 1d = 226147÷1SELECT * FROM crm field values WHERE com field id = 226147:SELECT * FROM crm_configurations WHERE id = 380;SSLEMCONCAT(u.id, CASE WHEN U.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,sa.*,t.owner_id FROM social_accounts saSo jiminny027 A9 A23 У.3 У 105 ^= w=10 Ml WESe WPSelect item to previevCall HierarchyW Windsurf Teams 69:25 UTF-8 Po 4 spaces...
|
NULL
|
|
72622
|
1770
|
27
|
2026-04-22T15:58:36.191711+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-22/1776 /Users/lukas/.screenpipe/data/data/2026-04-22/1776873516191_m1.jpg...
|
Control Centre
|
Control Centre
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Wi‑Fi
Focus
Bluetooth
AirDrop
Stage Manager
Screen Wi‑Fi
Focus
Bluetooth
AirDrop
Stage Manager
Screen Mirroring
Display
Sound
Airplay Audio
Freelancers & Teams: Get Automatic Time Tracking + Distraction Protection in One Tool, RescueTime
pause
next...
|
[{"role":"AXCheckBox","text [{"role":"AXCheckBox","text":"Wi‑Fi","depth":2,"bounds":{"left":0.79583335,"top":0.051666666,"width":0.093055554,"height":0.045555554},"automation_id":"controlcenter-wifi","role_description":"toggle button","subrole":"AXToggle","is_enabled":true},{"role":"AXCheckBox","text":"Focus","depth":2,"bounds":{"left":0.8958333,"top":0.044444446,"width":0.093055554,"height":0.06888889},"automation_id":"controlcenter-focus-modes","role_description":"toggle button","subrole":"AXToggle","is_enabled":true},{"role":"AXCheckBox","text":"Bluetooth","depth":2,"bounds":{"left":0.79583335,"top":0.09611111,"width":0.093055554,"height":0.045555554},"automation_id":"controlcenter-bluetooth","role_description":"toggle button","subrole":"AXToggle","is_enabled":true},{"role":"AXCheckBox","text":"AirDrop","depth":2,"bounds":{"left":0.79583335,"top":0.14055556,"width":0.093055554,"height":0.045555554},"automation_id":"controlcenter-airdrop","role_description":"toggle button","subrole":"AXToggle","is_enabled":true},{"role":"AXButton","text":"Stage Manager","depth":2,"bounds":{"left":0.90260416,"top":0.135,"width":0.029513888,"height":0.050555557},"automation_id":"controlcenter-stagemanager","role_description":"toggle button","subrole":"AXToggle","is_enabled":true},{"role":"AXButton","text":"Screen Mirroring","depth":2,"bounds":{"left":0.9458333,"top":0.12444445,"width":0.043055557,"height":0.06888889},"automation_id":"controlcenter-screen-mirroring","role_description":"toggle button","subrole":"AXToggle","is_enabled":true},{"role":"AXStaticText","text":"Display","depth":3,"bounds":{"left":0.80277777,"top":0.21166667,"width":0.029513888,"height":0.016666668},"role_description":"text"},{"role":"AXStaticText","text":"Sound","depth":2,"bounds":{"left":0.80277777,"top":0.29166666,"width":0.025694445,"height":0.016666668},"automation_id":"controlcenter-volume","role_description":"text"},{"role":"AXCheckBox","text":"Airplay Audio","depth":2,"bounds":{"left":0.9638889,"top":0.31333333,"width":0.018055556,"height":0.028888889},"automation_id":"controlcenter-airplay","role_description":"toggle button","subrole":"AXToggle","is_enabled":true},{"role":"AXStaticText","text":"Freelancers & Teams: Get Automatic Time Tracking + Distraction Protection in One Tool, RescueTime","depth":2,"bounds":{"left":0.8375,"top":0.38333333,"width":0.099652775,"height":0.031111112},"role_description":"text"},{"role":"AXButton","text":"pause","depth":2,"bounds":{"left":0.9458333,"top":0.38444445,"width":0.018055556,"height":0.028888889},"role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"next","depth":2,"bounds":{"left":0.9638889,"top":0.38444445,"width":0.018055556,"height":0.028888889},"role_description":"button","is_enabled":false}]...
|
-8545831866306753471
|
8682004382524819492
|
click
|
accessibility
|
NULL
|
Wi‑Fi
Focus
Bluetooth
AirDrop
Stage Manager
Screen Wi‑Fi
Focus
Bluetooth
AirDrop
Stage Manager
Screen Mirroring
Display
Sound
Airplay Audio
Freelancers & Teams: Get Automatic Time Tracking + Distraction Protection in One Tool, RescueTime
pause
next...
|
NULL
|
|
42183
|
895
|
26
|
2026-04-17T06:51:07.565322+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-17/1776 /Users/lukas/.screenpipe/data/data/2026-04-17/1776408667565_m2.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
FirefoxFileEoitViewHistory BookmarksProfilesToolsW FirefoxFileEoitViewHistory BookmarksProfilesToolsWindow Helpapp.datadoghq.com/dashboard/8dc-jtf-87d/workers?fromUser=false&refresh_moc& Workers vaws_account-Platform Sprint 2 Q2 - Platform TeSaved Views vproc[SRD-6793] Les Mills activity type:DATADOGNew TabQ Go to...36 + KSymfony|Component\Debug\Excer+* Bits AICloudWatch | us-east-2DashboardsZ Configure SSH access to multipleE Monitoring@ Console Home | Console Home | elE) Incident ResponseNew Tab4 Automation+ New Tabgo Infrastructure$ Cloud CostF APMa7 Digital ExperienceS Software Delivery* SecurityFa Data ObservabilityC AI ObservabilityXừ Errors(%) MetricsFõ LogsFilter by:Món 13lue 14Món 13Average Work TimeMessages ReceivedMon 13Mon 13Messages SentMessages Processed SuccessfullyMón 13Món 13Ees service kunning/rendingMessages Deletedmillypatm2kdeatbleenMon 13lue 14Món 13ECS ContainerInsiahts Service CPU Reserve….ECS Service CPUsoMón 13Tue 14Món 13ECS Service Memory40 lil • | Daily - Platform • nowA100% [[ ShareAnomalies& Show Overlays@ ConfigureApr 13, 12:00 am - Apr 14, 11:59 pm8 • Fri 17 Apr 9:51:07Request Edit AccessIn1.Tue 14Messages ReceivedMessages In-Flight20Messages In-FlightMon 13SQS Empty ReceivesMon 13tes contalnerinsignts service lask countMon 13enlloeApproximate Age or Uldest Message20Món 13Approximate Number of Messages VisibleAverage Work TimeMón 13Messages SentMon 13ECS Service Running/Pendingmds.MhMon 13ECS ContainerInsiahts Service CPU Reserve.Món 13Tue 14Messages Processed SuccessfullyMon 13Messages DeletedECS Service CPU10050Món 13Tue 14ECS Service MemorySQS Empty ReceivesMon 13Tue 14ECS ContainerInsights Service Task CountMon 13Tue 14Approximate Age of Oldest MessageMon 13Approximate Number of Messages Visibler [EMAIL], IncNEWsupportHelpMón 13Tue 14Mon 13Top Jobs188.12jiminny.component.languagedetection.jobs.detectactivitylanguagejob168.68jiminny.services.recallai.webhooks...ndlers.botstatuschangehandlerjobjiminny.component.activityanalytics.job.analyzeactivitytopictriggers11O.9OJminny.services.recallal..oos.createbotoo3.41minny.services.recallal.oos.lmportootrecoraineloo29.1511.95liminny.services.recallal.loos.senachatmessageloo5.33uminnv.services.recallal.loos.removeoocromcalliooSlowest Jobs1845jmmmmy.services.lecdlidi.weunlooks.lldnulers.0o/tdtuscridngendiurergor1755mmmy.componenulaneudgeuerecuom..o0s.detectdctrvitylanlgudue.orjiminny.services.recallai.jobs.createbotjoblminny.services.recallal. oos.derereoot.o011341010lminny.services.recallal. oos.senaaualonotiticationooliminny.services.recallal.loos.senachatmessagelobjiminny.component.activityanalytics.job.analyzeactivitytopictriggersNudgesDYNAMIC GROUPAverage Wait TimeMax Wait TimeMax Work TimeMon 13Top Jobs6121jiminny.jobs.user.synctointercomjiminny.jobs.audio.deleteremotetrackJminny.lsteners.acuvidles.soronone.userpllot.creacealaleracuivicyevent_minny.lsteners.acuvlules.soronone.olannat.createolaleracuvitvevent_minny.lsteners.acuvlules.soronone.intercom.credrealaleracuvityeventiminny.lobs.team.synctointercomjiminny.component.aiautomation.li...endingaianalysisafterstagechangedTue 14Tue 14Slowest Jobs7.35 min1.8/13.342.231.52jiminny.jobs.activity.deleteteamsretentiondatammmy.component.dldutomidtlo:.....unopporturmlydidfidlysislistenet_minny.component.alautomacion..os.alautomatlonanalysisreaay.oiminny.componenc.alautomation..ners.runacuvityalanalysislistenerlminny.oos.ceam.synctoolannatjiminny.jobs.mailbox.deleteemailmessagesjobjiminny.jobs.team.synctointercom...
|
NULL
|
-8545598758687491646
|
NULL
|
visual_change
|
ocr
|
NULL
|
FirefoxFileEoitViewHistory BookmarksProfilesToolsW FirefoxFileEoitViewHistory BookmarksProfilesToolsWindow Helpapp.datadoghq.com/dashboard/8dc-jtf-87d/workers?fromUser=false&refresh_moc& Workers vaws_account-Platform Sprint 2 Q2 - Platform TeSaved Views vproc[SRD-6793] Les Mills activity type:DATADOGNew TabQ Go to...36 + KSymfony|Component\Debug\Excer+* Bits AICloudWatch | us-east-2DashboardsZ Configure SSH access to multipleE Monitoring@ Console Home | Console Home | elE) Incident ResponseNew Tab4 Automation+ New Tabgo Infrastructure$ Cloud CostF APMa7 Digital ExperienceS Software Delivery* SecurityFa Data ObservabilityC AI ObservabilityXừ Errors(%) MetricsFõ LogsFilter by:Món 13lue 14Món 13Average Work TimeMessages ReceivedMon 13Mon 13Messages SentMessages Processed SuccessfullyMón 13Món 13Ees service kunning/rendingMessages Deletedmillypatm2kdeatbleenMon 13lue 14Món 13ECS ContainerInsiahts Service CPU Reserve….ECS Service CPUsoMón 13Tue 14Món 13ECS Service Memory40 lil • | Daily - Platform • nowA100% [[ ShareAnomalies& Show Overlays@ ConfigureApr 13, 12:00 am - Apr 14, 11:59 pm8 • Fri 17 Apr 9:51:07Request Edit AccessIn1.Tue 14Messages ReceivedMessages In-Flight20Messages In-FlightMon 13SQS Empty ReceivesMon 13tes contalnerinsignts service lask countMon 13enlloeApproximate Age or Uldest Message20Món 13Approximate Number of Messages VisibleAverage Work TimeMón 13Messages SentMon 13ECS Service Running/Pendingmds.MhMon 13ECS ContainerInsiahts Service CPU Reserve.Món 13Tue 14Messages Processed SuccessfullyMon 13Messages DeletedECS Service CPU10050Món 13Tue 14ECS Service MemorySQS Empty ReceivesMon 13Tue 14ECS ContainerInsights Service Task CountMon 13Tue 14Approximate Age of Oldest MessageMon 13Approximate Number of Messages Visibler [EMAIL], IncNEWsupportHelpMón 13Tue 14Mon 13Top Jobs188.12jiminny.component.languagedetection.jobs.detectactivitylanguagejob168.68jiminny.services.recallai.webhooks...ndlers.botstatuschangehandlerjobjiminny.component.activityanalytics.job.analyzeactivitytopictriggers11O.9OJminny.services.recallal..oos.createbotoo3.41minny.services.recallal.oos.lmportootrecoraineloo29.1511.95liminny.services.recallal.loos.senachatmessageloo5.33uminnv.services.recallal.loos.removeoocromcalliooSlowest Jobs1845jmmmmy.services.lecdlidi.weunlooks.lldnulers.0o/tdtuscridngendiurergor1755mmmy.componenulaneudgeuerecuom..o0s.detectdctrvitylanlgudue.orjiminny.services.recallai.jobs.createbotjoblminny.services.recallal. oos.derereoot.o011341010lminny.services.recallal. oos.senaaualonotiticationooliminny.services.recallal.loos.senachatmessagelobjiminny.component.activityanalytics.job.analyzeactivitytopictriggersNudgesDYNAMIC GROUPAverage Wait TimeMax Wait TimeMax Work TimeMon 13Top Jobs6121jiminny.jobs.user.synctointercomjiminny.jobs.audio.deleteremotetrackJminny.lsteners.acuvidles.soronone.userpllot.creacealaleracuivicyevent_minny.lsteners.acuvlules.soronone.olannat.createolaleracuvitvevent_minny.lsteners.acuvlules.soronone.intercom.credrealaleracuvityeventiminny.lobs.team.synctointercomjiminny.component.aiautomation.li...endingaianalysisafterstagechangedTue 14Tue 14Slowest Jobs7.35 min1.8/13.342.231.52jiminny.jobs.activity.deleteteamsretentiondatammmy.component.dldutomidtlo:.....unopporturmlydidfidlysislistenet_minny.component.alautomacion..os.alautomatlonanalysisreaay.oiminny.componenc.alautomation..ners.runacuvityalanalysislistenerlminny.oos.ceam.synctoolannatjiminny.jobs.mailbox.deleteemailmessagesjobjiminny.jobs.team.synctointercom...
|
42182
|
|
18609
|
395
|
75
|
2026-04-14T16:21:57.174804+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-14/1776 /Users/lukas/.screenpipe/data/data/2026-04-14/1776183717174_m2.jpg...
|
Boosteroid
|
Boosteroid
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
75416422636/45Castle Age--Gate Built--Farm Exhaust 75416422636/45Castle Age--Gate Built--Farm Exhausted--Right-click to repair this building to its fullHP. The repair cost is deducted from yourstockpile.6 Mindaugas: 2143/21435 Honorius: 2129/21294 Siddhraj Jaisingh: 2102/2102 €7 Basil the Macedonian: 2037/20373 Bird Jaguar: 2020/20208 Ashikaga Takauji: 1967/19671 kovaliklukas: 1863/18632 Anceu Hualloc: 1842/1842BBBGBEBABuilderkovalfklukas (Britons))...
|
NULL
|
-8545508958221697435
|
NULL
|
click
|
ocr
|
NULL
|
75416422636/45Castle Age--Gate Built--Farm Exhaust 75416422636/45Castle Age--Gate Built--Farm Exhausted--Right-click to repair this building to its fullHP. The repair cost is deducted from yourstockpile.6 Mindaugas: 2143/21435 Honorius: 2129/21294 Siddhraj Jaisingh: 2102/2102 €7 Basil the Macedonian: 2037/20373 Bird Jaguar: 2020/20208 Ashikaga Takauji: 1967/19671 kovaliklukas: 1863/18632 Anceu Hualloc: 1842/1842BBBGBEBABuilderkovalfklukas (Britons))...
|
NULL
|