Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 1734
0.00% covered (danger)
0.00%
0 / 82
CRAP
0.00% covered (danger)
0.00%
0 / 1
AdminAccountCenterDashboardController
0.00% covered (danger)
0.00%
0 / 1734
0.00% covered (danger)
0.00%
0 / 82
98910
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 _buildUsersResponse
0.00% covered (danger)
0.00%
0 / 53
0.00% covered (danger)
0.00%
0 / 1
210
 _make_line_chart_data
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
12
 _make_line_chart_data_for_counts
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
12
 _make_line_chart_data_extension
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
20
 _assign_plan
0.00% covered (danger)
0.00%
0 / 57
0.00% covered (danger)
0.00%
0 / 1
552
 filterUsersByAdmin
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 filterUsersByGroupIds
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
 users
0.00% covered (danger)
0.00%
0 / 82
0.00% covered (danger)
0.00%
0 / 1
812
 groups
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
30
 search_users
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
6
 update_user
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 update_invite
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
12
 create_user_manually
0.00% covered (danger)
0.00%
0 / 117
0.00% covered (danger)
0.00%
0 / 1
182
 resend_temporary_password
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 1
6
 create_user_by_csv
0.00% covered (danger)
0.00%
0 / 64
0.00% covered (danger)
0.00%
0 / 1
30
 deactivate_user
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 deactivate_invited_user
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
20
 delete_user
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 reset_password
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
 reset_invitation_password
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
 resend_user_invitation
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
6
 resend_invitations
0.00% covered (danger)
0.00%
0 / 63
0.00% covered (danger)
0.00%
0 / 1
42
 move_group
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 move_group_invite
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 assign_role
0.00% covered (danger)
0.00%
0 / 78
0.00% covered (danger)
0.00%
0 / 1
210
 deactivate_user_bulk
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 _reactive_users
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
30
 _assign_plans_to_users
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
20
 reactivate_user_bulk
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
20
 delete_user_bulk
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
56
 reset_password_bulk
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
20
 resend_user_invitation_bulk
0.00% covered (danger)
0.00%
0 / 42
0.00% covered (danger)
0.00%
0 / 1
30
 move_group_bulk
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
2
 group_exists
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
12
 create_group
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
2
 update_group
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 delete_group
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
42
 add_users_to_group
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 add_subgroup_to_group
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
2
 export_csv
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 import_csv
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 reporting_licenses
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
20
 reporting_roi_spotlight
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
20
 reporting_productivity_spotlight
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
20
 reporting_total_characters_typed_spotlight
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
20
 reporting_total_fly_grammar_spotlight
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
20
 reporting_total_fly_cuts_spotlight
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
20
 reporting_flymsg_coach_level
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
20
 reporting_flycuts_created
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
20
 reporting_flycuts_created_top_users
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 reporting_flyplates_added
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
20
 reporting_flyplates_added_top_users
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 reporting_sentence_rewrite_ai
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
20
 reporting_sentence_rewrite_ai_used_top_users
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 reporting_paragraph_rewrite_ai
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
20
 reporting_paragraph_rewrite_ai_used_top_users
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 reporting_flyengage_ai
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
20
 reporting_flyengage_ai_used_top_users
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 reporting_flypost_ai
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
20
 reporting_flypost_ai_used_top_users
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 getTop5
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
30
 reporting_characters_typed_saving_top_users
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 reporting_fly_grammar_top_users
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 reporting_fly_cuts_top_users
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 reporting_fly_grammar_accepted_top_users
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 reporting_fly_grammar_autocorrect_top_users
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 reporting_fly_grammar_autocomplete_top_users
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 reporting_time_saving_top_users
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 reporting_cost_saving_top_users
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 reporting_active_users
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
20
 reporting_extension_usage
