Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Calendar Integration for Tasks #204

Merged
merged 1 commit into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions app/Filament/App/Resources/TaskResource/Pages/CreateTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

use App\Filament\App\Resources\TaskResource;
use App\Services\GoogleCalendarService;
use App\Services\OutlookCalendarService;
use Filament\Actions;
use Filament\Forms\Components\DateTimePicker;
use Filament\Forms\Components\Toggle;
use Filament\Forms\Components\Select;
use Filament\Resources\Pages\CreateRecord;

class CreateTask extends CreateRecord
Expand All @@ -20,18 +21,23 @@ protected function getFormSchema(): array
[
DateTimePicker::make('reminder_date')
->label('Reminder Date'),
Toggle::make('sync_to_google_calendar')
->label('Sync to Google Calendar')
->default(false),
Select::make('calendar_type')
->label('Sync to Calendar')
->options([
'none' => 'No Sync',
'google' => 'Google Calendar',
'outlook' => 'Outlook Calendar',
])
->default('none')
->reactive(),
]
);
}

protected function afterCreate(): void
{
if ($this->record->sync_to_google_calendar) {
$googleCalendarService = app(GoogleCalendarService::class);
$googleCalendarService->createEvent($this->record);
if ($this->record->calendar_type !== 'none') {
$this->record->syncWithCalendar();
}
}
}
33 changes: 33 additions & 0 deletions app/Models/Task.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ class Task extends Model
'opportunity_id',
'reminder_date',
'reminder_sent',
'google_event_id',
'outlook_event_id',
'calendar_type',
];

protected $casts = [
Expand All @@ -44,4 +47,34 @@ public function opportunity()
{
return $this->belongsTo(Opportunity::class);
}

public function syncWithCalendar()
{
$calendarService = $this->getCalendarService();
if ($calendarService) {
if ($this->google_event_id || $this->outlook_event_id) {
$calendarService->updateEvent($this);
} else {
$calendarService->createEvent($this);
}
}
}

public function deleteFromCalendar()
{
$calendarService = $this->getCalendarService();
if ($calendarService && ($this->google_event_id || $this->outlook_event_id)) {
$calendarService->deleteEvent($this);
}
}

protected function getCalendarService()
{
if ($this->calendar_type === 'google') {
return app(GoogleCalendarService::class);
} elseif ($this->calendar_type === 'outlook') {
return app(OutlookCalendarService::class);
}
return null;
}
}
14 changes: 14 additions & 0 deletions app/Services/CalendarService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace App\Services;

use App\Models\Task;

interface CalendarService
{
public function createEvent(Task $task);
public function updateEvent(Task $task);
public function deleteEvent(Task $task);
public function fetchEvents(array $params = []);
public function syncEvents(array $events);
}
58 changes: 54 additions & 4 deletions app/Services/GoogleCalendarService.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use Google_Service_Calendar;
use Google_Service_Calendar_Event;

class GoogleCalendarService
class GoogleCalendarService implements CalendarService
{
protected $client;
protected $service;
Expand All @@ -31,16 +31,66 @@ public function createEvent(Task $task)
]);

$calendarId = 'primary';
$this->service->events->insert($calendarId, $event);
$createdEvent = $this->service->events->insert($calendarId, $event);

$task->google_event_id = $createdEvent->id;
$task->save();
}

public function updateEvent(Task $task)
{
// Implement update logic
$event = $this->service->events->get('primary', $task->google_event_id);

$event->setSummary($task->name);
$event->setDescription($task->description);
$event->setStart(['dateTime' => $task->due_date->toRfc3339String()]);
$event->setEnd(['dateTime' => $task->due_date->addHour()->toRfc3339String()]);

$this->service->events->update('primary', $event->getId(), $event);
}

public function deleteEvent(Task $task)
{
// Implement delete logic
$this->service->events->delete('primary', $task->google_event_id);
$task->google_event_id = null;
$task->save();
}

public function fetchEvents(array $params = [])
{
$optParams = [
'maxResults' => 100,
'orderBy' => 'startTime',
'singleEvents' => true,
'timeMin' => date('c'),
];

$optParams = array_merge($optParams, $params);

$results = $this->service->events->listEvents('primary', $optParams);
return $results->getItems();
}

