Skip to content

Commit

Permalink
feat: add ability to set timezone of an employee (#706)
Browse files Browse the repository at this point in the history
  • Loading branch information
djaiss committed Apr 6, 2021
1 parent 4210b0e commit e394aad
Show file tree
Hide file tree
Showing 15 changed files with 355 additions and 61 deletions.
13 changes: 6 additions & 7 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ APP_KEY=
APP_DEBUG=true
APP_URL=https://officelife.test

LOG_CHANNEL=stack

# Database to store information
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
Expand All @@ -21,16 +19,22 @@ DB_TEST_HOST=127.0.0.1
DB_TEST_USERNAME=root
DB_TEST_PASSWORD=root

# Logs
LOG_CHANNEL=stack

# Drivers
BROADCAST_DRIVER=log
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120

# Redis, if you need it
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

# Mailing
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
Expand All @@ -41,11 +45,6 @@ MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=
MAIL_FROM_NAME=OfficeLife

PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1

MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"

Expand Down
109 changes: 109 additions & 0 deletions app/Helpers/TimezoneHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php

namespace App\Helpers;

use DateTimeZone;
use Carbon\Carbon;

class TimezoneHelper
{
/**
* Get the list of timezones.
*
* @return array
*/
public static function getListOfTimezones(): array
{
$timezones = DateTimeZone::listIdentifiers();

$timezoneCollection = collect();
foreach ($timezones as $timezone) {
$formatedTimezone = self::formatTimezone($timezone);
$timezoneCollection->push([
'value' => $formatedTimezone['name'],
'label' => $formatedTimezone['friendly_name'],
'sort_key' => $formatedTimezone['offset'],
]);
}

// we need to remove the keys we don't need as the view needs only 2 keys
$timezoneCollection = $timezoneCollection->sortBy('sort_key');
$timezoneCollection = $timezoneCollection->map(function ($item) {
return array_only($item, ['value', 'label']);
});

// we need to reset the keys before converting it to an array
// otherwise, Vue doesn't like it
return $timezoneCollection->values()->toArray();
}

/**
* Format a timezone to be displayed (english locale only).
*
* @param string $timezone
* @return array
*/
private static function formatTimezone(string $timezone): array
{
$offset = self::getOffset($timezone);

if ($timezone == 'UTC') {
$timezoneName = 'UTC';
} else {
$timezoneName = Carbon::now($timezone)->tzName;
}

$friendlyName = self::getTimezoneFriendlyName($timezoneName, $offset);

return [
'name' => $timezoneName,
'friendly_name' => $friendlyName,
'offset' => $offset,
];
}

private static function getTimezoneFriendlyName(string $timezoneName, string $offset): string
{
$dtimezone = new DateTimeZone($timezoneName);
$location = $dtimezone->getLocation();

if ($timezoneName == 'UTC') {
$friendlyName = '(UTC) Universal Time Coordinated';
} else {
if (empty($location['comments'])) {
$friendlyName = '(UTC '.$offset.') '.$timezoneName;
} else {
$friendlyName = '(UTC '.$offset.') '.$location['comments'].' ('.$timezoneName.')';
}
}

return $friendlyName;
}

private static function getOffset(string $timezoneName): string
{
return Carbon::now($timezoneName)->format('P');
}

/**
* Get the value/label combo of the timezone in an array based on its codename.
*
* @param string $timezoneName
* @return array
*/
public static function getTimezoneKeyValue(string $timezoneName): array
{
$timezones = DateTimeZone::listIdentifiers();

foreach ($timezones as $timezone) {
if ($timezoneName == Carbon::now($timezone)->tzName) {
return [
'value' => $timezoneName,
'label' => self::getTimezoneFriendlyName($timezoneName, self::getOffset($timezoneName)),
];
}
}

return [];
}
}
26 changes: 5 additions & 21 deletions app/Http/Controllers/Company/Employee/EmployeeEditController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Inertia\Response;
use Illuminate\Http\Request;
use App\Helpers\InstanceHelper;
use App\Helpers\TimezoneHelper;
use App\Models\Company\Country;
use App\Models\Company\Employee;
use Illuminate\Http\JsonResponse;
Expand All @@ -15,6 +16,7 @@
use Illuminate\Support\Facades\Auth;
use App\Services\Company\Place\CreatePlace;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use App\Http\ViewHelpers\Employee\EmployeeEditViewHelper;
use App\Http\ViewHelpers\Employee\EmployeeShowViewHelper;
use App\Services\Company\Employee\Birthdate\SetBirthdate;
use App\Services\Company\Employee\HiringDate\SetHiringDate;
Expand Down Expand Up @@ -59,27 +61,8 @@ public function show(Request $request, int $companyId, int $employeeId)
}

return Inertia::render('Employee/Edit', [
'employee' => [
'id' => $employee->id,
'first_name' => $employee->first_name,
'last_name' => $employee->last_name,
'name' => $employee->name,
'email' => $employee->email,
'phone' => $employee->phone_number,
'birthdate' => (! $employee->birthdate) ? null : [
'year' => $employee->birthdate->year,
'month' => $employee->birthdate->month,
'day' => $employee->birthdate->day,
],
'hired_at' => (! $employee->hired_at) ? null : [
'year' => $employee->hired_at->year,
'month' => $employee->hired_at->month,
'day' => $employee->hired_at->day,
],
'twitter_handle' => $employee->twitter_handle,
'slack_handle' => $employee->slack_handle,
'max_year' => Carbon::now()->year,
],
'employee' => EmployeeEditViewHelper::show($employee),
'timezones' => TimezoneHelper::getListOfTimezones(),
'permissions' => EmployeeShowViewHelper::permissions($loggedEmployee, $employee),
'notifications' => NotificationHelper::getNotifications(InstanceHelper::getLoggedEmployee()),
]);
Expand All @@ -105,6 +88,7 @@ public function update(Request $request, int $companyId, int $employeeId): JsonR
'last_name' => $request->input('last_name'),
'email' => $request->input('email'),
'phone' => $request->input('phone'),
'timezone' => $request->input('timezone'),
];

