From c218e44b4e675122f1b8a72f833c1dcda4038a35 Mon Sep 17 00:00:00 2001 From: Matteo Gheza Date: Wed, 15 Mar 2023 23:09:02 +0100 Subject: [PATCH] Implement availability schedules --- backend/app/Console/Kernel.php | 2 + .../Controllers/ScheduleSlotsController.php | 34 ++++++ .../UpdateAvailabilityWithSchedulesJob.php | 48 ++++++++ backend/app/Models/ScheduleSlots.php | 66 +++++++++++ ..._10_230556_create_schedule_slots_table.php | 30 +++++ backend/routes/api.php | 4 + ...modal-availability-schedule.component.html | 18 +-- .../modal-availability-schedule.component.ts | 106 ++++++------------ .../src/app/_routes/list/list.component.html | 2 +- 9 files changed, 229 insertions(+), 81 deletions(-) create mode 100644 backend/app/Http/Controllers/ScheduleSlotsController.php create mode 100644 backend/app/Jobs/UpdateAvailabilityWithSchedulesJob.php create mode 100644 backend/app/Models/ScheduleSlots.php create mode 100644 backend/database/migrations/2023_03_10_230556_create_schedule_slots_table.php diff --git a/backend/app/Console/Kernel.php b/backend/app/Console/Kernel.php index 7586ecd..c43d22d 100644 --- a/backend/app/Console/Kernel.php +++ b/backend/app/Console/Kernel.php @@ -5,6 +5,7 @@ namespace App\Console; use Illuminate\Console\Scheduling\Schedule; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; use App\Jobs\IncrementAvailabilityMinutesJob; +use App\Jobs\UpdateAvailabilityWithSchedulesJob; class Kernel extends ConsoleKernel { @@ -14,6 +15,7 @@ class Kernel extends ConsoleKernel protected function schedule(Schedule $schedule): void { $schedule->job(new IncrementAvailabilityMinutesJob)->everyMinute(); + $schedule->job(new UpdateAvailabilityWithSchedulesJob)->everyThirtyMinutes(); } /** diff --git a/backend/app/Http/Controllers/ScheduleSlotsController.php b/backend/app/Http/Controllers/ScheduleSlotsController.php new file mode 100644 index 0000000..6b07d6a --- /dev/null +++ b/backend/app/Http/Controllers/ScheduleSlotsController.php @@ -0,0 +1,34 @@ +where('user', $request->user()->id) + ->get(); + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + ScheduleSlots::where('user', $request->user()->id)->delete(); + + $schedules = array_map(function ($schedule) { + $schedule["user"] = auth()->id(); + return $schedule; + }, $request->input('schedules')); + + return ScheduleSlots::insert($schedules); + } +} diff --git a/backend/app/Jobs/UpdateAvailabilityWithSchedulesJob.php b/backend/app/Jobs/UpdateAvailabilityWithSchedulesJob.php new file mode 100644 index 0000000..d8f2344 --- /dev/null +++ b/backend/app/Jobs/UpdateAvailabilityWithSchedulesJob.php @@ -0,0 +1,48 @@ +dayOfWeek-1; + //There are 48 slots of 30 minutes, starting from 0 (00:00-00:30) to 47 (23:30-00:00) + $curr_slot = now()->hour * 2 + (now()->minute >= 30); + + $scheduled_users = ScheduleSlots::where([ + ["day", "=", $curr_day], + ["slot", "=", $curr_slot] + ])->pluck("user"); + + User::whereIn("id", $scheduled_users) + ->where([ + ["banned", "=", 0], + ["availability_manual_mode", "=", 0] + ]) + ->update(['available' => 1]); + } +} diff --git a/backend/app/Models/ScheduleSlots.php b/backend/app/Models/ScheduleSlots.php new file mode 100644 index 0000000..75526f2 --- /dev/null +++ b/backend/app/Models/ScheduleSlots.php @@ -0,0 +1,66 @@ + + */ + protected $fillable = [ + 'day', + 'slot' + ]; + + /** + * The attributes that should be hidden for serialization. + * + * @var array + */ + protected $hidden = [ + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + ]; + + /** + * Get the user that owns the phone. + */ + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + protected static function defAttr($messages, $attribute){ + + if(isset($messages[$attribute])){ + return $messages[$attribute]; + } + + $attributes = [ + "user" => auth()->id(), + ]; + + return $attributes[$attribute]; + } + + protected static function booted() + { + static::creating(function ($messages) { + $messages->user = self::defAttr($messages, "user"); + }); + } +} diff --git a/backend/database/migrations/2023_03_10_230556_create_schedule_slots_table.php b/backend/database/migrations/2023_03_10_230556_create_schedule_slots_table.php new file mode 100644 index 0000000..3ece992 --- /dev/null +++ b/backend/database/migrations/2023_03_10_230556_create_schedule_slots_table.php @@ -0,0 +1,30 @@ +id(); + $table->unsignedTinyInteger('day'); + $table->unsignedTinyInteger('slot'); + $table->unsignedBigInteger('user')->unsigned(); + $table->foreign('user')->references('id')->on('users')->cascadeOnDelete(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('schedule_slots'); + } +}; diff --git a/backend/routes/api.php b/backend/routes/api.php index 7595f58..31e96f3 100644 --- a/backend/routes/api.php +++ b/backend/routes/api.php @@ -3,6 +3,7 @@ use Illuminate\Support\Facades\Route; use App\Http\Controllers\AuthController; use App\Http\Controllers\UserController; +use App\Http\Controllers\ScheduleSlotsController; use App\Http\Controllers\AvailabilityController; use Illuminate\Http\Request; use Illuminate\Support\Facades\Artisan; @@ -27,6 +28,9 @@ Route::middleware('auth:sanctum')->group( function () { Route::get('/list', [UserController::class, 'index']); + Route::get('/schedules', [ScheduleSlotsController::class, 'index']); + Route::post('/schedules', [ScheduleSlotsController::class, 'store']); + Route::get('/availability', [AvailabilityController::class, 'get']); Route::post('/availability', [AvailabilityController::class, 'updateAvailability']); Route::post('/manual_mode', [AvailabilityController::class, 'updateAvailabilityManualMode']); diff --git a/frontend/src/app/_components/modal-availability-schedule/modal-availability-schedule.component.html b/frontend/src/app/_components/modal-availability-schedule/modal-availability-schedule.component.html index e7532ec..1ab4f94 100644 --- a/frontend/src/app/_components/modal-availability-schedule/modal-availability-schedule.component.html +++ b/frontend/src/app/_components/modal-availability-schedule/modal-availability-schedule.component.html @@ -11,22 +11,22 @@ - {{ day.short|translate }} + {{ day.short|translate }} - - {{ hour }} + + {{ isEven ? (slot/2) : ((slot-1)/2) }}:{{ isEven ? "00" : "30" }} - + - {{ hour }} + {{ slot }} - + @@ -34,9 +34,9 @@ - {{ day.short|translate }} - - + {{ day.short|translate }} + + diff --git a/frontend/src/app/_components/modal-availability-schedule/modal-availability-schedule.component.ts b/frontend/src/app/_components/modal-availability-schedule/modal-availability-schedule.component.ts index 47c94bc..163d417 100644 --- a/frontend/src/app/_components/modal-availability-schedule/modal-availability-schedule.component.ts +++ b/frontend/src/app/_components/modal-availability-schedule/modal-availability-schedule.component.ts @@ -41,37 +41,12 @@ export class ModalAvailabilityScheduleComponent implements OnInit { short: 'sunday_short' } ]; - public hours = [ - "0:00", "0:30", - "1:00", "1:30", - "2:00", "2:30", - "3:00", "3:30", - "4:00", "4:30", - "5:00", "5:30", - "6:00", "6:30", - "7:00", "7:30", - "8:00", "8:30", - "9:00", "9:30", - "10:00", "10:30", - "11:00", "11:30", - "12:00", "12:30", - "13:00", "13:30", - "14:00", "14:30", - "15:00", "15:30", - "16:00", "16:30", - "17:00", "17:30", - "18:00", "18:30", - "19:00", "19:30", - "20:00", "20:30", - "21:00", "21:30", - "22:00", "22:30", - "23:00", "23:30", - ]; + public slots = Array(48).fill(0).map((x,i)=>i); public selectedCells: any = []; //Used for "select all" - public selectedHours: string[] = []; + public selectedSlots: number[] = []; public selectedDays: number[] = []; public isSelecting = false; @@ -91,13 +66,9 @@ export class ModalAvailabilityScheduleComponent implements OnInit { ngOnInit(): void { this.orientation = window.innerHeight > window.innerWidth ? "portrait" : "landscape"; - if(localStorage.getItem('schedules') === null) { - this.api.get("schedules").then((response: any) => { - this.loadSchedules(response.schedules); - }); - } else { - this.loadSchedules(JSON.parse((localStorage.getItem('schedules') as string))); - } + this.api.get("schedules").then((response: any) => { + this.loadSchedules(response); + }); } saveChanges() { @@ -105,79 +76,72 @@ export class ModalAvailabilityScheduleComponent implements OnInit { this.api.post("schedules", { schedules: this.selectedCells }); - localStorage.removeItem('schedules'); this.bsModalRef.hide(); } - saveChangesInLocal() { - localStorage.setItem('schedules', JSON.stringify(this.selectedCells)); - } - @HostListener('window:resize', ['$event']) onResize(event: Event) { this.orientation = window.innerHeight > window.innerWidth ? "portrait" : "landscape"; } - isCellSelected(day: number, hour: string) { - return this.selectedCells.find((cell: any) => cell.day === day && cell.hour === hour); + isCellSelected(day: number, slot: number) { + return this.selectedCells.find((cell: any) => cell.day === day && cell.slot === slot); } - toggleCell(day: number, hour: string) { - if(!this.isCellSelected(day, hour)) { + toggleCell(day: number, slot: number) { + if(!this.isCellSelected(day, slot)) { this.selectedCells.push({ - day, hour + day, slot }); } else { - this.selectedCells = this.selectedCells.filter((cell: any) => cell.day !== day || cell.hour !== hour); + this.selectedCells = this.selectedCells.filter((cell: any) => cell.day !== day || cell.slot !== slot); } - this.saveChangesInLocal(); } - selectHour(hour: string) { - console.log("Hour selected", hour); - if(this.selectedHours.includes(hour)) { + selectEverySlotWithHour(slot: number) { + console.log("Slot hour selected", slot); + debugger; + if(this.selectedSlots.includes(slot)) { this.days.forEach((day: any, i: number) => { - this.selectedCells = this.selectedCells.filter((cell: any) => cell.day !== i || cell.hour !== hour); + this.selectedCells = this.selectedCells.filter((cell: any) => cell.day !== i || cell.slot !== slot); }); - this.selectedHours = this.selectedHours.filter((h: string) => h !== hour); + this.selectedSlots = this.selectedSlots.filter((h: number) => h !== slot); } else { this.days.forEach((day: any, i: number) => { - if(!this.isCellSelected(i, hour)) { + if(!this.isCellSelected(i, slot)) { this.selectedCells.push({ - day: i, hour + day: i, slot }); } }); - this.selectedHours.push(hour); + this.selectedSlots.push(slot); } - this.saveChangesInLocal(); } - selectDay(day: number) { + selectEntireDay(day: number) { console.log("Day selected", day); if(this.selectedDays.includes(day)) { - this.hours.forEach((hour: string) => { - this.selectedCells = this.selectedCells.filter((cell: any) => cell.day !== day || cell.hour !== hour); - }); + for (let slot = 0; slot < 48; slot++) { + this.selectedCells = this.selectedCells.filter((cell: any) => cell.day !== day || cell.slot !== slot); + } this.selectedDays = this.selectedDays.filter((i: number) => i !== day); } else { - this.hours.forEach((hour: string) => { - if(!this.isCellSelected(day, hour)) { + for (let slot = 0; slot < 48; slot++) { + if(!this.isCellSelected(day, slot)) { this.selectedCells.push({ - day, hour + day, slot }); } - }); + } this.selectedDays.push(day); } - this.saveChangesInLocal(); } - mouseDownCell(day: number, hour: string) { + mouseDownCell(day: number, slot: number) { this.isSelecting = true; console.log("Mouse down"); - console.log("Hour cell selected", day, hour); - this.toggleCell(day, hour); + console.log("Slot cell selected", day, slot); + this.toggleCell(day, slot); return false; } @@ -186,11 +150,11 @@ export class ModalAvailabilityScheduleComponent implements OnInit { console.log("Mouse up"); } - mouseOverCell(day: number, hour: string) { + mouseOverCell(day: number, slot: number) { if (this.isSelecting) { - console.log("Mouse over", day, hour); - console.log("Hour cell selected", day, hour); - this.toggleCell(day, hour); + console.log("Mouse over", day, slot); + console.log("Slot cell selected", day, slot); + this.toggleCell(day, slot); } } diff --git a/frontend/src/app/_routes/list/list.component.html b/frontend/src/app/_routes/list/list.component.html index cbb8545..d71e801 100644 --- a/frontend/src/app/_routes/list/list.component.html +++ b/frontend/src/app/_routes/list/list.component.html @@ -13,7 +13,7 @@
-