public function syncEvents(array $events)
{
foreach ($events as $event) {
$task = Task::where('google_event_id', $event->id)->first();

if ($task) {
// Update existing task
$task->name = $event->getSummary();
$task->description = $event->getDescription();
$task->due_date = $event->getStart()->getDateTime();
$task->save();
} else {
// Create new task
Task::create([
'name' => $event->getSummary(),
'description' => $event->getDescription(),
'due_date' => $event->getStart()->getDateTime(),
'google_event_id' => $event->id,
]);
}
}
}
}
102 changes: 102 additions & 0 deletions app/Services/OutlookCalendarService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

namespace App\Services;

use App\Models\Task;
use Microsoft\Graph\Graph;
use Microsoft\Graph\Model;

class OutlookCalendarService implements CalendarService
{
protected $graph;

public function __construct()
{
$this->graph = new Graph();
$this->graph->setAccessToken($this->getAccessToken());
}

protected function getAccessToken()
{
// Implement OAuth logic to get access token
// This is a placeholder and should be replaced with actual OAuth implementation
return 'access_token';
}

public function createEvent(Task $task)
{
$event = new Model\Event();
$event->setSubject($task->name);
$event->setBody(new Model\ItemBody(['content' => $task->description]));
$event->setStart(new Model\DateTimeTimeZone(['dateTime' => $task->due_date->format('Y-m-d\TH:i:s'), 'timeZone' => 'UTC']));
$event->setEnd(new Model\DateTimeTimeZone(['dateTime' => $task->due_date->addHour()->format('Y-m-d\TH:i:s'), 'timeZone' => 'UTC']));

$newEvent = $this->graph->createRequest('POST', '/me/events')
->attachBody($event)
->setReturnType(Model\Event::class)
->execute();

$task->outlook_event_id = $newEvent->getId();
$task->save();
}

public function updateEvent(Task $task)
{
$event = new Model\Event();
$event->setSubject($task->name);
$event->setBody(new Model\ItemBody(['content' => $task->description]));
$event->setStart(new Model\DateTimeTimeZone(['dateTime' => $task->due_date->format('Y-m-d\TH:i:s'), 'timeZone' => 'UTC']));
$event->setEnd(new Model\DateTimeTimeZone(['dateTime' => $task->due_date->addHour()->format('Y-m-d\TH:i:s'), 'timeZone' => 'UTC']));

$this->graph->createRequest('PATCH', '/me/events/' . $task->outlook_event_id)
->attachBody($event)
->execute();
}

public function deleteEvent(Task $task)
{
$this->graph->createRequest('DELETE', '/me/events/' . $task->outlook_event_id)
->execute();

$task->outlook_event_id = null;
$task->save();
}

public function fetchEvents(array $params = [])
{
$queryParams = [
'$top' => $params['maxResults'] ?? 100,
'$orderby' => 'start/dateTime',
'$filter' => 'start/dateTime ge ' . date('c'),
];

$events = $this->graph->createRequest('GET', '/me/events?' . http_build_query($queryParams))
->setReturnType(Model\Event::class)
->execute();

return $events;
}

public function syncEvents(array $events)
{
foreach ($events as $event) {
$task = Task::where('outlook_event_id', $event->getId())->first();

if ($task) {
// Update existing task
$task->name = $event->getSubject();
$task->description = $event->getBody()->getContent();
$task->due_date = new \DateTime($event->getStart()->getDateTime());
$task->save();
} else {
// Create new task
Task::create([
'name' => $event->getSubject(),
'description' => $event->getBody()->getContent(),
'due_date' => new \DateTime($event->getStart()->getDateTime()),
'outlook_event_id' => $event->getId(),
]);
}
}
}
}
13 changes: 13 additions & 0 deletions config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,17 @@
];
},

'google' => [
'client_id' => env('GOOGLE_CLIENT_ID'),
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
'redirect_uri' => env('GOOGLE_REDIRECT_URI'),
'credentials_path' => env('GOOGLE_CREDENTIALS_PATH'),
],

'outlook' => [
'client_id' => env('OUTLOOK_CLIENT_ID'),
'client_secret' => env('OUTLOOK_CLIENT_SECRET'),
'redirect_uri' => env('OUTLOOK_REDIRECT_URI'),
],

];
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

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

class AddCalendarFieldsToTasksTable extends Migration
{
public function up()
{
Schema::table('tasks', function (Blueprint $table) {
$table->string('google_event_id')->nullable();
$table->string('outlook_event_id')->nullable();
$table->enum('calendar_type', ['none', 'google', 'outlook'])->default('none');
});
}

public function down()
{
Schema::table('tasks', function (Blueprint $table) {
$table->dropColumn(['google_event_id', 'outlook_event_id', 'calendar_type']);
});
}
}
Loading
Loading