(new SetPersonalDetails)->execute($data);
Expand Down
42 changes: 42 additions & 0 deletions app/Http/ViewHelpers/Employee/EmployeeEditViewHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace App\Http\ViewHelpers\Employee;

use Carbon\Carbon;
use App\Helpers\TimezoneHelper;
use App\Models\Company\Employee;

class EmployeeEditViewHelper
{
/**
* Get information about the employee being edited.
*
* @param Employee $employee
* @return array
*/
public static function show(Employee $employee): array
{
return [
'id' => $employee->id,
'first_name' => $employee->first_name,
'last_name' => $employee->last_name,
'name' => $employee->name,
'email' => $employee->email,
'phone' => $employee->phone_number,
'birthdate' => (! $employee->birthdate) ? null : [
'year' => $employee->birthdate->year,
'month' => $employee->birthdate->month,
'day' => $employee->birthdate->day,
],
'hired_at' => (! $employee->hired_at) ? null : [
'year' => $employee->hired_at->year,
'month' => $employee->hired_at->month,
'day' => $employee->hired_at->day,
],
'twitter_handle' => $employee->twitter_handle,
'slack_handle' => $employee->slack_handle,
'max_year' => Carbon::now()->year,
'timezone' => TimezoneHelper::getTimezoneKeyValue($employee->timezone),
];
}
}
1 change: 1 addition & 0 deletions app/Models/Company/Employee.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class Employee extends Model
'email',
'first_name',
'last_name',
'timezone',
'birthdate',
'hired_at',
'description',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
class SetPersonalDetails extends BaseService
{
private Employee $employee;
private array $data;

/**
* Get the validation rules that apply to the service.
Expand All @@ -28,50 +29,54 @@ public function rules(): array
'last_name' => 'required|string|max:255',
'email' => 'required|email:rfc|max:255',
'phone' => 'nullable|max:255',
'timezone' => 'required|string|max:255',
];
}

/**
* Set the name and email address of an employee.
*
* @param array $data
*
* @throws NotEnoughPermissionException
*
* @return Employee
*/
public function execute(array $data): Employee
{
$this->validateRules($data);

$this->author($data['author_id'])
->inCompany($data['company_id'])
->asAtLeastHR()
->canBypassPermissionLevelIfEmployee($data['employee_id'])
->canExecuteService();
$this->data = $data;
$this->validate();
$this->save();
$this->log();

$this->employee = $this->validateEmployeeBelongsToCompany($data);
return $this->employee->refresh();
}

$this->save($data);
private function validate(): void
{
$this->validateRules($this->data);

$this->log($data);
$this->author($this->data['author_id'])
->inCompany($this->data['company_id'])
->asAtLeastHR()
->canBypassPermissionLevelIfEmployee($this->data['employee_id'])
->canExecuteService();

return $this->employee->refresh();
$this->employee = $this->validateEmployeeBelongsToCompany($this->data);
}

private function save(array $data): void
private function save(): void
{
$this->employee->first_name = $data['first_name'];
$this->employee->last_name = $data['last_name'];
$this->employee->email = $data['email'];
$this->employee->phone_number = $data['phone'];
$this->employee->first_name = $this->data['first_name'];
$this->employee->last_name = $this->data['last_name'];
$this->employee->email = $this->data['email'];
$this->employee->phone_number = $this->data['phone'];
$this->employee->timezone = $this->data['timezone'];
$this->employee->save();
}

private function log(array $data): void
private function log(): void
{
LogAccountAudit::dispatch([
'company_id' => $data['company_id'],
'company_id' => $this->data['company_id'],
'action' => 'employee_personal_details_set',
'author_id' => $this->author->id,
'author_name' => $this->author->name,
Expand All @@ -84,7 +89,7 @@ private function log(array $data): void
])->onQueue('low');

LogEmployeeAudit::dispatch([
'employee_id' => $data['employee_id'],
'employee_id' => $this->data['employee_id'],
'action' => 'personal_details_set',
'author_id' => $this->author->id,
'author_name' => $this->author->name,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class AddTimezonesToEmployees extends Migration
{
/**
* Run the migrations.
*/
public function up()
{
// necessary for SQLlite
Schema::enableForeignKeyConstraints();

Schema::table('employees', function (Blueprint $table) {
$table->string('timezone')->after('pronoun_id')->default('UTC');
});
}
}
1 change: 1 addition & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
<PossiblyNullPropertyAssignmentValue errorLevel="info" />
<PossiblyNullPropertyFetch errorLevel="info" />
<PossiblyNullReference errorLevel="info" />
<PossiblyFalseIterator errorLevel="info" />

<!-- level 5 issues - should be avoided at mosts costs... -->

Expand Down

0 comments on commit e394aad

Please sign in to comment.