Skip to content
Closed
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
2 changes: 1 addition & 1 deletion app-modules/activity/database/factories/MessageFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function definition(): array
return [
'id' => fake()->uuid(),
'external_identity_id' => ExternalIdentity::factory(),
'provider_message_id' => fake()->randomNumber(4),
'provider_message_id' => fake()->unique()->randomNumber(4),
Comment thread
fernanduandrade marked this conversation as resolved.
'channel_id' => fake()->randomNumber(4),
'content' => fake()->sentence(),
'sent_at' => now(),
Expand Down
44 changes: 44 additions & 0 deletions app-modules/community/database/factories/UpcomingEventFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace He4rt\Community\Database\Factories;

use He4rt\Community\UpcomingEvent\Enums\UpcomingEventCategory;
use He4rt\Community\UpcomingEvent\Models\UpcomingEvent;
use Illuminate\Database\Eloquent\Factories\Factory;

/**
* @extends Factory<UpcomingEvent>
*/
final class UpcomingEventFactory extends Factory
{
protected $model = UpcomingEvent::class;

public function definition(): array
{
return [
'title' => fake()->words(3, asText: true),
'description' => fake()->sentence(),
'category' => UpcomingEventCategory::ReuniaoSemanal,
'week_day' => fake()->numberBetween(0, 6),
'time' => fake()->time('H:i'),
'location' => null,
'external_url' => null,
'host_name' => fake()->name(),
'host_role' => fake()->jobTitle(),
'is_active' => true,
'skip_next_occurrence' => false,
'sort_order' => 0,
];
}

public function oneOff(): self
{
return $this->state(fn () => [
'week_day' => null,
'time' => null,
'event_at' => now()->addDays(fake()->numberBetween(1, 30)),
]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

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

return new class extends Migration
{
public function up(): void
{
Schema::create('upcoming_events', static function (Blueprint $table): void {
$table->uuid('id')->primary();
$table->string('title', 200);
$table->text('description')->nullable();
$table->string('category', 30);
$table->unsignedTinyInteger('week_day')->nullable();
$table->time('time')->nullable();
$table->timestampTz('event_at')->nullable();
$table->string('location', 255)->nullable();
$table->string('external_url', 255)->nullable();
$table->boolean('is_active')->default(value: true);
$table->boolean('skip_next_occurrence')->default(value: false);
$table->unsignedInteger('sort_order')->default(0);
$table->timestampsTz();

$table->index(['is_active', 'sort_order']);
});
}

public function down(): void
{
Schema::dropIfExists('upcoming_events');
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

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

return new class extends Migration
{
public function up(): void
{
Schema::table('upcoming_events', static function (Blueprint $table): void {
$table->string('host_name', 255)->nullable()->after('external_url');
$table->string('host_role', 255)->nullable()->after('host_name');
});
}

public function down(): void
{
Schema::table('upcoming_events', static function (Blueprint $table): void {
$table->dropColumn(['host_name', 'host_role']);
});
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

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

return new class extends Migration
{
public function up(): void
{
Schema::table('upcoming_events', static function (Blueprint $table): void {
$table->timestampTz('skip_until')->nullable()->after('skip_next_occurrence');
});
}

public function down(): void
{
Schema::table('upcoming_events', static function (Blueprint $table): void {
$table->dropColumn('skip_until');
});
}
};
6 changes: 6 additions & 0 deletions app-modules/community/src/CommunityServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace He4rt\Community;

use He4rt\Community\UpcomingEvent\Models\UpcomingEvent;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\ServiceProvider;

class CommunityServiceProvider extends ServiceProvider
Expand All @@ -13,5 +15,9 @@ public function register(): void {}
public function boot(): void
{
$this->loadMigrationsFrom(__DIR__.'/../database/migrations');

Relation::morphMap([
'upcoming_event' => UpcomingEvent::class,
]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace He4rt\Community\UpcomingEvent\Enums;

use Filament\Support\Contracts\HasLabel;

enum UpcomingEventCategory: string implements HasLabel
{
case ReuniaoSemanal = 'reuniao_semanal';

case Aula = 'aula';

case AulaIngles = 'aula_ingles';

case Onboarding = 'onboarding';

case Networking = 'networking';

public function getLabel(): string
{
return match ($this) {
self::ReuniaoSemanal => 'Reunião Semanal',
self::Aula => 'Aula Livre',
self::AulaIngles => 'Aula de Inglês',
self::Onboarding => 'Onboarding',
self::Networking => 'Networking',
};
}
}
104 changes: 104 additions & 0 deletions app-modules/community/src/UpcomingEvent/Models/UpcomingEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

declare(strict_types=1);

namespace He4rt\Community\UpcomingEvent\Models;

use Carbon\CarbonInterface;
use He4rt\Community\Database\Factories\UpcomingEventFactory;
use He4rt\Community\UpcomingEvent\Enums\UpcomingEventCategory;
use He4rt\Community\UpcomingEvent\Observers\UpcomingEventObserver;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Attributes\Table;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;

/**
* @property string $id
* @property string $title
* @property string|null $description
* @property UpcomingEventCategory $category
* @property int|null $week_day
* @property string|null $time
* @property CarbonInterface|null $event_at
* @property string|null $location
* @property string|null $external_url
* @property string|null $host_name
* @property string|null $host_role
* @property bool $is_active
* @property bool $skip_next_occurrence
* @property CarbonInterface|null $skip_until
* @property int $sort_order
* @property CarbonInterface|null $created_at
* @property CarbonInterface|null $updated_at
*/
#[ObservedBy(classes: UpcomingEventObserver::class)]
#[Table(name: 'upcoming_events')]
final class UpcomingEvent extends Model implements HasMedia
{
/** @use HasFactory<UpcomingEventFactory> */
use HasFactory;
use HasUuids;
use InteractsWithMedia;
Comment thread
fernanduandrade marked this conversation as resolved.

public function registerMediaCollections(): void
{
$this->addMediaCollection('cover')
->singleFile()
->useDisk('public');
}

public function nextOccurrence(): ?CarbonInterface
{
if ($this->event_at instanceof CarbonInterface) {
return $this->event_at;
}

if ($this->week_day === null || $this->time === null) {
return null;
}

$occurrence = $this->resolveRecurringOccurrence();

if ($this->skip_until instanceof CarbonInterface && $occurrence->lessThanOrEqualTo($this->skip_until)) {
return $occurrence->addWeek();
}
Comment thread
fernanduandrade marked this conversation as resolved.

return $occurrence;
}

protected static function newFactory(): UpcomingEventFactory
{
return UpcomingEventFactory::new();
}

protected function casts(): array
{
return [
'category' => UpcomingEventCategory::class,
'week_day' => 'integer',
'is_active' => 'boolean',
'skip_next_occurrence' => 'boolean',
'skip_until' => 'datetime',
'event_at' => 'datetime',
'sort_order' => 'integer',
];
}

private function resolveRecurringOccurrence(): CarbonInterface
{
$weekDay = (int) $this->week_day;
$time = (string) $this->time;

$now = now();

$base = $now->dayOfWeek === $weekDay && $now->format('H:i') < $time
? $now
: $now->copy()->next($weekDay);

return $base->setTimeFromTimeString($time);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace He4rt\Community\UpcomingEvent\Observers;

use Carbon\CarbonInterface;
use He4rt\Community\UpcomingEvent\Models\UpcomingEvent;

final readonly class UpcomingEventObserver
{
public function saving(UpcomingEvent $event): void
{
if ($event->skip_until instanceof CarbonInterface && $event->skip_until->isPast()) {
$event->skip_next_occurrence = false;
$event->skip_until = null;

return;
}

if (!$event->skip_next_occurrence) {
$event->skip_until = null;

return;
}

if ($event->skip_until === null) {
$event->skip_until = $event->nextOccurrence();
}
}
}
Loading