Hi, I have this page that is pretty information heavy it contains 3 elements: some info cards, some filters and a map component. Depending on the filters and user role / permissions all the information changes especially the map component info which has two states: current locations where you see the active users last location or the user route where you follow one users movement through the day. The loading time is cra-zy like people are complaining the site doesn't work slow.
Here are some photos
https://ibb.co/Tcyzbd8 current locations -> this is the default information on the page
https://ibb.co/X7y9LtC following specific user's movement
I'm using vue2 + inertia.js :)
Here is my method. I know it's huge but I'm not sure how to make it better. I'm thinking to move the different parts to different methods and use axios to get it async but I'm not sure if there is a better way.
public function index(Request $request)
{
$remove_these_roles = ['Super Administrador', 'Back Office', 'Cliente'];
// Users
$users = User::query();
$users->select(['id as value', 'name as label', 'is_active']);
$users->whereHas('roles', fn ($q) => $q->whereNotIn('name', $remove_these_roles));
$locations = Location::query();
$locations->select('user_id')->selectRaw('MAX(datetime) AS "datetime"');
$locations->groupBy('user_id');
$locations->whereHas('user', function ($query) use ($remove_these_roles) {
$query->isActive();
$query->whereHas('roles', fn ($q) => $q->whereNotIn('name', $remove_these_roles));
});
// Sale Points Stats
$countSalePointCheckOuts = SalePointCheckOut::query()
->whereHas('user.roles', fn ($q) => $q->whereNotIn('name', $remove_these_roles));
$countSalePointCheckOuts->selectRaw(
"SUM(CASE WHEN sale_point_entry = ? AND sale_point_exit = ? THEN 1 END) AS visited",
[true, true]
);
$countSalePointCheckOuts->selectRaw(
"SUM(CASE WHEN sale_point_entry = ? AND sale_point_exit = ? THEN 1 END) AS in_progress",
[true, false]
);
$countSalePointCheckOuts->selectRaw(
"SUM(CASE WHEN sale_point_entry = ? AND sale_point_exit = ? THEN 1 END) AS not_visited",
[false, false]
);
$countSalePointCheckOuts->selectRaw(
"AVG(CASE WHEN sale_point_entry = ? AND sale_point_exit = ? THEN TIMESTAMPDIFF(MINUTE, date_entry, date_exit) END) as avg_time",
[true, true]
);
$countSalePointCheckOuts->selectRaw("COUNT(sale_point_check_outs.id) AS total_stops");
// User Movement
$userMovement = Location::select('*')->selectRaw('ROW_NUMBER() OVER (PARTITION BY user_id, DATE(datetime), CAST(latitude as decimal(13,3)), CAST(longitude as decimal(13,3)) ORDER BY datetime DESC) AS "row_number"');
$userMovement->where('precision', '<', 100);
// User Sale Point Check Outs
$userRouteCheckOuts = SalePointCheckOut::with('salePoint.salePoint', 'route', 'salePointRoute')
->whereHas('user.roles', fn ($q) => $q->whereNotIn('name', $remove_these_roles));
$userRouteCheckOuts->select('sale_point_check_outs.*');
$userRouteCheckOuts->leftJoin('sale_point_route', function ($join) {
$join->on('sale_point_route.sale_point_id', '=', 'sale_point_check_outs.sale_point_id');
$join->on('sale_point_route.route_id', '=', 'sale_point_check_outs.route_id');
});
$userRouteCheckOuts->orderBy('sale_point_route.priority');
// Filters
if (!$request->user()->can('gps-read') && $request->user()->can('gps-read-team')) {
$users->whereRelation('team', 'user_id', '=', $request->user()->id)
->orWhereHas('teams', function ($query) use ($request) {
$query->whereIn('teams.id', Team::query()->where('user_id', $request->user()->id)->select('id'));
});
$locations->whereRelation('locationSalePointCheckOut.user.team', 'id', '=', $request->user()->id)
->orWhereHas('locationSalePointCheckOut.user.teams', function ($query) use ($request) {
$query->whereIn('teams.id', Team::query()->where('user_id', $request->user()->id)->select('id'));
});
$userMovement->whereRelation('locationSalePointCheckOut.user.team', 'id', '=', $request->user()->id)
->orWhereHas('locationSalePointCheckOut.user.teams', function ($query) use ($request) {
$query->whereIn('teams.id', Team::query()->where('user_id', $request->user()->id)->select('id'));
});
$userRouteCheckOuts->whereRelation('user.team', 'user_id', '=', $request->user()->id)
->orWhereHas('user.teams', function ($query) use ($request) {
$query->whereIn('teams.id', Team::query()->where('user_id', $request->user()->id)->select('id'));
});
$countSalePointCheckOuts->whereRelation('user.team', 'user_id', '=', $request->user()->id)
->orWhereHas('user.teams', function ($query) use ($request) {
$query->whereIn('teams.id', Team::query()->where('user_id', $request->user()->id)->select('id'));
});
}
if ($request->filled('user_id')) {
$locations->where('user_id', $request->input('user_id'));
$userMovement->where('user_id', $request->input('user_id'));
$userRouteCheckOuts->where('user_id', $request->input('user_id'));
$countSalePointCheckOuts->where('user_id', $request->input('user_id'));
}
if ($request->filled('user_roles')) {
$users->whereHas('roles', fn ($query) => $query->whereIn('id', $request->input('user_roles')));
$locations->whereHas('user.roles', fn ($query) => $query->whereIn('id', $request->input('user_roles')));
$userMovement->whereHas('user.roles', fn ($query) => $query->whereIn('id', $request->input('user_roles')));
$userRouteCheckOuts->whereHas('user.roles', fn ($query) => $query->whereIn('id', $request->input('user_roles')));
$countSalePointCheckOuts->whereHas('user.roles', fn ($query) => $query->whereIn('id', $request->input('user_roles')));
}
$startDate = now()->setTimezone(tenant('timezone'))->startOfDay()->utc();
$endDateStartOfDay = now()->setTimezone(tenant('timezone'))->startOfDay()->utc();
$endDateEndOfDay = now()->setTimezone(tenant('timezone'))->endOfDay()->utc();
if ($request->filled('date_range.start', 'date_range.end')) {
$startDate = $request->date('date_range.start')->shiftTimezone(tenant('timezone'))->startOfDay()->utc();
$endDateStartOfDay = $request->date('date_range.end')->shiftTimezone(tenant('timezone'))->startOfDay()->utc();
$endDateEndOfDay = $request->date('date_range.end')->shiftTimezone(tenant('timezone'))->endOfDay()->utc();
}
$locations->where('datetime', '>=', $endDateStartOfDay);
$locations->where('datetime', '<=', $endDateEndOfDay);
$userMovement->where('datetime', '>=', $endDateStartOfDay);
$userMovement->where('datetime', '<=', $endDateEndOfDay);
$userRouteCheckOuts->whereDate('date', '>=', $startDate);
$userRouteCheckOuts->whereDate('date', '<=', $endDateStartOfDay);
$countSalePointCheckOuts->whereDate('date', '>=', $startDate);
$countSalePointCheckOuts->whereDate('date', '<=', $endDateStartOfDay);
// Current Locations
$currentLocations = Location::with('user.roles');
$currentLocations->whereHas('user.roles', fn ($q) => $q->whereNotIn('name', $remove_these_roles));
$currentLocations->joinSub($locations, 'last_locations', function (JoinClause $query) {
$query->on('last_locations.user_id', '=', 'locations.user_id');
$query->on('last_locations.datetime', '=', 'locations.datetime');
});
return Inertia::render(
'Tenant/GPS/GpsIndex',
[
'user_id' => $request->input('user_id'),
'roles' => Role::query()->has('users.salePointCheckOuts')->whereNotIn('name', $remove_these_roles)->get(['id', 'name']),
'users' => $users->orderByDesc('is_active')->orderBy('label')->get(),
'chart_info' => Inertia::lazy(function () use (&$countSalePointCheckOuts, $endDateEndOfDay) {
$countSalePointCheckOuts = $countSalePointCheckOuts->first();
return [
'visited' => $countSalePointCheckOuts?->visited ?: 0,
'last_visited' => $endDateEndOfDay,
'in_progress' => $countSalePointCheckOuts?->in_progress ?: 0,
'last_in_progress' => $endDateEndOfDay,
'not_visited' => $countSalePointCheckOuts?->not_visited ?: 0,
'last_not_visited' => $endDateEndOfDay,
'avg_time' => $countSalePointCheckOuts?->avg_time ?: 0,
'last_avg_time' => $endDateEndOfDay,
'total' => $countSalePointCheckOuts?->total_stops ?: 0
];
}),
'current_locations' => function () use ($currentLocations) {
$locations = $currentLocations->get();
return [
'locations' => $locations,
'counts' => $locations->countBy(fn ($location) => $location->user->roles[0]->name),
'totals' => Role::query()->withWhereHas('users', fn ($query) => $query->isActive())->withCount(['users' => fn ($query) => $query->isActive()])->get()
];
},
'user_route' => Inertia::lazy(fn () => Location::query()->fromSub($userMovement, 'locations')->where('row_number', 1)->orderByDesc('datetime')->get()),
'user_route_check_outs' => Inertia::lazy(fn () => $userRouteCheckOuts->with('salePoint.salePoint')->get()),
'chart_visited' => Inertia::lazy(fn () => $userRouteCheckOuts->with('user', 'justification', 'salePoint')->where('sale_point_entry', true)->where('sale_point_exit', true)->get()),
'chart_progress' => Inertia::lazy(fn () => $userRouteCheckOuts->with('user', 'justification', 'salePoint')->where('sale_point_entry', true)->where('sale_point_exit', false)->get()),
'chart_not_visited' => Inertia::lazy(fn () => $userRouteCheckOuts->with('user', 'justification', 'salePoint')->where('sale_point_entry', false)->where('sale_point_exit', false)->get()),
]
);
}
If you made it this far, thanks. Any suggestions are welcome.