0.00% covered (danger)
0.00%
0 / 68
0.00% covered (danger)
0.00%
0 / 1
156
 reporting_user_details
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
30
 reporting_get_columns
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
6
 reporting_save_columns
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 1
56
 export_user_details_csv
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
6
 reset_invited_user_password
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 1
30
 export_csv_report
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 export_csv_report_overview
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 export_csv_report_usage
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
 remove_users_from_group
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 user_info
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace App\Http\Controllers\v1\AdminPortal;
4
5use App\Actions\AccountCenter\Reporting\FmsCoachLevelSpotlightAction;
6use App\Actions\AccountCenter\Reporting\ProductivitySpotlightAction;
7use App\Actions\AccountCenter\Reporting\RoiSpotlightAction;
8use App\Actions\AccountCenter\Reporting\TotalCharactersTypedSpotlightAction;
9use App\Actions\AccountCenter\Reporting\TotalFlyCutsSpotlightAction;
10use App\Actions\AccountCenter\Reporting\TotalFlyGrammarSpotlightAction;
11use App\Actions\Users\SendUserInvitationAction;
12use App\Actions\Users\UpdateUserAction;
13use App\DTO\AccountCenter\Reporting\ReportingRequestDTO;
14use App\DTO\User\SendUserInvitationDTO;
15use App\DTO\User\UpdateUserDTO;
16use App\Events\DeleteInstancyGroup;
17use App\Events\User\Registered;
18use App\Exceptions\ExpectedException;
19use App\Exports\UsersExport;
20use App\Exports\UsersReportExport;
21use App\Exports\UsersReportOverviewExport;
22use App\Exports\UsersReportUsageExport;
23use App\Helpers\Constants;
24use App\Helpers\FlyMSGLogger;
25use App\Http\Controllers\Controller;
26use App\Http\Models\Admin\AdminUserInvitation;
27use App\Http\Models\Admin\Company;
28use App\Http\Models\Admin\CompanyGroup;
29use App\Http\Models\Admin\CompanyLicenses;
30use App\Http\Models\Admin\UserDetailsColumnSetting;
31use App\Http\Models\Auth\Role;
32use App\Http\Models\Auth\User;
33use App\Http\Models\Auth\UserRole;
34use App\Http\Models\FlyMsgUserDailyUsage;
35use App\Http\Models\HubspotProperties;
36use App\Http\Models\Plans;
37use App\Http\Models\SalesProTeamManager;
38use App\Http\Resources\AdminCenterCompanyGroupResource;
39use App\Http\Resources\AdminCenterUserResource;
40use App\Http\Resources\UserDetailsReportingResource;
41use App\Http\Resources\UserDetailsReportResource;
42use App\Http\Services\Admin\Groups\AdminGroupsService;
43use App\Http\Services\Admin\Reports\AccountCenterReportingService;
44use App\Http\Services\Admin\Reports\FindUsersOverviewFilter;
45use App\Http\Services\Admin\Users\AdminUsersService;
46use App\Http\Services\CsvExportService;
47use App\Http\Services\InstancyServiceV2;
48use App\Imports\UsersImport;
49use App\Jobs\Emails\AddNewUserNotification;
50use App\Jobs\Emails\AddNewUserReminder;
51use App\Jobs\Emails\ReactivateUserNotification;
52use App\Mail\AdminCenterAddExistingUser;
53use App\Mail\AdminCenterAddNewUser;
54use App\Mail\AdminCenterAddNewUserByEmail;
55use App\Mail\InvitationToJoinFlymsgEmail;
56use App\Mail\SendResetPasswordRequestedEmail;
57use App\Services\Email\EmailService;
58use App\Traits\AccountCenter\Reporting\ChartTrait;
59use Carbon\Carbon;
60use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
61use Illuminate\Http\JsonResponse;
62use Illuminate\Http\Request;
63use Illuminate\Support\Facades\Hash;
64use Illuminate\Support\Facades\Password;
65use Illuminate\Support\Str;
66use Illuminate\Validation\Rule;
67use MongoDB\BSON\UTCDateTime;
68
69class AdminAccountCenterDashboardController extends Controller
70{
71    use ChartTrait;
72    use SendsPasswordResetEmails;
73
74    public function __construct(
75        private AdminUsersService $adminUsersService,
76        private AdminGroupsService $adminGroupsService,
77        private AccountCenterReportingService $accountCenterReportingService,
78        private EmailService $emailService,
79        private CsvExportService $csvExportService
80    ) {}
81
82    private function _buildUsersResponse($model, $group_name, $subgroup_name, $is_invite, $company)
83    {
84        $identifier = 'freemium';
85        if ($model->subscription('main') && $model->subscription('main')->valid() && $model->subscription('main')->plan) {
86            $identifier = $model->subscription('main')->plan->identifier;
87        } elseif ($model->subscription('invitation')) {
88            $identifier = $model->subscription('invitation')->identifier;
89        } elseif ($model->status == 'Invited') {
90            $invitation = AdminUserInvitation::where('email', $model->email)->first();
91            if (filled($invitation?->plan_id)) {
92                $plan = Plans::find($invitation->plan_id);
93                $identifier = $plan?->identifier ?? 'freemium';
94            }
95        }
96
97        $plan_name = $this->parseSubscriptionPlanName($identifier);
98
99        $statusDate = match (true) {
100            ! is_null($model->deactivated_at)
101                ? ($model->deactivated_at instanceof \Carbon\Carbon
102                    ? $model->deactivated_at->toFormattedDateString()
103                    : \Carbon\Carbon::parse($model->deactivated_at)->toFormattedDateString())
104                : null,
105            ! is_null($model->activation_date) => Carbon::parse($model->activation_date)->toFormattedDateString(),
106            ! is_null($model->created_at) => $model->created_at->toFormattedDateString(),
107            default => null,
108        };
109
110        $status = $model->status ?? 'Active';
111
112        $user_properties = [
113            'id' => $model->id,
114            'avatar' => $model->avatar,
115            'first_name' => $model->first_name,
116            'last_name' => $model->last_name,
117            'email' => $model->email,
118            'role' => role($model->role),
119            'group' => $group_name,
120            'sub_group' => $subgroup_name,
121            'group_subgroup' => $subgroup_name != 'Not Assigned' ? $subgroup_name : $group_name,
122            'licenseType' => $plan_name,
123            'status' => $status,
124            'invitation_link' => $model->getInvitationLinkForAdminPortal(),
125            'created_at' => $model->created_at ?? $model->updated_at,
126            'user_id' => $model->id,
127            'group_id' => $model->company_group_id,
128            'group_subgroup_id' => $model->company_group_id,
129            'statusDate' => $statusDate,
130            'is_invite' => $is_invite,
131            'company_slug' => $company->slug,
132        ];
133
134        if ($user_properties['status'] == 'Invited') {
135            $user_properties['invitation_link'] = $model->getInvitationLinkForAdminPortal();
136            $invitation = AdminUserInvitation::where('email', $model->email)->first();
137            if ($invitation) {
138                $user_properties['statusDate'] = $invitation?->updated_at?->toFormattedDateString();
139
140                if (filled($invitation->role_name)) {
141                    $user_properties['role'] = $invitation->role_name;
142                }
143            }
144        }
145
146        if ($model->sales_pro_team_manager) {
147            $user_properties['groups_admin'] = $model->sales_pro_team_manager?->company_group_ids;
148        }
149
150        return $user_properties;
151    }
152
153    private function _make_line_chart_data($flycut_usages, $months)
154    {
155        // Initialize the line chart data array
156        $line_chart_data = [];
157        $current_date = Carbon::now();
158        for ($i = 0; $i < $months; $i++) {
159            $month_year = $current_date->subMonth()->format('M Y');
160            $line_chart_data[$month_year] = [
161                'month_year' => $month_year,
162                'flycuts_used' => 0,
163                'characters_typed' => round(0, 2),
164                'time_saved' => round(0, 2),
165                'cost_saved' => round(0, 2),
166            ];
167        }
168
169        // Aggregate the data by month
170        $aggregated_data = $flycut_usages->groupBy(function ($date) {
171            return Carbon::parse($date->created_at)->format('M Y');
172        });
173
174        // Process the aggregated data
175        foreach ($aggregated_data as $month_year => $usages) {
176            $month = Carbon::parse($usages->first()->created_at)->format('M Y');
177
178            $flycuts_used = $usages->count();
179            $characters_typed = $usages->sum('characters_typed');
180            $time_saved = $usages->sum('time_saved');
181            $cost_saved = $usages->sum('cost_saved');
182
183            $line_chart_data[$month] = [
184                'month_year' => $month_year,
185                'flycuts_used' => $flycuts_used,
186                'characters_typed' => round($characters_typed, 2),
187                'time_saved' => round($time_saved, 2),
188                'cost_saved' => round($cost_saved, 2),
189            ];
190        }
191
192        // Convert month-year strings to DateTime objects and sort by date
193        uksort($line_chart_data, function ($a, $b) {
194            return strtotime($a) - strtotime($b);
195        });
196
197        return $line_chart_data;
198    }
199
200    private function _make_line_chart_data_for_counts($flycuts, $months)
201    {
202        // Initialize an array with 12 months set to 0
203        $line_chart_data = [];
204        $current_date = Carbon::now();
205        for ($i = 0; $i < $months; $i++) {
206            $month_year = $current_date->subMonth()->format('M Y');
207            $line_chart_data[$month_year] = ['count' => 0];
208        }
209
210        // Reset the current date to now
211        $current_date = Carbon::now();
212
213        // Aggregate the data by month
214        $aggregated_data = $flycuts->groupBy(function ($date) {
215            return Carbon::parse($date->created_at)->format('Y-m');
216        });
217
218        // Process the aggregated data and populate the line chart data array
219        foreach ($aggregated_data as $month_year => $usages) {
220            $month = Carbon::parse($usages->first()->created_at)->format('M Y');
221            $flycuts_used = $usages->count();
222            $line_chart_data[$month] = ['count' => $flycuts_used];
223        }
224
225        // Ensure the data is sorted by date keys
226        uksort($line_chart_data, function ($a, $b) {
227            return strtotime($a) - strtotime($b);
228        });
229
230        return $line_chart_data;
231    }
232
233    private function _make_line_chart_data_extension($extensions_data, $months, $start_date)
234    {
235        $line_chart_data = [];
236        for ($i = 0; $i <= $months; $i++) {
237            $month_year = $start_date->copy()->addMonths($i)->format('M Y');
238            $line_chart_data[$month_year] = [
239                'edge_installed' => 0,
240                'chrome_installed' => 0,
241                'uninstalls' => 0,
242            ];
243        }
244
245        $aggregated_data = $extensions_data->groupBy(function ($data) {
246            return Carbon::parse($data->created_at)->format('M Y');
247        });
248
249        foreach ($aggregated_data as $month_year => $usages) {
250            $edge_count = $usages->where('flymsg_edge_extension_installed', 'Yes')->count();
251            $chrome_count = $usages->where('flymsg_chrome_extension_installed', 'Yes')->count();
252            $uninstall_count = $usages->where('flymsg_chrome_extension_uninstalled', 'Yes')->count() + $usages->where('flymsg_edge_extension_uninstalled', 'Yes')->count();
253
254            if (isset($line_chart_data[$month_year])) {
255                $line_chart_data[$month_year]['edge_installed'] += $edge_count;
256                $line_chart_data[$month_year]['chrome_installed'] += $chrome_count;
257                $line_chart_data[$month_year]['uninstalls'] += $uninstall_count;
258            }
259        }
260
261        uksort($line_chart_data, function ($a, $b) {
262            return strtotime($a) - strtotime($b);
263        });
264
265        return $line_chart_data;
266    }
267
268    private function _assign_plan($user, $plan, $selected_plan, $email, $is_reactivate = false)
269    {
270        if ($plan) {
271            $company_license = CompanyLicenses::where('company_id', $user->company_id)
272                ->active()
273                ->first();
274            if (! $company_license) {
275                throw new ExpectedException("Failed to assign $selected_plan. The company doesn't have any active license.");
276            }
277
278            switch ($selected_plan) {
279                case 'Starter':
280                case 'Starter Yearly':
281                    if ($company_license->total_starter_license_remaining < 1) {
282                        // throw new ExpectedException("Failed to assign $selected_plan. License plan unavailable");
283                    }
284                    $company_license->decrement('total_starter_license_remaining');
285                    break;
286                case 'Growth':
287                case 'Growth Yearly':
288                    if ($company_license->total_growth_license_remaining < 1) {
289                        // throw new ExpectedException("Failed to assign $selected_plan. License plan unavailable");
290                    }
291                    $company_license->decrement('total_growth_license_remaining');
292                    break;
293                case 'Sales Pro':
294                case 'FlyMSG.io Sales Pro - YR Plan':
295                    if ($company_license->total_sales_pro_license_remaining < 1) {
296                        // throw new ExpectedException("Failed to assign $selected_plan. License plan unavailable");
297                    }
298                    $company_license->decrement('total_sales_pro_license_remaining');
299                    break;
300                case 'Sales Pro Teams':
301                case 'Pro Plan Teams - ENT':
302                    if ($company_license->total_sales_pro_teams_license_remaining < 1) {
303                        // throw new ExpectedException("Failed to assign $selected_plan. License plan unavailable");
304                    }
305                    $company_license->decrement('total_sales_pro_teams_license_remaining');
306                    break;
307            }
308
309            if ($user instanceof User) {
310                if ($user->subscribed('main')) {
311                    if ($is_reactivate) {
312                        $user->subscription('main')->markAsCancelledOnlyInDB();
313                    } else {
314                        $user->subscription('main')->cancel();
315                    }
316                } else {
317                    $subscription = $user->subscriptions()->latest()->first();
318                    if ($subscription) {
319                        $subscription->update([
320                            'stripe_status' => 'canceled',
321                            'ends_at' => Carbon::now()->toDateTimeString(),
322                        ]);
323                    }
324                }
325
326                $end_date = $company_license->contract_end_date;
327
328                $user->subscriptions()->create([
329                    'name' => 'main',
330                    'stripe_status' => 'active',
331                    'stripe_plan' => $plan->stripe_id,
332                    'quantity' => '1',
333                    'ends_at' => $end_date,
334                    'starts_at' => Carbon::now()->toDateTimeString(),
335                ]);
336
337                if (in_array($selected_plan, ['Sales Pro', 'Sales Pro Teams'])) {
338                    try {
339                        $group = CompanyGroup::find($user->company_group_id);
340                        $company = Company::find($user->company_id);
341
342                        $groupId = false;
343                        if ($company) {
344                            $groupId = $company->instancy_id;
345                        } elseif ($group) {
346                            $groupId = $group->instancy_id;
347                        }
348
349                        (new InstancyServiceV2)->createInstancyUser($email, $groupId);
350                    } catch (\Throwable $th) {
351                        FlyMSGLogger::logError(__METHOD__, $th);
352                    }
353                }
354            }
355        }
356    }
357
358    private function filterUsersByAdmin($query, $role, $company_id, $admin_group_ids)
359    {
360        return match ($role) {
361            Role::GLOBAL_ADMIN, Role::VENGRESO_ADMIN => $query->where('company_id', '=', $company_id),
362            default => $query->whereIn('company_group_id', $admin_group_ids),
363        };
364    }
365
366    private function filterUsersByGroupIds($query, $role, $group_ids, $company_id, $admin_group_ids)
367    {
368        if ($role == Role::GLOBAL_ADMIN || $role == Role::VENGRESO_ADMIN) {
369            return $query->where('company_id', '=', $company_id);
370        } elseif (count($group_ids) == 0) {
371            return $query->whereIn('company_group_id', $admin_group_ids);
372        }
373
374        return $query;
375    }
376
377    public function users(Request $request)
378    {
379        // Deactivated users are soft deleted users
380        $only_deactivated = $request->has('deactivated') && $request->deactivated == 'true';
381
382        $admin = auth()->user();
383        $roles = $admin->role;
384        $role = role($roles);
385        $company_id = $request->company_id;
386        $admin_group_ids = $request->admin_group_ids;
387
388        $company = Company::find($company_id);
389
390        $users = User::select([
391            'id',
392            'avatar',
393            'first_name',
394            'last_name',
395            'email',
396            'role',
397            'company_id',
398            'company_group_id',
399            'sales_pro_team_manager_id',
400            'status',
401            'created_at',
402            'updated_at',
403            'deleted_at',
404            'activation_date',
405            'deactivated_at',
406        ]);
407
408        $users = $this->filterUsersByAdmin($users, $role, $company_id, $admin_group_ids);
409
410        if ($only_deactivated) {
411            $users = $users->whereNotNull('deactivated_at');
412        } else {
413            $users = $users->whereNull('deactivated_at');
414        }
415
416        $users = $users->latest();
417        $users = $users->get();
418
419        // TODO MA. Please find a better way to do this and
420        // avoid this loop.
421        $response_builder = [];
422        foreach ($users as $user) {
423            $group_name = $user->company_group ? $user->company_group->name : 'Not Assigned';
424            $subgroup_name = $user->company_group ? $user->company_group->name : 'Not Assigned';
425
426            if ($user->company_group && $user->company_group->parent_id) {
427                $group_name = $user->company_group->parent ? $user->company_group->parent->name : 'Not Assigned';
428            }
429
430            if ($user->company_group && ! $user->company_group->parent_id) {
431                $subgroup_name = 'Not Assigned';
432            }
433
434            $user_properties = $this->_buildUsersResponse($user, $group_name, $subgroup_name, false, $company);
435
436            $response_builder[] = $user_properties;
437        }
438
439        if (! $only_deactivated) {
440            $invitations = $this->filterUsersByAdmin(
441                AdminUserInvitation::where('company_id', $company_id)
442                    ->whereNull('deactivated_at')
443                    ->whereNotIn('email', $users->pluck('email')),
444                $role,
445                $company_id,
446                $admin_group_ids
447            )->get();
448
449            foreach ($invitations as $invitation) {
450                $group_name = $invitation->company_group ? $invitation->company_group->name : 'Not Assigned';
451                $subgroup_name = $invitation->company_group ? $invitation->company_group->name : 'Not Assigned';
452
453                if ($invitation->company_group && $invitation->company_group->parent_id) {
454                    $group_name = $invitation->company_group->parent ? $invitation->company_group->parent->name : 'Not Assigned';
455                }
456
457                if ($invitation->company_group && ! $invitation->company_group->parent_id) {
458                    $subgroup_name = 'Not Assigned';
459                }
460
461                $user_properties = $this->_buildUsersResponse($invitation, $group_name, $subgroup_name, true, $company);
462
463                $response_builder[] = $user_properties;
464            }
465        } else {
466
467            $invitations = $this->filterUsersByAdmin(
468                AdminUserInvitation::where('company_id', $company_id)
469                    ->whereNotNull('deactivated_at')
470                    ->whereNotIn('email', $users->pluck('email')),
471                $role,
472                $company_id,
473                $admin_group_ids
474            )->get();
475
476            foreach ($invitations as $invitation) {
477                $group_name = $invitation->company_group ? $invitation->company_group->name : 'Not Assigned';
478                $subgroup_name = $invitation->company_group ? $invitation->company_group->name : 'Not Assigned';
479
480                if ($invitation->company_group && $invitation->company_group->parent_id) {
481                    $group_name = $invitation->company_group->parent ? $invitation->company_group->parent->name : 'Not Assigned';
482                }
483
484                if ($invitation->company_group && ! $invitation->company_group->parent_id) {
485                    $subgroup_name = 'Not Assigned';
486                }
487
488                $user_properties = $this->_buildUsersResponse($invitation, $group_name, $subgroup_name, true, $company);
489
490                $response_builder[] = $user_properties;
491            }
492        }
493
494        return response()->json(
495            data: [
496                'success' => true,
497                'users' => $response_builder,
498            ],
499            status: 200
500        );
501    }
502
503    public function groups(Request $request)
504    {
505        $user = auth()->user();
506        $roles = $user->role;
507        $role = role($roles);
508        $show_not_assigned = $request->show_not_assigned === 'true';
509        $company_id = $request->company_id;
510        $admin_group_ids = $request->admin_group_ids;
511
512        $groups = CompanyGroup::select(['id', 'name', 'company_id', 'parent_id'])
513            ->whereNull('parent_id')
514            ->where('company_id', $company_id)
515            ->with(['users', 'subgroups']);
516
517        if ($role == Role::GLOBAL_ADMIN || $role == Role::VENGRESO_ADMIN) {
518            $groups = $groups->get();
519        } else {
520            $sales_pro_manager = SalesProTeamManager::where('email', $user->email)->first();
521            if (! $sales_pro_manager) {
522                throw new ExpectedException('User role is not setup to handle [GroupAdmin/ReportingAdmin] roles well. We have been notified about it!');
523            }
524            $groups = $groups->whereIn('_id', $admin_group_ids)->get();
525        }
526
527        if ($show_not_assigned) {
528            $notAssignedGroup = new CompanyGroup;
529            $notAssignedGroup->name = 'Not Assigned';
530            $notAssignedGroup->slug = 'not-assigned';
531            $notAssignedGroup->id = -1;
532            $groups->push($notAssignedGroup);
533        }
534
535        $transformer = AdminCenterCompanyGroupResource::collection($groups);
536
537        return response()->json(data: [
538            'success' => true,
539            'groups' => $transformer->toArray($request),
540        ], status: 200);
541    }
542
543    public function search_users(Request $request)
544    {
545        $user = auth()->user();
546        $roles = $user->role;
547        $role = role($roles);
548        $search_term = $request->search_term;
549        $company_id = $request->company_id;
550        $admin_group_ids = $request->admin_group_ids;
551
552        $users = User::select([
553            'id',
554            'first_name',
555            'last_name',
556            'email',
557            'role',
558            'company_id',
559            'company_group_id',
560            'sales_pro_team_manager_id',
561            'deactivated_at',
562            'status',
563            'created_at',
564            'avatar',
565        ]);
566
567        if ($search_term) {
568            $users = $users->where('email', 'like', "%$search_term%")
569                ->orWhere('first_name', 'like', "%$search_term%")
570                ->orWhere('last_name', 'like', "%$search_term%");
571        }
572
573        $users = $this->filterUsersByAdmin($users, $role, $company_id, $admin_group_ids);
574
575        $users = $users->get();
576
577        $users_response = AdminCenterUserResource::collection($users);
578
579        return response()->json(data: [
580            'success' => true,
581            'users' => $users_response->toArray($request),
582        ], status: 200);
583    }
584
585    public function update_user(Request $request, $slug, User $user, UpdateUserAction $updateUserAction)
586    {
587        $userDTO = new UpdateUserDTO(
588            email: strtolower($request->email),
589            firstName: $request->first_name,
590            lastName: $request->last_name,
591        );
592
593        $updateUserAction->execute($user, $userDTO);
594
595        return response()->json(data: [
596            'success' => true,
597            'user' => new AdminCenterUserResource($user),
598        ], status: 200);
599    }
600
601    public function update_invite(Request $request, $slug, AdminUserInvitation $invite)
602    {
603        $inputData = $request->all();
604        $newEmail = strtolower($inputData['email']);
605        $sendEmail = strtolower($invite->email) !== $newEmail;
606        $inputData['email'] = $newEmail;
607
608        if (! $invite->update($inputData)) {
609            throw new ExpectedException('Failed to update the users data. Please try again later.');
610        }
611
612        if ($sendEmail) {
613            $this->emailService->send(
614                $invite->email,
615                new AdminCenterAddNewUserByEmail(
616                    email: $invite->email
617                ),
618                'cac_add_user_by_email'
619            );
620        }
621
622        return response()->json(data: [
623            'success' => true,
624            'user' => new AdminCenterUserResource($invite),
625        ], status: 200);
626    }
627
628    public function create_user_manually(Request $request)
629    {
630        $existing_user = false;
631
632        $validated_data = $request->validate([
633            'first_name' => 'required|string',
634            'last_name' => 'required|string',
635            'email' => 'required|email|max:255',
636            'selected_plan' => ['required', 'string', Rule::in(['Starter', 'Growth', 'Sales Pro', 'Sales Pro Teams'])],
637        ]);
638
639        $admin = auth()->user();
640        $company_id = $request->company_id;
641
642        $email = strtolower($request->email);
643        $password = Str::password(16);
644        $hashed_password = bcrypt($password);
645        $selected_plan = $request->selected_plan;
646
647        $user = User::where('email', $email)
648            ->where('company_id', $company_id)
649            ->first();
650        if ($user) {
651            throw new ExpectedException("The user with email {$email} already exists in your Organization.");
652        }
653
654        $planTitle = '';
655        if ($selected_plan == 'Sales Pro') {
656            $planTitle = 'FlyMSG.io Sales Pro - YR Plan';
657        } elseif ($selected_plan == 'Growth') {
658            $planTitle = 'Growth Yearly';
659        } elseif ($selected_plan == 'Starter') {
660            $planTitle = 'Starter Yearly';
661        } elseif ($selected_plan == 'Sales Pro Teams') {
662            $planTitle = 'Pro Plan Teams - ENT';
663        }
664
665        $plan = Plans::firstWhere('title', $planTitle);
666
667        $password_expiry = Carbon::now()->addDays(7);
668
669        $user = User::where('email', $email)
670            ->first();
671        if ($user) {
672            $existing_user = true;
673
674            $user->company_id = $company_id;
675            $user->invited_to_company = true;
676            $user->invited_to_company_by_admin = $admin->email;
677            $user->status = 'Invited';
678            $user->save();
679
680            $this->emailService->send(
681                $email,
682                new AdminCenterAddExistingUser(
683                    admin_email: $admin->email,
684                    email: $user->email,
685                    company: $admin->company->name,
686                ),
687                'cac_add_existent_user'
688            );
689        } else {
690            $existing_user = false;
691
692            $user = new User;
693            $user->fill($validated_data);
694            $user->status = 'Invited';
695            $user->email = $email;
696            $user->password = $hashed_password;
697            $user->company_id = $company_id;
698            $user->temp_password_expiry = $password_expiry->toDateTimeString();
699            $user->temp_password = $hashed_password;
700            $user->email_verified_at = new UTCDateTime(now()->getTimestamp() * 1000);
701            $user->onboardingv2_presented = true;
702
703            $user->save();
704        }
705
706        $password_expiry = $password_expiry->format('m/d/Y').' at '.$password_expiry->format('h:i A');
707
708        $data = [
709            'email' => $email,
710            'company_id' => $company_id,
711            'company_group_id' => $request->group_id,
712            'company_subgroup_id' => $request->subgroup_id,
713            'temp_password_expiry' => $user->temp_password_expiry,
714            'temp_password' => $hashed_password,
715            'password' => $hashed_password,
716            'admin_email' => $admin->email,
717        ];
718
719        $invitation = AdminUserInvitation::updateOrCreate(['email' => $email], $data);
720
721        // if there is plan, assign it
722        // if there is a plan, reduce the count after assignement in the company license
723        if ($plan) {
724            $invitation->plan = $plan;
725            $invitation->plan_id = $plan->id;
726            $invitation->save();
727            if (! $existing_user) {
728                $this->_assign_plan($user, $plan, $selected_plan, $email);
729            }
730        }
731
732        $data = [
733            'email' => $user->email,
734            'first_name' => $user->first_name,
735            'last_name' => $user->last_name,
736        ];
737
738        if (! $existing_user) {
739            Registered::dispatch($user, $data);
740
741            $this->emailService->send(
742                $email,
743                new AdminCenterAddNewUser(
744                    email: $email,
745                    password: $password,
746                    inviter: $admin->email,
747                    password_expiry: $password_expiry,
748                ),
749                'cac_add_new_user_manually'
750            );
751
752            $companyName = $admin->company->name;
753            $companyEmail = $admin->email;
754
755            if ($company_id) {
756                $company = Company::find($user->company_id);
757                $companyLicense = CompanyLicenses::where('company_id', $company->id)->first();
758                $community = $companyLicense->business_pro_enterprise_plus ?? [];
759                $isPro = in_array('yes_dedicated', $community) || in_array('yes_community', $community);
760
761                if ($isPro) {
762                    AddNewUserNotification::dispatch($data['email'], $companyEmail, $companyName, $this->emailService)->delay(now()->addSeconds(2));
763                }
764            }
765
766            $addnewUserEmailJob = (new AddNewUserReminder(
767                $email,
768                $password,
769                $admin->email,
770                $password_expiry,
771                $this->emailService
772            ))->delay(now()->addHours(48));
773
774            $job = app(\Illuminate\Contracts\Bus\Dispatcher::class)->dispatch($addnewUserEmailJob);
775
776            $invitation->reminder_job_id = $job;
777            $invitation->save();
778        }
779
780        return response()->json(data: [
781            'success' => true,
782            'user' => (new AdminCenterUserResource($user))->setPlan($plan),
783        ], status: 200);
784    }
785
786    public function resend_temporary_password(Request $request)
787    {
788        $validated_data = $request->validate([
789            'email' => 'required|email|max:255',
790        ]);
791
792        $email = strtolower($validated_data['email']);
793
794        $user = User::where('email', $email)
795            ->first();
796
797        $invitation = AdminUserInvitation::where('email', $email)
798            ->first();
799
800        // if ($user && $invitation) {
801        //     throw new ExpectedException("The user with email {$email} already exists in your company.");
802        // }
803
804        $password = Str::password(15);
805        $hashed_password = bcrypt($password);
806
807        $password_expiry = Carbon::now()->addDays(7);
808
809        $data = [
810            'email' => $email,
811            'temp_password_expiry' => $password_expiry->toDateTimeString(),
812            'temp_password' => $hashed_password,
813            'password' => $hashed_password,
814        ];
815
816        if ($user) {
817            $user->password = $hashed_password;
818            $user->temp_password = $hashed_password;
819            $user->temp_password_expiry = $password_expiry->toDateTimeString();
820            $user->save();
821        }
822
823        AdminUserInvitation::updateOrCreate(['email' => $email], $data);
824
825        $this->emailService->send(
826            $email,
827            new AdminCenterAddNewUser(
828                email: $email,
829                password: $password,
830                inviter: $invitation->admin_email,
831                password_expiry: $password_expiry->format('m/d/Y').' at '.$password_expiry->format('h:i A'),
832                emailSubject: 'New Invitation. Welcome to FlyMSG! ðŸ§¡'
833            ),
834            'cac_add_user'
835        );
836
837        return response()->json(
838            data: [
839                'success' => true,
840                'message' => 'Temporary password reset sent successfully',
841            ],
842            status: 200
843        );
844    }
845
846    public function create_user_by_csv(Request $request)
847    {
848        $validated_data = $request->validate([
849            'file' => 'required',
850        ]);
851
852        $file = $request->file('file');
853
854        $admin = auth()->user();
855        $company_id = $request->company_id;
856
857        // TODO MA
858        // Prcess csv read it into array
859        $users_in_file = [];
860
861        $invitation_sent_count = 0;
862
863        foreach ($users_in_file as $user_object) {
864            $email = strtolower($user_object['email']);
865            $plan_name = $user_object['plan'];
866
867            $existing_user = false;
868
869            $plan = match ($plan_name) {
870                'Sales Pro' => Plans::whereTitle('FlyMSG.io Sales Pro - YR Plan')->first(),
871                'Starter' => Plans::whereTitle('Starter Yearly')->first(),
872                'Growth' => Plans::whereTitle('Growth Yearly')->first(),
873                'Sales Pro Teams' => Plans::whereTitle('Pro Plan Teams - ENT')->first(),
874                default => null,
875            };
876
877            if (! $plan) {
878                throw new ExpectedException("The selected plan for {$email} was not found.");
879            }
880
881            $user = User::where('email', $email)
882                ->first();
883            if ($user) {
884                $existing_user = true;
885
886                $user->company_id = $company_id;
887                $user->invited_to_company = true;
888                $user->invited_to_company_by_admin = $admin->email;
889                $user->status = 'Invited';
890                $user->save();
891
892                $this->emailService->send(
893                    $email,
894                    new AdminCenterAddExistingUser(
895                        admin_email: $admin->email,
896                        email: $user->email,
897                        company: $admin->company->name,
898                    ),
899                    'cac_add_existent_user'
900                );
901            } else {
902                $existing_user = false;
903                $invitation_sent_count++;
904            }
905
906            $invitation = new AdminUserInvitation;
907            $invitation->email = $email;
908            $invitation->plan_id = $plan->id;
909            $invitation->plan = $plan;
910            $invitation->company_id = $company_id;
911            $invitation->admin_email = $user->email;
912            $invitation->save();
913
914            if (! $existing_user) {
915                $this->emailService->send(
916                    $invitation->email,
917                    new InvitationToJoinFlymsgEmail(
918                        email: $invitation->email,
919                        password: ''
920                    ),
921                    'cac_invitation_to_join'
922                );
923            }
924        }
925
926        return response()->json(
927            data: [
928                'success' => true,
929                'message' => "{$invitation_sent_count} users invited successfully",
930            ],
931            status: 200
932        );
933    }
934
935    public function deactivate_user(Request $request, $slug, User $user)
936    {
937        $admin = auth()->user();
938        $company_id = $request->company_id;
939
940        $this->adminUsersService->deactivateUser($user, $admin->id, $company_id);
941
942        return response()->json(data: [
943            'success' => true,
944            'message' => 'User deactivated successfully',
945        ], status: 200);
946    }
947
948    public function deactivate_invited_user(Request $request, $slug, $user_id)
949    {
950        $admin = auth()->user();
951        try {
952            $invitation = AdminUserInvitation::find($user_id);
953
954            if (! $invitation) {
955                $user_detail = User::find($user_id);
956
957                $invitation = AdminUserInvitation::where('email', $user_detail->email)->first();
958            }
959
960            if (! empty($user_detail)) {
961                $this->adminUsersService->rejectUserInvitation($user_detail, $admin);
962            }
963
964            $this->adminUsersService->deleteInvitedUser($invitation);
965
966            return response()->json(data: [
967                'success' => true,
968                'message' => 'User deactivated successfully',
969            ], status: 200);
970        } catch (\Exception $th) {
971            return response()->json(data: [
972                'success' => false,
973                'message' => 'Something went wrong, please try again',
974            ], status: 404);
975        }
976    }
977
978    /**
979     * Hard delete a user fro, the database. This action cannot
980     * be undone.
981     *
982     *
983     * @return JsonResponse
984     */
985    public function delete_user(Request $request, $slug, User $user)
986    {
987        $name = $user->first_name.' '.$user->last_name;
988        $admin = auth()->user();
989
990        $this->adminUsersService->deleteUser($user, $admin);
991
992        return response()->json(data: [
993            'success' => true,
994            'message' => "$name deleted successfully",
995        ], status: 200);
996    }
997
998    public function reset_password(Request $request, $slug, User $user)
999    {
1000        if ($user->status == 'Invited') {
1001            throw new ExpectedException('This user has not signed up yet. Please try again later.');
1002        }
1003
1004        $request = $request->merge(['email' => $user->email]);
1005        $response = $this->broker()->sendResetLink($this->credentials($request));
1006
1007        if ($response != Password::RESET_LINK_SENT) {
1008            throw new ExpectedException('Failed to send password reset link. Please try again later');
1009        }
1010
1011        return response()->json(data: [
1012            'success' => true,
1013            'message' => 'Password reset successfully. User will get the link to update their password.',
1014        ], status: 200);
1015    }
1016
1017    public function reset_invitation_password(Request $request, $slug, AdminUserInvitation $invite)
1018    {
1019        if ($invite->status == 'Invited') {
1020            throw new ExpectedException('This invite has not signed up yet. Please try again later.');
1021        }
1022
1023        $request = $request->merge(['email' => $invite->email]);
1024        $response = $this->broker()->sendResetLink($this->credentials($request));
1025
1026        if ($response != Password::RESET_LINK_SENT) {
1027            throw new ExpectedException('Failed to send password reset link. Please try again later');
1028        }
1029
1030        return response()->json(data: [
1031            'success' => true,
1032            'message' => 'Password reset successfully. User will get the link to update their password.',
1033        ], status: 200);
1034    }
1035
1036    public function resend_user_invitation(Request $request, SendUserInvitationAction $sendUserInvitationAction)
1037    {
1038        $admin = auth()->user();
1039        $validated_data = $request->validate([
1040            'email' => 'required',
1041        ]);
1042
1043        $email = $validated_data['email'];
1044
1045        $invitation = AdminUserInvitation::where('email', $email)->first();
1046
1047        $email = strtolower($email);
1048
1049        if (! $invitation) {
1050            throw new ExpectedException('Previous invitation not found');
1051        }
1052
1053        $user = User::where('email', $email)
1054            ->whereStatus('Invited')
1055            ->first();
1056
1057        $sendInvitationDTO = new SendUserInvitationDTO(
1058            user: $user ?? $invitation,
1059            invitation: $invitation,
1060            newEmail: $email,
1061            adminEmail: $admin->email,
1062            companyName: $admin->company->name,
1063        );
1064
1065        $sendUserInvitationAction->execute($sendInvitationDTO);
1066
1067        return response()->json(data: [
1068            'success' => true,
1069            'message' => 'User profile updated successfully',
1070        ], status: 200);
1071    }
1072
1073    public function resend_invitations(Request $request)
1074    {
1075        $admin = auth()->user();
1076        $role = $request->current_role;
1077        $company_id = $request->company_id;
1078        $admin_group_ids = $request->admin_group_ids;
1079
1080        $users = User::select([
1081            'id',
1082            'first_name',
1083            'last_name',
1084            'email',
1085            'role',
1086            'company_id',
1087            'company_group_id',
1088            'sales_pro_team_manager_id',
1089            'status',
1090            'created_at',
1091            'deleted_at',
1092            'deactivated_at',
1093            'avatar',
1094        ])->where('status', 'Invited');
1095
1096        $users = $this->filterUsersByAdmin($users, $role, $company_id, $admin_group_ids);
1097        $users = $users->latest()->get();
1098
1099        foreach ($users as $user) {
1100            $userEmail = $user->email;
1101            $invitation = AdminUserInvitation::where('email', $userEmail)->first();
1102            $userEmail = strtolower($user->email);
1103
1104            if (! $invitation) {
1105                continue;
1106            }
1107
1108            if ($user && $user->created_at->diffInMinutes($invitation?->updated_at) > 5) {
1109                $this->emailService->send(
1110                    $userEmail,
1111                    new AdminCenterAddExistingUser(
1112                        admin_email: $admin->email,
1113                        email: $userEmail,
1114                        company: $admin->company->name,
1115                    ),
1116                    'cac_add_existent_user'
1117                );
1118            } else {
1119                $password = Str::random(16);
1120                $encrypted_password = bcrypt($password);
1121                $temp_password_expiry = Carbon::parse($invitation->temp_password_expiry);
1122
1123                $invitation->password = $encrypted_password;
1124                $invitation->temp_password = $encrypted_password;
1125                $invitation->admin_email = $admin->email;
1126                $invitation->email = $userEmail;
1127                $invitation->save();
1128
1129                if ($user) {
1130                    $user->password = $encrypted_password;
1131                    $user->temp_password = $encrypted_password;
1132                    $user->save();
1133                }
1134
1135                $this->emailService->send(
1136                    $userEmail,
1137                    new AdminCenterAddNewUser(
1138                        email: $userEmail,
1139                        password: $password,
1140                        inviter: $admin->email,
1141                        password_expiry: $temp_password_expiry->format('m/d/Y').' at '.$temp_password_expiry->format('h:i A'),
1142                    ),
1143                    'cac_resend_invitation'
1144                );
1145            }
1146        }
1147
1148        return response()->json(data: [
1149            'success' => true,
1150            'message' => 'Invites resent successfully',
1151        ], status: 200);
1152    }
1153
1154    public function move_group(Request $request, $slug, User $user)
1155    {
1156        $validated_data = $request->validate([
1157            'to_group_id' => 'required',
1158        ]);
1159
1160        $group = $this->adminGroupsService->getGroup($validated_data['to_group_id']);
1161
1162        $user = $this->adminUsersService->moveUserToGroup($user, $group);
1163
1164        return response()->json(data: [
1165            'success' => true,
1166            'message' => 'User profile updated successfully',
1167            'user' => new AdminCenterUserResource($user->fresh()),
1168        ], status: 200);
1169    }
1170
1171    public function move_group_invite(Request $request, $slug, AdminUserInvitation $invite)
1172    {
1173        $validated_data = $request->validate([
1174            'to_group_id' => 'required',
1175        ]);
1176        $invite->company_group_id = $validated_data['to_group_id'];
1177        $invite->save();
1178
1179        return response()->json(data: [
1180            'success' => true,
1181            'message' => 'User invitation updated successfully',
1182            'user' => new AdminCenterUserResource($invite->fresh()),
1183        ], status: 200);
1184    }
1185
1186    public function assign_role(Request $request, $slug, User $user)
1187    {
1188        $validated_data = $request->validate([
1189            'role_name' => 'required',
1190            'groups' => 'sometimes',
1191        ]);
1192
1193        $role = Role::find($validated_data['role_name']);
1194        if (! $role) {
1195            $role = Role::where('name', $validated_data['role_name'])->first();
1196            if (! $role) {
1197                throw new ExpectedException('Role not found');
1198            }
1199        }
1200
1201        $adminRole = Role::where('name', Role::GLOBAL_ADMIN)->first();
1202
1203        if ($role->name != Role::GLOBAL_ADMIN) {
1204            $isAdmin = UserRole::where('role_id', $adminRole->id)
1205                ->where('user_id', $user->id)
1206                ->count() > 0;
1207
1208            if ($isAdmin) {
1209                $roleId = $adminRole->id;
1210                $companyId = $request->company_id;
1211                $currentUserId = $user->id;
1212
1213                $exists = UserRole::raw(function ($collection) use ($roleId, $companyId, $currentUserId) {
1214                    return $collection->aggregate([
1215                        [
1216                            '$lookup' => [
1217                                'from' => 'users',
1218                                'let' => ['userId' => ['$toObjectId' => '$user_id']],
1219                                'pipeline' => [
1220                                    ['$match' => [
1221                                        '$expr' => [
1222                                            '$eq' => ['$$userId', '$_id'],
1223                                        ],
1224                                    ]],
1225                                ],
1226                                'as' => 'user',
1227                            ],
1228                        ],
1229                        [
1230                            '$match' => [
1231                                'role_id' => $roleId,
1232                                'user.company_id' => $companyId,
1233                                'user_id' => ['$ne' => $currentUserId],
1234                            ],
1235                        ],
1236                        [
1237                            '$limit' => 1,
1238                        ],
1239                    ]);
1240                });
1241
1242                $isLastAdmin = empty($exists->toArray());
1243
1244                if ($isAdmin && $isLastAdmin) {
1245                    throw new ExpectedException('Last admin cannot be demoted', 409);
1246                }
1247            }
1248        }
1249
1250        UserRole::where('user_id', $user->id)->delete();
1251
1252        UserRole::create([
1253            'user_id' => $user->id,
1254            'role_id' => $role->id,
1255        ]);
1256        if ($role->name == Role::GROUP_ADMIN || $role->name = Role::REPORTING_ADMIN) {
1257            $group_ids = $validated_data['groups'] ?? [];
1258
1259            $management_record = $user->sales_pro_team_manager;
1260            if (! $management_record) {
1261                $management_record = new SalesProTeamManager;
1262                $management_record->user_id = $user->id;
1263                $management_record->company_id = $user->company_id;
1264                $management_record->first_name = $user->first_name;
1265                $management_record->last_name = $user->last_name;
1266                $management_record->email = $user->email;
1267                $management_record->save();
1268            }
1269
1270            if (! empty($group_ids)) {
1271                $groups = CompanyGroup::whereIn('_id', $group_ids)->get();
1272                if ($groups->isNotEmpty()) {
1273                    $management_record->groups()->sync($groups);
1274                }
1275            }
1276
1277            $new_group_id = ! empty($group_ids) ? $group_ids[0] : null;
1278            if ($user->company_group_id !== $new_group_id) {
1279                $user->company_group_id = $new_group_id;
1280                $user->save();
1281            }
1282        }
1283
1284        return response()->json(data: [
1285            'success' => true,
1286            'message' => 'User profile updated successfully',
1287            'user' => new AdminCenterUserResource($user->fresh()),
1288        ], status: 200);
1289    }
1290
1291    public function deactivate_user_bulk(Request $request)
1292    {
1293        $validated_data = $request->validate([
1294            'users' => 'required',
1295        ]);
1296        $admin = auth()->user();
1297        $company_id = $request->company_id;
1298
1299        $this->adminUsersService->deactivateUserBulk($validated_data['users'], $admin->id, $company_id);
1300
1301        return response()->json(data: [
1302            'success' => true,
1303            'message' => 'Users deactivated successfully',
1304        ], status: 200);
1305    }
1306
1307    private function _reactive_users(array $userIds)
1308    {
1309        $users = User::withTrashed()->whereIn('_id', $userIds)->get();
1310        $admin = auth()->user();
1311
1312        foreach ($users as $user) {
1313            $user->restore();
1314
1315            $company_id = $user->company_id;
1316            if ($company_id) {
1317                $company = Company::find($user->company_id);
1318                $companyLicense = CompanyLicenses::where('company_id', $company->id)->first();
1319                $community = $companyLicense->business_pro_enterprise_plus ?? [];
1320                $companyName = $company->name;
1321                $companyEmail = $admin->email;
1322                $isPro = in_array('yes_dedicated', $community) || in_array('yes_community', $community);
1323
1324                if ($isPro) {
1325                    ReactivateUserNotification::dispatch($user->email, $companyEmail, $companyName, $this->emailService)->delay(now()->addSeconds(2));
1326                }
1327            }
1328        }
1329    }
1330
1331    private function _assign_plans_to_users(array $usersWithPlan)
1332    {
1333        foreach ($usersWithPlan as $userPlan) {
1334            $userId = $userPlan['user_id'];
1335            $planTitle = match ($userPlan['plan']) {
1336                'Sales Pro' => 'FlyMSG.io Sales Pro - YR Plan',
1337                'Growth' => 'Growth Yearly',
1338                'Starter' => 'Starter Yearly',
1339                'Sales Pro Teams' => 'Pro Plan Teams - ENT',
1340                default => $userPlan['plan'],
1341            };
1342
1343            $plan = Plans::where('title', $planTitle)->first();
1344
1345            if ($plan) {
1346                $user = User::withTrashed()->find($userId);
1347                if ($user) {
1348                    $this->_assign_plan($user, $plan, $planTitle, $user->email, true);
1349                }
1350            }
1351        }
1352    }
1353
1354    public function reactivate_user_bulk(Request $request)
1355    {
1356        $validated_data = $request->validate([
1357            'users' => 'required',
1358            'usersWithPlan' => 'sometimes|array',
1359        ]);
1360        $admin = auth()->user();
1361        if (isset($validated_data['users']) && in_array($admin->id, $validated_data['users'])) {
1362            throw new ExpectedException('You cannot reactivate your own account.');
1363        }
1364
1365        $this->_reactive_users($validated_data['users']);
1366
1367        if (! empty($validated_data['usersWithPlan'])) {
1368            $this->_assign_plans_to_users($validated_data['usersWithPlan']);
1369        }
1370
1371        return response()->json(data: [
1372            'success' => true,
1373            'message' => 'Users reactivated successfully',
1374        ], status: 200);
1375    }
1376
1377    public function delete_user_bulk(Request $request)
1378    {
1379        $validated_data = $request->validate([
1380            'users' => 'required',
1381        ]);
1382
1383        $admin = auth()->user();
1384        $company_id = $request->company_id;
1385        if (isset($validated_data['users']) && in_array($admin->id, $validated_data['users'])) {
1386            throw new ExpectedException('You cannot delete your own account.');
1387        }
1388
1389        $users = User::whereIn('_id', $validated_data['users'])->get();
1390        foreach ($users as $user) {
1391            // Get the users current plan and cancel it.
1392            $user_sub = $user->subscription('main');
1393            if ($user_sub) {
1394                $plan = $user_sub->plan;
1395                if ($plan->identifier != 'freemium') {
1396                    $user_sub->markAsCancelledOnlyInDB();
1397                }
1398                // Deternming the plan and increase the availability in company licenses
1399                $company_license = CompanyLicenses::where('company_id', $company_id)
1400                    ->active()
1401                    ->first();
1402                if ($company_license) {
1403                    $company_license->restoreLicenseCountForDeletedUsers($plan);
1404                }
1405            }
1406            $user->forceDelete();
1407        }
1408
1409        return response()->json(data: [
1410            'success' => true,
1411            'message' => 'Users deleted successfully',
1412        ], status: 200);
1413    }
1414
1415    public function reset_password_bulk(Request $request)
1416    {
1417        $validated_data = $request->validate([
1418            'users' => 'required',
1419        ]);
1420        $admin = auth()->user();
1421        $users = $validated_data['users'];
1422        foreach ($users as $email) {
1423            $email = strtolower($email);
1424            $user = User::where('email', $email)->first();
1425            if (! $user) {
1426                // throw new ExpectedException("User not found");
1427                continue;
1428            }
1429
1430            $request = $request->merge(['email' => $email]);
1431            $token = $this->broker()->sendResetLink($this->credentials($request), function ($user, $token) {
1432                return $token;
1433            });
1434
1435            if (in_array($token, [Password::INVALID_USER, Password::RESET_THROTTLED])) {
1436                continue;
1437            }
1438
1439            $this->emailService->send(
1440                $user->email,
1441                new SendResetPasswordRequestedEmail(
1442                    name: $user->first_name.' '.$user->last_name,
1443                    admin_email: $admin->email,
1444                    email: $email,
1445                    token: $token
1446                ),
1447                'cac_send_password_requested'
1448            );
1449        }
1450
1451        return response()->json(data: [
1452            'success' => true,
1453            'message' => 'Password reset successfully. User will get the link to update their password.',
1454        ], status: 200);
1455    }
1456
1457    public function resend_user_invitation_bulk(Request $request)
1458    {
1459        $admin = auth()->user();
1460        $validated_data = $request->validate([
1461            'users' => 'required',
1462        ]);
1463        $users = $validated_data['users'];
1464        foreach ($users as $email) {
1465            $user = User::where('email', strtolower($email))->first();
1466
1467            $invitation = AdminUserInvitation::where('email', $email)->first();
1468            $email = strtolower($email);
1469            if (! $invitation) {
1470                // throw new ExpectedException("Previous invitation not found");
1471                continue;
1472            }
1473
1474            if ($user && $user->created_at->diffInMinutes($invitation?->updated_at) > 5) {
1475                $this->emailService->send(
1476                    $email,
1477                    new AdminCenterAddExistingUser(
1478                        admin_email: $admin->email,
1479                        email: $email,
1480                        company: $admin->company->name,
1481                    ),
1482                    'cac_add_existent_user'
1483                );
1484            } else {
1485                $password = Str::random(16);
1486                $encrypted_password = bcrypt($password);
1487                $password_expiry = Carbon::now()->addDays(7);
1488
1489                $invitation->password = $encrypted_password;
1490                $invitation->temp_password = $encrypted_password;
1491                $invitation->temp_password_expiry = $password_expiry->toDateTimeString();
1492                $invitation->admin_email = $request->user()->email;
1493                $invitation->email = $email;
1494                $invitation->save();
1495
1496                $this->emailService->send(
1497                    $email,
1498                    new InvitationToJoinFlymsgEmail(
1499                        email: $email,
1500                        password: $password
1501                    ),
1502                    'cac_invitation_to_join'
1503                );
1504            }
1505        }
1506
1507        return response()->json(data: [
1508            'success' => true,
1509            'message' => 'User profile updated successfully',
1510        ], status: 200);
1511    }
1512
1513    public function move_group_bulk(Request $request)
1514    {
1515        $validated_data = $request->validate([
1516            'users' => 'required',
1517            'to_group_id' => 'required',
1518        ]);
1519
1520        $group = $this->adminGroupsService->getGroup($validated_data['to_group_id']);
1521
1522        $users = $this->adminUsersService->moveUsersToGroup($validated_data['users'], $group);
1523
1524        return response()->json(data: [
1525            'success' => true,
1526            'message' => 'User profile updated successfully',
1527            'users' => AdminCenterUserResource::collection($users),
1528        ], status: 200);
1529    }
1530
1531    /**
1532     * Checks if a group/subgroup is unique in a company
1533     *
1534     * @param  Request  $request  {name:string}
1535     * @return JsonResponse
1536     */
1537    public function group_exists(Request $request)
1538    {
1539        $validated_data = $request->validate([
1540            'name' => 'required|string',
1541        ]);
1542        $name = $validated_data['name'];
1543        $company_id = $request->company_id;
1544
1545        $exists = CompanyGroup::where([
1546            ['name', $name],
1547            ['company_id', $company_id],
1548        ])->exists();
1549
1550        return response()->json([
1551            'success' => true,
1552            'exists' => $exists ? true : false,
1553            'message' => $exists ? "The name: $name has already been taken." : "The name $name is available.",
1554        ], 200);
1555    }
1556
1557    /**
1558     * Creates a new group. You can assign members of the
1559     * group too if you like.
1560     *
1561     * @param  Request  $request  {name:string, users:[array if user IDs]}
1562     * @return JsonResponse
1563     */
1564    public function create_group(Request $request)
1565    {
1566        $company_id = $request->company_id;
1567        $validated_data = $request->validate([
1568            'name' => 'required',
1569            'users' => 'sometimes',
1570        ]);
1571
1572        $group = $this->adminGroupsService->createGroup($validated_data['name'], $company_id);
1573
1574        $this->adminUsersService->moveUsersToGroup($validated_data['users'] ?? [], $group);
1575
1576        return response()->json(data: [
1577            'success' => true,
1578            'message' => "New group $group->name added",
1579            'group' => new AdminCenterCompanyGroupResource($group->load('users')),
1580        ], status: 200);
1581    }
1582
1583    public function update_group(Request $request, $slug, CompanyGroup $group)
1584    {
1585        $validated_data = $request->validate([
1586            'name' => 'required',
1587        ]);
1588
1589        $result = $this->adminGroupsService->updateGroup($group, $validated_data['name'], $request->company_id);
1590
1591        return response()->json(data: $result, status: 200);
1592    }
1593
1594    public function delete_group(Request $request, $slug, CompanyGroup $group)
1595    {
1596        // get group instancy id
1597        $groupInstancyId = isset($group->instancy_id) ? $group->instancy_id : '';
1598
1599        if ($group->users()->count() > 0) {
1600            $group->users()->update(['company_group_id' => null]);
1601            $user_ids = $group->users()->pluck('_id')->toArray();
1602            $this->adminUsersService->moveUsersToGroup($user_ids, null);
1603        }
1604
1605        // Of a top parent group is being deleted, delete subgroups too?
1606        if ($group->subgroups()->count() > 0) {
1607            foreach ($group->subgroups as $subgroup) {
1608                $subgroup->users()->update(['company_group_id' => null]);
1609
1610                $user_ids = $subgroup->users()->pluck('_id')->toArray();
1611                $this->adminUsersService->moveUsersToGroup($user_ids, null);
1612
1613                $subgroup->delete();
1614            }
1615        }
1616        $group->delete();
1617
1618        // after above delete the group on instancy
1619        if ($groupInstancyId) {
1620            DeleteInstancyGroup::dispatch($groupInstancyId);
1621            // $job = DeleteInstancyGroup::dispatch($groupInstancyId);
1622            // if ($job) {
1623            //     $job->delay(now()->addMinutes(5));
1624            // }
1625        }
1626
1627        return response()->json(data: [
1628            'success' => true,
1629            'message' => 'Group deleted successfully',
1630        ], status: 200);
1631    }
1632
1633    public function add_users_to_group(Request $request, $slug, CompanyGroup $group)
1634    {
1635        $validated_data = $request->validate([
1636            'users' => 'required',
1637        ]);
1638
1639        $this->adminUsersService->moveUsersToGroup($validated_data['users'], $group);
1640
1641        return response()->json(data: [
1642            'success' => true,
1643            'message' => 'Updated group users successfully',
1644        ], status: 200);
1645    }
1646
1647    public function add_subgroup_to_group(Request $request, $slug, CompanyGroup $group)
1648    {
1649        $company_id = $request->company_id;
1650
1651        $validated_data = $request->validate([
1652            'name' => 'required',
1653            'users' => 'sometimes',
1654        ]);
1655
1656        $subgroup = $this->adminGroupsService->createGroup($validated_data['name'], $company_id, $group);
1657
1658        $this->adminUsersService->moveUsersToGroup($validated_data['users'] ?? [], $subgroup);
1659
1660        return response()->json(data: [
1661            'success' => true,
1662            'message' => 'New subgroup added',
1663            'group' => $subgroup->load('users'),
1664        ], status: 200);
1665    }
1666
1667    public function export_csv(Request $request)
1668    {
1669        $users = $request->users ? explode(',', $request->users) : [];
1670        $only_deactivated_users = $request->deactivated == 'true' ? true : false;
1671
1672        return $this->csvExportService->downloadWithUserDelimiter(
1673            auth()->user(),
1674            fn () => (new UsersExport($only_deactivated_users, $users))->download('users.csv')
1675        );
1676    }
1677
1678    public function import_csv(Request $request)
1679    {
1680        $request->validate([
1681            'csv_file' => 'required',
1682        ]);
1683
1684        $file = $request->file('csv_file');
1685
1686        (new UsersImport)->import($file);
1687
1688        return response()->json([
1689            'success' => true,
1690            'message' => 'CSV imported successfully and file deleted.',
1691        ], 200);
1692    }
1693
1694    public function reporting_licenses(Request $request)
1695    {
1696        $filters = new FindUsersOverviewFilter(
1697            fromDate: $request->fromDate ? Carbon::parse($request->fromDate)->startOfDay() : null,
1698            toDate: $request->toDate ? Carbon::parse($request->toDate)->endOfDay() : null,
1699            userIds: array_filter(explode(',', $request->user_ids), function ($value) {
1700                return $value !== '';
1701            }),
1702            groupIds: array_filter(explode(',', $request->group_ids), function ($value) {
1703                return $value !== '';
1704            }),
1705            subgroupIds: array_filter(explode(',', $request->subgroup_ids), function ($value) {
1706                return $value !== '';
1707            }),
1708            companyIds: $request->company_ids ? explode(',', $request->company_ids) : null,
1709            currentRole: $request->current_role,
1710            companyId: $request->company_id,
1711            adminGroupIds: $request->admin_group_ids,
1712            monthPeriod: 12,
1713        );
1714
1715        $data = $this->accountCenterReportingService->findLicenseOverview($filters);
1716
1717        return response()->json(data: [
1718            'success' => true,
1719            'licenses' => $data,
1720        ], status: 200);
1721    }
1722
1723    public function reporting_roi_spotlight(Request $request, RoiSpotlightAction $roiSpotlightAction)
1724    {
1725        $filters = new ReportingRequestDTO(
1726            fromDate: $request->fromDate,
1727            toDate: $request->toDate,
1728            companyId: $request->company_id,
1729            adminGroupIds: $request->admin_group_ids,
1730            userIds: $request->user_ids ? $request->user_ids : null,
1731            groupIds: $request->group_ids ? $request->group_ids : null,
1732            subgroupIds: $request->subgroup_ids ? $request->subgroup_ids : null,
1733            role: $request->current_role,
1734        );
1735
1736        $data = $roiSpotlightAction->execute($filters);
1737
1738        return response()->json(data: [
1739            'success' => true,
1740            'data' => $data,
1741        ], status: 200);
1742    }
1743
1744    public function reporting_productivity_spotlight(Request $request, ProductivitySpotlightAction $productivitySpotlightAction)
1745    {
1746        $filters = new ReportingRequestDTO(
1747            fromDate: $request->fromDate,
1748            toDate: $request->toDate,
1749            companyId: $request->company_id,
1750            adminGroupIds: $request->admin_group_ids,
1751            userIds: $request->user_ids ? $request->user_ids : null,
1752            groupIds: $request->group_ids ? $request->group_ids : null,
1753            subgroupIds: $request->subgroup_ids ? $request->subgroup_ids : null,
1754            role: $request->current_role,
1755        );
1756
1757        $data = $productivitySpotlightAction->execute($filters);
1758
1759        return response()->json(data: [
1760            'success' => true,
1761            'data' => $data,
1762        ], status: 200);
1763    }
1764
1765    public function reporting_total_characters_typed_spotlight(Request $request, TotalCharactersTypedSpotlightAction $totalCharactersTypedSpotlightAction)
1766    {
1767        $filters = new ReportingRequestDTO(
1768            fromDate: $request->fromDate,
1769            toDate: $request->toDate,
1770            companyId: $request->company_id,
1771            adminGroupIds: $request->admin_group_ids,
1772            userIds: $request->user_ids ? $request->user_ids : null,
1773            groupIds: $request->group_ids ? $request->group_ids : null,
1774            subgroupIds: $request->subgroup_ids ? $request->subgroup_ids : null,
1775            role: $request->current_role,
1776        );
1777
1778        $data = $totalCharactersTypedSpotlightAction->execute($filters);
1779
1780        return response()->json(data: [
1781            'success' => true,
1782            'data' => $data,
1783        ], status: 200);
1784    }
1785
1786    public function reporting_total_fly_grammar_spotlight(Request $request, TotalFlyGrammarSpotlightAction $totalFlyGrammarSpotlightAction)
1787    {
1788        $filters = new ReportingRequestDTO(
1789            fromDate: $request->fromDate,
1790            toDate: $request->toDate,
1791            companyId: $request->company_id,
1792            adminGroupIds: $request->admin_group_ids,
1793            userIds: $request->user_ids ? $request->user_ids : null,
1794            groupIds: $request->group_ids ? $request->group_ids : null,
1795            subgroupIds: $request->subgroup_ids ? $request->subgroup_ids : null,
1796            role: $request->current_role,
1797        );
1798
1799        $data = $totalFlyGrammarSpotlightAction->execute($filters);
1800
1801        return response()->json(data: [
1802            'success' => true,
1803            'data' => $data,
1804        ], status: 200);
1805    }
1806
1807    public function reporting_total_fly_cuts_spotlight(Request $request, TotalFlyCutsSpotlightAction $totalFlyCutsSpotlightAction)
1808    {
1809        $filters = new ReportingRequestDTO(
1810            fromDate: $request->fromDate,
1811            toDate: $request->toDate,
1812            companyId: $request->company_id,
1813            adminGroupIds: $request->admin_group_ids,
1814            userIds: $request->user_ids ? $request->user_ids : null,
1815            groupIds: $request->group_ids ? $request->group_ids : null,
1816            subgroupIds: $request->subgroup_ids ? $request->subgroup_ids : null,
1817            role: $request->current_role,
1818        );
1819
1820        $data = $totalFlyCutsSpotlightAction->execute($filters);
1821
1822        return response()->json(data: [
1823            'success' => true,
1824            'data' => $data,
1825        ], status: 200);
1826    }
1827
1828    public function reporting_flymsg_coach_level(Request $request, FmsCoachLevelSpotlightAction $fmsCoachLevelSpotlightAction)
1829    {
1830        $filters = new ReportingRequestDTO(
1831            fromDate: $request->fromDate,
1832            toDate: $request->toDate,
1833            companyId: $request->company_id,
1834            adminGroupIds: $request->admin_group_ids,
1835            userIds: $request->user_ids ? $request->user_ids : null,
1836            groupIds: $request->group_ids ? $request->group_ids : null,
1837            subgroupIds: $request->subgroup_ids ? $request->subgroup_ids : null,
1838            role: $request->current_role,
1839        );
1840
1841        $data = $fmsCoachLevelSpotlightAction->execute($filters);
1842
1843        return response()->json([
1844            'success' => true,
1845            'data' => $data,
1846        ], 200);
1847    }
1848
1849    public function reporting_flycuts_created(Request $request)
1850    {
1851        $filters = new FindUsersOverviewFilter(
1852            fromDate: $request->fromDate ? Carbon::parse($request->fromDate)->startOfDay() : null,
1853            toDate: $request->toDate ? Carbon::parse($request->toDate)->endOfDay() : null,
1854            userIds: array_filter(explode(',', $request->user_ids), function ($value) {
1855                return $value !== '';
1856            }),
1857            groupIds: array_filter(explode(',', $request->group_ids), function ($value) {
1858                return $value !== '';
1859            }),
1860            subgroupIds: array_filter(explode(',', $request->subgroup_ids), function ($value) {
1861                return $value !== '';
1862            }),
1863            companyIds: $request->company_ids ? explode(',', $request->company_ids) : null,
1864            currentRole: $request->current_role,
1865            companyId: $request->company_id,
1866            adminGroupIds: $request->admin_group_ids,
1867            monthPeriod: 12,
1868            userDefined: true,
1869        );
1870
1871        $shortcuts = $this->accountCenterReportingService->findShortcut($filters);
1872
1873        $data = [
1874            'chart' => $shortcuts['chart'],
1875            'total_flycuts_created' => number_format($shortcuts['total_shortcut_created'], 0, '.', ','),
1876            'average_flycuts_per_active_user' => number_format(round($shortcuts['average_shortcut_per_active_user'], 2), 2, '.', ','),
1877        ];
1878
1879        return response()->json(data: [
1880            'success' => true,
1881            'data' => $data,
1882        ], status: 200);
1883    }
1884
1885    public function reporting_flycuts_created_top_users(Request $request)
1886    {
1887        return response()->json(data: [
1888            'success' => true,
1889            'users' => $this->getTop5($request, 'flycuts_created'),
1890        ]);
1891    }
1892
1893    public function reporting_flyplates_added(Request $request)
1894    {
1895
1896        $filters = new FindUsersOverviewFilter(
1897            fromDate: $request->fromDate ? Carbon::parse($request->fromDate)->startOfDay() : null,
1898            toDate: $request->toDate ? Carbon::parse($request->toDate)->endOfDay() : null,
1899            userIds: array_filter(explode(',', $request->user_ids), function ($value) {
1900                return $value !== '';
1901            }),
1902            groupIds: array_filter(explode(',', $request->group_ids), function ($value) {
1903                return $value !== '';
1904            }),
1905            subgroupIds: array_filter(explode(',', $request->subgroup_ids), function ($value) {
1906                return $value !== '';
1907            }),
1908            companyIds: $request->company_ids ? explode(',', $request->company_ids) : null,
1909            currentRole: $request->current_role,
1910            companyId: $request->company_id,
1911            adminGroupIds: $request->admin_group_ids,
1912            monthPeriod: 12,
1913            userDefined: false,
1914        );
1915
1916        $shortcuts = $this->accountCenterReportingService->findShortcut($filters);
1917
1918        $data = [
1919            'chart' => $shortcuts['chart'],
1920            'total_flyplate_created' => number_format($shortcuts['total_shortcut_created'], 0, '.', ','),
1921            'average_flyplate_per_active_user' => number_format(round($shortcuts['average_shortcut_per_active_user'], 2), 2, '.', ','),
1922        ];
1923
1924        return response()->json(data: [
1925            'success' => true,
1926            'data' => $data,
1927        ], status: 200);
1928    }
1929
1930    public function reporting_flyplates_added_top_users(Request $request)
1931    {
1932        return response()->json(data: [
1933            'success' => true,
1934            'users' => $this->getTop5($request, 'flyplates_added'),
1935        ]);
1936    }
1937
1938    public function reporting_sentence_rewrite_ai(Request $request)
1939    {
1940
1941        $filters = new FindUsersOverviewFilter(
1942            fromDate: $request->fromDate ? Carbon::parse($request->fromDate)->startOfDay() : null,
1943            toDate: $request->toDate ? Carbon::parse($request->toDate)->endOfDay() : null,
1944            userIds: array_filter(explode(',', $request->user_ids), function ($value) {
1945                return $value !== '';
1946            }),
1947            groupIds: array_filter(explode(',', $request->group_ids), function ($value) {
1948                return $value !== '';
1949            }),
1950            subgroupIds: array_filter(explode(',', $request->subgroup_ids), function ($value) {
1951                return $value !== '';
1952            }),
1953            companyIds: $request->company_ids ? explode(',', $request->company_ids) : null,
1954            currentRole: $request->current_role,
1955            companyId: $request->company_id,
1956            adminGroupIds: $request->admin_group_ids,
1957            monthPeriod: 12,
1958            userDefined: false,
1959            feature: 'sentence_rewrite',
1960        );
1961
1962        $flyMsgAI = $this->accountCenterReportingService->findFlyMsgAI($filters);
1963
1964        $data = [
1965            'chart' => $flyMsgAI['chart'],
1966            'total_sentence_rewrite_ai_created' => number_format($flyMsgAI['total_flymsg_ai'], 0, '.', ','),
1967            'average_sentence_rewrite_ai_per_active_user' => number_format(round($flyMsgAI['average_flymsgai_per_active_user'], 2), 2, '.', ','),
1968        ];
1969
1970        return response()->json(data: [
1971            'success' => true,
1972            'data' => $data,
1973        ], status: 200);
1974    }
1975
1976    public function reporting_sentence_rewrite_ai_used_top_users(Request $request)
1977    {
1978        return response()->json(data: [
1979            'success' => true,
1980            'users' => $this->getTop5($request, 'sentence_rewrite_count'),
1981        ]);
1982    }
1983
1984    public function reporting_paragraph_rewrite_ai(Request $request)
1985    {
1986
1987        $filters = new FindUsersOverviewFilter(
1988            fromDate: $request->fromDate ? Carbon::parse($request->fromDate)->startOfDay() : null,
1989            toDate: $request->toDate ? Carbon::parse($request->toDate)->endOfDay() : null,
1990            userIds: array_filter(explode(',', $request->user_ids), function ($value) {
1991                return $value !== '';
1992            }),
1993            groupIds: array_filter(explode(',', $request->group_ids), function ($value) {
1994                return $value !== '';
1995            }),
1996            subgroupIds: array_filter(explode(',', $request->subgroup_ids), function ($value) {
1997                return $value !== '';
1998            }),
1999            companyIds: $request->company_ids ? explode(',', $request->company_ids) : null,
2000            currentRole: $request->current_role,
2001            companyId: $request->company_id,
2002            adminGroupIds: $request->admin_group_ids,
2003            monthPeriod: 12,
2004            userDefined: false,
2005            feature: 'paragraph_rewrite',
2006        );
2007
2008        $flyMsgAI = $this->accountCenterReportingService->findFlyMsgAI($filters);
2009
2010        $data = [
2011            'chart' => $flyMsgAI['chart'],
2012            'total_paragraph_rewrite_ai_created' => number_format($flyMsgAI['total_flymsg_ai'], 0, '.', ','),
2013            'average_paragraph_rewrite_ai_per_active_user' => number_format(round($flyMsgAI['average_flymsgai_per_active_user'], 2), 2, '.', ','),
2014        ];
2015
2016        return response()->json(data: [
2017            'success' => true,
2018            'data' => $data,
2019        ], status: 200);
2020    }
2021
2022    public function reporting_paragraph_rewrite_ai_used_top_users(Request $request)
2023    {
2024        return response()->json(data: [
2025            'success' => true,
2026            'users' => $this->getTop5($request, 'paragraph_rewrite_count'),
2027        ]);
2028    }
2029
2030    public function reporting_flyengage_ai(Request $request)
2031    {
2032
2033        $filters = new FindUsersOverviewFilter(
2034            fromDate: $request->fromDate ? Carbon::parse($request->fromDate)->startOfDay() : null,
2035            toDate: $request->toDate ? Carbon::parse($request->toDate)->endOfDay() : null,
2036            userIds: array_filter(explode(',', $request->user_ids), function ($value) {
2037                return $value !== '';
2038            }),
2039            groupIds: array_filter(explode(',', $request->group_ids), function ($value) {
2040                return $value !== '';
2041            }),
2042            subgroupIds: array_filter(explode(',', $request->subgroup_ids), function ($value) {
2043                return $value !== '';
2044            }),
2045            companyIds: $request->company_ids ? explode(',', $request->company_ids) : null,
2046            currentRole: $request->current_role,
2047            companyId: $request->company_id,
2048            adminGroupIds: $request->admin_group_ids,
2049            monthPeriod: 12,
2050            userDefined: false,
2051            feature: 'flyengage',
2052        );
2053
2054        $flyMsgAI = $this->accountCenterReportingService->findFlyMsgAI($filters);
2055
2056        $data = [
2057            'chart' => $flyMsgAI['chart'],
2058            'total_flyengage_ai_created' => number_format($flyMsgAI['total_flymsg_ai'], 0, '.', ','),
2059            'average_flyengage_ai_per_active_user' => number_format(round($flyMsgAI['average_flymsgai_per_active_user'], 2), 2, '.', ','),
2060        ];
2061
2062        return response()->json(data: [
2063            'success' => true,
2064            'data' => $data,
2065        ], status: 200);
2066    }
2067
2068    public function reporting_flyengage_ai_used_top_users(Request $request)
2069    {
2070        return response()->json(data: [
2071            'success' => true,
2072            'users' => $this->getTop5($request, 'flyengage_count'),
2073        ]);
2074    }
2075
2076    public function reporting_flypost_ai(Request $request)
2077    {
2078        $filters = new FindUsersOverviewFilter(
2079            fromDate: $request->fromDate ? Carbon::parse($request->fromDate)->startOfDay() : null,
2080            toDate: $request->toDate ? Carbon::parse($request->toDate)->endOfDay() : null,
2081            userIds: array_filter(explode(',', $request->user_ids), function ($value) {
2082                return $value !== '';
2083            }),
2084            groupIds: array_filter(explode(',', $request->group_ids), function ($value) {
2085                return $value !== '';
2086            }),
2087            subgroupIds: array_filter(explode(',', $request->subgroup_ids), function ($value) {
2088                return $value !== '';
2089            }),
2090            companyIds: $request->company_ids ? explode(',', $request->company_ids) : null,
2091            currentRole: $request->current_role,
2092            companyId: $request->company_id,
2093            adminGroupIds: $request->admin_group_ids,
2094            monthPeriod: 12,
2095            userDefined: false,
2096            feature: 'flypost',
2097        );
2098
2099        $flyMsgAI = $this->accountCenterReportingService->findFlyMsgAI($filters);
2100
2101        $data = [
2102            'chart' => $flyMsgAI['chart'],
2103            'total_flypost_ai_created' => number_format($flyMsgAI['total_flymsg_ai'], 0, '.', ','),
2104            'average_flypost_ai_per_active_user' => number_format(round($flyMsgAI['average_flymsgai_per_active_user'], 2), 2, '.', ','),
2105        ];
2106
2107        return response()->json(data: [
2108            'success' => true,
2109            'data' => $data,
2110        ], status: 200);
2111    }
2112
2113    public function reporting_flypost_ai_used_top_users(Request $request)
2114    {
2115        return response()->json(data: [
2116            'success' => true,
2117            'users' => $this->getTop5($request, 'flypost_count'),
2118        ]);
2119    }
2120
2121    private function getTop5(Request $request, string $property)
2122    {
2123        $filter = new ReportingRequestDTO(
2124            fromDate: ! empty($request->fromDate) ? Carbon::parse($request->fromDate)->startOfDay() : null,
2125            toDate: ! empty($request->toDate) ? Carbon::parse($request->toDate)->endOfDay() : null,
2126            companyId: $request->company_id,
2127            adminGroupIds: $request->admin_group_ids,
2128            userIds: $request->user_ids,
2129            groupIds: $request->group_ids,
2130            subgroupIds: $request->subgroup_ids,
2131            role: $request->current_role,
2132            companyIds: $request->company_ids ? explode(',', $request->company_ids) : null,
2133        );
2134
2135        $top5 = $this->accountCenterReportingService->getTop5Users($filter, $property);
2136
2137        // Get the top 5 users
2138        $top_5_users = $top5->map(function ($top) use ($property) {
2139            $user = User::firstWhere('_id', $top['user_id']);
2140
2141            if ($user) {
2142                $full_name = $user->first_name.' '.$user->last_name;
2143
2144                return ['name' => $full_name, 'count' => number_format($top[$property], 0, '.', ','), 'image' => $user->avatar];
2145            }
2146        });
2147
2148        return $top_5_users;
2149    }
2150
2151    public function reporting_characters_typed_saving_top_users(Request $request)
2152    {
2153        return response()->json(data: [
2154            'success' => true,
2155            'users' => $this->getTop5($request, 'characters_typed'),
2156        ]);
2157    }
2158
2159    public function reporting_fly_grammar_top_users(Request $request)
2160    {
2161        return response()->json(data: [
2162            'success' => true,
2163            'users' => $this->getTop5($request, 'fly_grammar_actions'),
2164        ]);
2165    }
2166
2167    public function reporting_fly_cuts_top_users(Request $request)
2168    {
2169        return response()->json(data: [
2170            'success' => true,
2171            'users' => $this->getTop5($request, 'flycut_count'),
2172        ]);
2173    }
2174
2175    public function reporting_fly_grammar_accepted_top_users(Request $request)
2176    {
2177        return response()->json(data: [
2178            'success' => true,
2179            'users' => $this->getTop5($request, 'fly_grammar_accepted'),
2180        ]);
2181    }
2182
2183    public function reporting_fly_grammar_autocorrect_top_users(Request $request)
2184    {
2185        return response()->json(data: [
2186            'success' => true,
2187            'users' => $this->getTop5($request, 'fly_grammar_autocorrect'),
2188        ]);
2189    }
2190
2191    public function reporting_fly_grammar_autocomplete_top_users(Request $request)
2192    {
2193        return response()->json(data: [
2194            'success' => true,
2195            'users' => $this->getTop5($request, 'fly_grammar_autocomplete'),
2196        ]);
2197    }
2198
2199    public function reporting_time_saving_top_users(Request $request)
2200    {
2201        return response()->json(data: [
2202            'success' => true,
2203            'users' => $this->getTop5($request, 'time_saved'),
2204        ]);
2205    }
2206
2207    public function reporting_cost_saving_top_users(Request $request)
2208    {
2209        return response()->json(data: [
2210            'success' => true,
2211            'users' => $this->getTop5($request, 'cost_savings'),
2212        ]);
2213    }
2214
2215    public function reporting_active_users(Request $request)
2216    {
2217        $filters = new FindUsersOverviewFilter(
2218            fromDate: $request->fromDate ? Carbon::parse($request->fromDate)->startOfDay() : null,
2219            toDate: $request->toDate ? Carbon::parse($request->toDate)->endOfDay() : null,
2220            userIds: array_filter(explode(',', $request->user_ids), function ($value) {
2221                return $value !== '';
2222            }),
2223            groupIds: array_filter(explode(',', $request->group_ids), function ($value) {
2224                return $value !== '';
2225            }),
2226            subgroupIds: array_filter(explode(',', $request->subgroup_ids), function ($value) {
2227                return $value !== '';
2228            }),
2229            companyIds: $request->company_ids ? explode(',', $request->company_ids) : null,
2230            currentRole: $request->current_role,
2231            companyId: $request->company_id,
2232            adminGroupIds: $request->admin_group_ids,
2233            monthPeriod: 12,
2234        );
2235
2236        $data = $this->accountCenterReportingService->findUsersOverview($filters);
2237
2238        return response()->json(data: [
2239            'success' => true,
2240            'data' => $data,
2241        ], status: 200);
2242    }
2243
2244    public function reporting_extension_usage(Request $request)
2245    {
2246        $admin = auth()->user();
2247        $roles = $admin->role;
2248        $role = role($roles);
2249        $company_id = $request->company_id;
2250        $admin_group_ids = $request->admin_group_ids;
2251
2252        $users = User::select([
2253            'id',
2254            'first_name',
2255            'last_name',
2256            'company_group_id',
2257            'company_id',
2258            'deleted_at',
2259            'avatar',
2260        ]);
2261
2262        $start_date = $request->fromDate;
2263        $end_date = $request->toDate;
2264
2265        if ($start_date && $end_date) {
2266            $start_date = Carbon::parse($start_date)->startOfDay();
2267            $end_date = Carbon::parse($end_date)->endOfDay();
2268        }
2269
2270        $user_ids = explode(',', $request->user_ids);
2271        $user_ids = array_filter($user_ids, function ($value) {
2272            return $value !== '';
2273        });
2274        if (count($user_ids) > 0) {
2275            $users = $users->whereIn('_id', $user_ids);
2276        }
2277
2278        $hasNotAssignedGroup = in_array('-1', explode(',', $request->group_ids));
2279
2280        if ($hasNotAssignedGroup) {
2281            $users = $users->whereNull('company_group_id');
2282        }
2283
2284        $group_ids = explode(',', $request->group_ids);
2285        $group_ids = array_filter($group_ids, function ($value) {
2286            return $value !== '' && $value !== '-1';
2287        });
2288
2289        if (count($group_ids) > 0) {
2290            $users = $users->whereIn('company_group_id', $group_ids);
2291        }
2292
2293        $subgroup_ids = explode(',', $request->subgroup_ids);
2294        $subgroup_ids = array_filter($subgroup_ids, function ($value) {
2295            return $value !== '';
2296        });
2297        if (count($subgroup_ids) > 0) {
2298            $users = $users->whereIn('company_group_id', $subgroup_ids);
2299        }
2300
2301        $users = $this->filterUsersByGroupIds($users, $role, $group_ids, $company_id, $admin_group_ids);
2302
2303        $users = $users->whereStatus('Active');
2304
2305        $users = $users->get();
2306        $ids = $users->pluck('id')->toArray();
2307
2308        $extensions_data = HubspotProperties::whereIn('flymsg_id', $ids);
2309
2310        if ($start_date && $end_date) {
2311            $extensions_data = $extensions_data->whereBetween('created_at', [
2312                new UTCDateTime($start_date->getTimestamp() * 1000),
2313                new UTCDateTime($end_date->getTimestamp() * 1000),
2314            ]);
2315        }
2316
2317        $extensions_data = $extensions_data->orderBy('created_at', 'desc')
2318            ->get()
2319            ->unique('flymsg_id');
2320
2321        $firstDate = $start_date;
2322        $lastDate = $end_date;
2323
2324        if (! $firstDate) {
2325            $firstDate = $extensions_data->min('created_at');
2326        }
2327
2328        if (! $lastDate) {
2329            $lastDate = $extensions_data->max('created_at');
2330        }
2331
2332        $months = $lastDate->diffInMonths($firstDate);
2333        $chart = $this->_make_line_chart_data_extension($extensions_data, $months, $firstDate);
2334
2335        $data = [
2336            'chart' => $chart,
2337        ];
2338
2339        return response()->json([
2340            'success' => true,
2341            'data' => $data,
2342        ], 200);
2343    }
2344
2345    public function reporting_user_details(Request $request)
2346    {
2347        $admin = auth()->user();
2348        $roles = $admin->role;
2349        $role = role($roles);
2350        $company_id = $request->company_id;
2351        $admin_group_ids = $request->admin_group_ids;
2352
2353        $start_date = $request->fromDate;
2354        $end_date = $request->toDate;
2355
2356        $users = User::withTrashed()
2357            ->select([
2358                'id',
2359                'first_name',
2360                'last_name',
2361                'company_group_id',
2362                'company_id',
2363                'deleted_at',
2364            ]);
2365
2366        if ($start_date && $end_date) {
2367            $start_date = Carbon::parse($start_date);
2368            $end_date = Carbon::parse($end_date);
2369
2370            $start_date = Carbon::now()->subMonths(12)->startOfMonth();
2371            $end_date = Carbon::now()->endOfMonth();
2372
2373            $users = $users->whereBetween('created_at', [
2374                new UTCDateTime($start_date->getTimestamp() * 1000),
2375                new UTCDateTime($end_date->getTimestamp() * 1000),
2376            ]);
2377        }
2378
2379        $users = $this->filterUsersByAdmin($users, $role, $company_id, $admin_group_ids);
2380
2381        $users = $users->get();
2382        $ids = $users->pluck('id')->toArray();
2383
2384        // TODO MA filter this data by the dates too?
2385        // - Total # of Characters Typed by FlyMSG by User
2386        // - Total Time Saved by FlyMSG by User
2387        // - Total Cost Savings by FlyMSG by User
2388
2389        $properties = HubspotProperties::whereIn('flymsg_id', $ids);
2390
2391        if ($start_date && $end_date) {
2392            $properties = $properties->whereBetween('created_at', [
2393                new UTCDateTime($start_date->getTimestamp() * 1000),
2394                new UTCDateTime($end_date->getTimestamp() * 1000),
2395            ]);
2396        }
2397
2398        $properties = $properties->get();
2399
2400        $properties = UserDetailsReportResource::collection($properties);
2401
2402        return response()->json([
2403            'success' => true,
2404            'users' => $properties,
2405        ], 200);
2406    }
2407
2408    public function reporting_get_columns(Request $request)
2409    {
2410        $admin = auth()->user();
2411        $company_id = $request->company_id;
2412
2413        $default_columns = collect();
2414
2415        $default_columns = $default_columns->push([
2416            'key' => 'first_name',
2417            'value' => 'First Name',
2418            'checked' => true,
2419        ]);
2420        $default_columns = $default_columns->push([
2421            'key' => 'last_name',
2422            'value' => 'Last Name',
2423            'checked' => true,
2424        ]);
2425        $default_columns = $default_columns->push([
2426            'key' => 'email',
2427            'value' => 'Email',
2428            'checked' => true,
2429        ]);
2430
2431        $columns = UserDetailsColumnSetting::where('user_id', $admin->id)
2432            ->where('company_id', $company_id)
2433            ->get();
2434
2435        if (count($columns) == 0) {
2436            $columns = collect(Constants::ADMIN_CENTER_USERS_TABLE_REPORTING_TABLE_HEADERS);
2437        } else {
2438            $columns = UserDetailsReportingResource::collection($columns);
2439            $columns = array_merge($default_columns->toArray(), $columns->toArray($request));
2440        }
2441
2442        return response()->json([
2443            'success' => true,
2444            'columns' => $columns,
2445        ], 200);
2446    }
2447
2448    public function reporting_save_columns(Request $request)
2449    {
2450        $validated_data = $request->validate([
2451            'columns' => 'required',
2452        ]);
2453
2454        $admin = auth()->user();
2455        $roles = $admin->role;
2456        $role = role($roles);
2457        $company_id = $request->company_id;
2458        $admin_group_ids = $request->admin_group_ids;
2459
2460        $columns = $validated_data['columns'];
2461
2462        foreach ($columns as $column) {
2463            $users_column = UserDetailsColumnSetting::where('user_id', $admin->id)
2464                ->where('key', $column['key'])
2465                ->first();
2466
2467            if ($users_column) {
2468                if ($users_column->column == 'first_name' || $users_column->column == 'last_name' || $users_column->column == 'email') {
2469                    continue;
2470                }
2471            }
2472
2473            $data = [
2474                'company_id' => $company_id,
2475                'user_id' => $admin->id,
2476                'key' => $column['key'],
2477                'value' => $column['value'],
2478                'checked' => $column['checked'],
2479            ];
2480
2481            if ($users_column) {
2482                $users_column->update($data);
2483            } else {
2484                UserDetailsColumnSetting::create($data);
2485            }
2486        }
2487
2488        $users = User::select([
2489            'id',
2490            'first_name',
2491            'last_name',
2492            'company_group_id',
2493            'company_id',
2494            'deleted_at',
2495            'avatar',
2496        ]);
2497
2498        $users = $this->filterUsersByAdmin($users, $role, $company_id, $admin_group_ids);
2499
2500        $ids = $users->pluck('id')->toArray();
2501
2502        $columns = UserDetailsColumnSetting::whereIn('user_id', $ids)
2503            ->where('company_id', $company_id)
2504            ->get();
2505
2506        return response()->json([
2507            'success' => true,
2508            'column' => $columns,
2509        ], 200);
2510    }
2511
2512    public function export_user_details_csv(Request $request)
2513    {
2514        $validated_data = $request->validate([
2515            'columns' => 'required',
2516        ]);
2517
2518        $admin = auth()->user();
2519        $company_id = $request->company_id;
2520
2521        $columns = UserDetailsColumnSetting::whereIn('user_id', $admin->id)
2522            ->where('company_id', $company_id)
2523            ->get();
2524
2525        if (count($columns) == 0) {
2526            $columns = collect(Constants::ADMIN_CENTER_USERS_TABLE_REPORTING_TABLE_HEADERS);
2527        }
2528
2529        return response()->json([
2530            'success' => true,
2531            'columns' => $columns,
2532        ], 200);
2533    }
2534
2535    public function reset_invited_user_password(Request $request)
2536    {
2537        $validated_data = $request->validate([
2538            'password' => 'required|confirmed|min:8|max:25',
2539        ]);
2540
2541        $user = $request->user();
2542
2543        $user->password = Hash::make($validated_data['password']);
2544
2545        if ($request->first_name) {
2546            $user->first_name = $request->first_name;
2547        }
2548
2549        if ($request->last_name) {
2550            $user->last_name = $request->last_name;
2551        }
2552
2553        // If user is invited then update status and delete invitation
2554        $userEmail = strtolower($user->email);
2555        $invitation = AdminUserInvitation::firstWhere('email', $userEmail);
2556        if ($invitation) {
2557            $user->status = 'Active';
2558            $user->activation_date = Carbon::now()->toDateTimeString();
2559            $user->company_id = $invitation->company_id;
2560            $user->company_group_id = $invitation->company_group_id ?? $invitation->company_subgroup_id;
2561
2562            FlyMsgUserDailyUsage::where('user_id', $user->id)
2563                ->update([
2564                    'company_id' => $user->company_id,
2565                    'group_id' => $user->company_group_id,
2566                    'user_status' => $user->status,
2567                ]);
2568
2569            $user->unset('invited_to_company');
2570            $user->unset('invited_to_company_by_admin');
2571        }
2572
2573        $user->save();
2574        User::where('_id', $user->id)
2575            ->update([
2576                '$unset' => [
2577                    'temp_password' => 1,
2578                    'temp_password_expiry' => 1,
2579                ],
2580            ]);
2581        if ($invitation) {
2582            $invitation->email = $userEmail;
2583            $invitation->delete();
2584        }
2585
2586        $data = [
2587            'first_name' => $user->first_name,
2588            'last_name' => $user->last_name,
2589            'email' => $userEmail,
2590            'do_not_send_welcome_notification' => true,
2591        ];
2592
2593        Registered::dispatch($user, $data);
2594
2595        return response()->json([
2596            'success' => true,
2597            'message' => 'Your password has been changed successfully.',
2598        ], 200);
2599    }
2600
2601    public function export_csv_report(Request $request)
2602    {
2603        $filters = $request->filters;
2604        $users_ids = $request->users ? explode(',', $request->users) : [];
2605
2606        return $this->csvExportService->downloadWithUserDelimiter(
2607            auth()->user(),
2608            fn () => (new UsersReportExport($filters, $users_ids))->download('reporting-details-export.csv')
2609        );
2610    }
2611
2612    public function export_csv_report_overview(Request $request)
2613    {
2614        $company_id = $request->company_id;
2615
2616        return $this->csvExportService->downloadWithUserDelimiter(
2617            auth()->user(),
2618            fn () => (new UsersReportOverviewExport($company_id))->download('reporting-overview-export.csv')
2619        );
2620    }
2621
2622    public function export_csv_report_usage(Request $request)
2623    {
2624        // $admin = auth()->user();
2625        // $from_date = $request->fromDate;
2626        // $to_date = $request->toDate;
2627        // $user_ids = $request->user_ids ? explode(',', $request->user_ids) : [];
2628        // $group_ids = $request->group_ids ? explode(',', $request->group_ids) : [];
2629        // $subgroup_ids = $request->subgroup_ids ? explode(',', $request->subgroup_ids) : [];
2630
2631        // return (new AllUsersReportUsageExport($admin, $from_date, $to_date, $user_ids, $group_ids, $subgroup_ids))->download('reporting-usage-export.csv');
2632
2633        $company_id = $request->company_id;
2634        $from_date = $request->fromDate;
2635        $to_date = $request->toDate;
2636        $users_ids = $request->users_ids ? explode(',', $request->user_ids) : [];
2637        $group_ids = $request->group_ids ? explode(',', $request->group_ids) : [];
2638        $subgroup_ids = $request->subgroup_ids ? explode(',', $request->users) : [];
2639
2640        return $this->csvExportService->downloadWithUserDelimiter(
2641            auth()->user(),
2642            fn () => (new UsersReportUsageExport($company_id, $from_date, $to_date, $users_ids, $group_ids, $subgroup_ids))->download('reporting-usage-export.csv')
2643        );
2644    }
2645
2646    public function remove_users_from_group(Request $request, $slug, CompanyGroup $company_group)
2647    {
2648        $user_ids = $request->users;
2649        if (! is_array($user_ids)) {
2650            throw new ExpectedException('You need to provide user ids in an array');
2651        }
2652
2653        $group = CompanyGroup::find($request->company_group_id);
2654        $this->adminUsersService->moveUsersToGroup($user_ids, $group);
2655
2656        return response()->json([
2657            'success' => true,
2658            'message' => "Users have been removed successfully from {$company_group->name}",
2659            'users' => $user_ids,
2660        ], 200);
2661    }
2662
2663    public function user_info(Request $request)
2664    {
2665        $user = User::where('email', $request->email)->first();
2666        $subscription = $user->subscription('main');
2667        $plan = $subscription->plan;
2668
2669        return response()->json([
2670            'flymsg_id' => $user->id,
2671            'name' => $user->first_name.' '.$user->last_name,
2672            'plan' => $plan->title,
2673            'identifier' => $plan->identifier,
2674            'email' => $request->email,
2675            'status' => $subscription->stripe_status,
2676            'ends_at' => $subscription->ends_at,
2677            'is_trial' => $subscription->onTrial(),
2678        ], 200);
2679    }
2680}