Skip to content

Commit c18b170

Browse files
committed
Load existing data to the appointment show/edit form
1 parent 722b2a7 commit c18b170

File tree

16 files changed

+248
-59
lines changed

16 files changed

+248
-59
lines changed

.idea/dataSources.xml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace App\Exceptions;
4+
5+
use Exception;
6+
7+
class ResourceConditionsException extends Exception
8+
{
9+
//
10+
}

app/Http/Controllers/AppointmentController.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace App\Http\Controllers;
44

55
use App\Http\Requests\ListAppointmentsRequest;
6+
use App\Http\Resources\AppointmentCollection;
67
use App\Http\Resources\AppointmentResource;
78
use App\Models\Animal;
89
use App\Models\Appointment;
@@ -33,7 +34,7 @@ public function index(ListAppointmentsRequest $request)
3334
$animalTypes = Animal::types(approved: false);
3435

3536
return Inertia::render('dashboard/appointments/Index', [
36-
'appointments' => AppointmentResource::collection($appointments),
37+
'appointments' => new AppointmentCollection($appointments)->listing(),
3738
'animalTypes' => $animalTypes,
3839
]);
3940
}
@@ -43,7 +44,7 @@ public function show(Appointment $appointment)
4344
$appointment = $appointment->load(['animal', 'animal.client']);
4445

4546
return Inertia::render('dashboard/appointments/Appointment', [
46-
'appointment' => $appointment,
47+
'appointment' => new AppointmentResource($appointment)->showing(),
4748
'animalTypes' => Animal::types(approved: false),
4849
'timesOfDay' => TimeOfDay::selectable(),
4950
]);

app/Http/Requests/StoreAppointmentSchedule.php

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use App\Helpers\AgeParser;
66
use App\TimeOfDay;
77
use Illuminate\Foundation\Http\FormRequest;
8-
use Illuminate\Support\Arr;
98
use Illuminate\Validation\Rule;
109

1110
class StoreAppointmentSchedule extends FormRequest
@@ -55,25 +54,20 @@ public function getAnimalAgeInMonths(): int
5554
}
5655

5756
/**
58-
* Casts preferred times from string to TimeOfDay enum and returns them
57+
* Casts preferred times from string to TimeOfDay enum
5958
*
6059
* This is necessary because they are sent as strings from the frontend
61-
*
62-
* @return TimeOfDay[]
6360
*/
64-
public function getAppointmentTimes(): array
61+
public function getAppointmentTimes(): TimeOfDay
6562
{
66-
return Arr::map(
67-
$this['appointment.preferred_time'],
68-
fn (string $time) => TimeOfDay::from($time)
69-
);
63+
return TimeOfDay::fromInputData($this['appointment.preferred_time']);
7064
}
7165

7266
public function getAppointmentData(): array
7367
{
7468
return [
7569
...$this['appointment'],
76-
'preferred_time' => TimeOfDay::allDayOrOneTime($this->getAppointmentTimes()),
70+
'preferred_time' => $this->getAppointmentTimes(),
7771
'animal_age_months' => $this->getAnimalAgeInMonths(),
7872
];
7973
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace App\Http\Resources;
4+
5+
use App\Exceptions\ResourceConditionsException;
6+
use App\ResourceConditions;
7+
use Illuminate\Http\Request;
8+
use Illuminate\Http\Resources\Json\ResourceCollection;
9+
10+
class AppointmentCollection extends ResourceCollection
11+
{
12+
use ResourceConditions;
13+
14+
protected array $conditions = ['listing', 'showing'];
15+
16+
/**
17+
* @throws ResourceConditionsException
18+
*/
19+
public function toArray(Request $request): array
20+
{
21+
// TODO Only set conditions on the resource itself, the ResourceCollection can just get it from there
22+
$this->injectConditions();
23+
24+
return parent::toArray($request);
25+
}
26+
}

app/Http/Resources/AppointmentResource.php

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,42 @@
44

55
use App\Models\Appointment;
66
use App\Models\User;
7+
use App\ResourceConditions;
8+
use App\TimeOfDay;
79
use Illuminate\Http\Request;
810
use Illuminate\Http\Resources\Json\JsonResource;
911

10-
/** @mixin Appointment */
12+
/** @mixin Appointment
13+
* @property bool $listing
14+
* @property bool $showing
15+
*/
1116
class AppointmentResource extends JsonResource
1217
{
18+
use ResourceConditions;
19+
20+
protected array $conditions = ['listing', 'showing'];
21+
1322
public function toArray(Request $request): array
1423
{
1524
return [
16-
'id' => $this->id,
17-
'preferred_date' => $this->preferred_date_formatted,
18-
'preferred_time' => $this->preferred_time_formatted,
19-
'symptoms' => $this->symptoms,
25+
'appointment' => [
26+
'id' => $this->when($this->listing, $this->id),
27+
'preferred_date_formatted' => $this->when($this->listing, $this->preferred_date_formatted),
28+
'preferred_time_formatted' => $this->when($this->listing, $this->preferred_time_formatted),
29+
'preferred_date' => $this->when($this->showing, $this->preferred_date->toDateString()),
30+
'preferred_time' => $this->when($this->showing, TimeOfDay::toInputData($this->preferred_time)),
31+
'symptoms' => $this->symptoms,
32+
],
2033
'animal' => [
2134
'name' => $this->animal->name,
2235
'type' => $this->animal->type,
23-
'age' => $this->animal_age_formatted,
36+
'age_human' => $this->animal_age->human(),
37+
'age_years' => $this->animal_age->years(),
38+
'age_months' => $this->animal_age->months(),
2439
],
2540
'client' => [
2641
'name' => $this->animal->client->name,
42+
'email' => $this->when($this->showing, $this->animal->client->email),
2743
],
2844
'medic' => $this->whenLoaded('medic', fn (User $medic) => [
2945
'name' => $medic->name,

app/Models/Appointment.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ class Appointment extends Model
2323
'animal_age_months',
2424
];
2525

26+
protected $dateFormat = 'Y-m-d';
27+
2628
protected $casts = [
27-
'preferred_date' => 'date',
29+
'preferred_date' => 'date:Y-m-d',
2830
'preferred_time' => TimeOfDay::class,
2931
'assigned_at' => 'timestamp',
3032
];
@@ -74,7 +76,7 @@ public function preferredTimeFormatted(): Attribute
7476
);
7577
}
7678

77-
public function animalAgeFormatted(): Attribute
79+
public function animalAge(): Attribute
7880
{
7981
return Attribute::make(
8082
get: fn () => new AgeParser($this->animal_age_months)

app/ResourceConditions.php

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
namespace App;
4+
5+
use App\Exceptions\ResourceConditionsException;
6+
use Illuminate\Http\Resources\DelegatesToResource;
7+
use Illuminate\Http\Resources\Json\ResourceCollection;
8+
9+
/**
10+
* Allows conditions to be passed to Resources and ResourceCollections
11+
*
12+
* Define `$conditions` in the resource/collection class and then call the name
13+
* of the conditions.
14+
*
15+
* Example:
16+
* <code>
17+
* class AnimalResource extends JsonResource {
18+
* use ResourceConditions;
19+
* protected $conditions = ['listing'];
20+
* }
21+
* </code>
22+
*
23+
* <code>
24+
* new AnimalResource($animal)->listing();
25+
* </code>
26+
*/
27+
trait ResourceConditions
28+
{
29+
use DelegatesToResource {
30+
__get as resourceGet;
31+
__call as resourceCall;
32+
}
33+
34+
public array $enabledConditions = [];
35+
36+
public function __call($name, $arguments)
37+
{
38+
in_array($name, $this->conditions)
39+
? array_push($this->enabledConditions, $name)
40+
: $this->resourceCall($name, $arguments);
41+
42+
return $this;
43+
}
44+
45+
public function __get($name)
46+
{
47+
return in_array($name, $this->conditions)
48+
? in_array($name, $this->enabledConditions)
49+
: $this->resourceGet($name);
50+
}
51+
52+
/**
53+
* @throws ResourceConditionsException
54+
*/
55+
public function injectConditions(): void
56+
{
57+
if (! is_subclass_of($this, ResourceCollection::class)) {
58+
throw new ResourceConditionsException(sprintf(
59+
"Class %s does not use %s, can't call injectConditions()",
60+
get_class($this), ResourceCollection::class
61+
));
62+
}
63+
64+
$this->collection->each(function ($resource) {
65+
if (! is_subclass_of($this, ResourceCollection::class)) {
66+
throw new ResourceConditionsException(sprintf(
67+
"Class %s does not use %s, can't call injectConditions()",
68+
get_class($resource), ResourceCollection::class
69+
));
70+
}
71+
72+
$resource->enabledConditions = $this->enabledConditions;
73+
});
74+
}
75+
}

app/TimeOfDay.php

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace App;
44

5+
use Illuminate\Support\Arr;
56
use Illuminate\Support\Str;
67

78
enum TimeOfDay: string
@@ -17,10 +18,17 @@ enum TimeOfDay: string
1718
*/
1819
public static function selectable(): array
1920
{
20-
return [
21-
self::Morning,
22-
self::Afternoon,
23-
];
21+
return [self::Morning, self::Afternoon];
22+
}
23+
24+
/**
25+
* Same as `selectable()` but returns an array of strings
26+
*
27+
* @return string[]
28+
*/
29+
public static function selectableStrings(): array
30+
{
31+
return Arr::map(self::selectable(), fn (TimeOfDay $time) => $time->value);
2432
}
2533

2634
/**
@@ -30,23 +38,29 @@ public static function selectable(): array
3038
*/
3139
public static function isAllDay(array $times): bool
3240
{
33-
return self::selectable() == $times;
41+
$selectable = array_map(fn (TimeOfDay $time) => $time->value, self::selectable());
42+
$given = array_map(fn (TimeOfDay $time) => $time->value, $times);
43+
44+
sort($selectable);
45+
sort($given);
46+
47+
return $selectable == $given;
3448
}
3549

3650
/**
3751
* If all possible times are selected, return `AllDay`; if not, return the only result
3852
*
3953
* @param TimeOfDay[] $times
4054
*/
41-
public static function allDayOrOneTime(array $times): TimeOfDay
55+
private static function allDayOrOneTime(array $times): TimeOfDay
4256
{
4357
if (self::isAllDay($times)) {
4458
return self::AllDay;
4559
}
4660

4761
assert(
4862
count($times) == 1,
49-
'Got times of day and it was not considered as AllDay'
63+
'Got times of day and it was not considered as AllDay. Did you pass an array of TimeOfDay?'
5064
);
5165

5266
return $times[0];
@@ -56,4 +70,23 @@ public function format(): string
5670
{
5771
return Str::headline($this->value);
5872
}
73+
74+
/**
75+
* @param string[] $values
76+
*/
77+
public static function fromInputData(array $values): TimeOfDay
78+
{
79+
$converted = array_map(fn (string $value) => TimeOfDay::from($value), $values);
80+
81+
return TimeOfDay::allDayOrOneTime($converted);
82+
}
83+
84+
public static function toInputData(TimeOfDay $value): array
85+
{
86+
if ($value == TimeOfDay::AllDay) {
87+
return TimeOfDay::selectable();
88+
}
89+
90+
return [$value];
91+
}
5992
}

database/factories/AppointmentFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public function definition(): array
2121
{
2222
return [
2323
'symptoms' => fake()->text(),
24-
'preferred_date' => fake()->dateTimeThisMonth('+15 days'),
24+
'preferred_date' => fake()->dateTimeThisMonth('+15 days')->format('Y-m-d'),
2525
'preferred_time' => fake()->randomElement(TimeOfDay::class),
2626
'animal_age_months' => fake()->randomNumber(2),
2727
'animal_id' => Animal::factory(),

0 commit comments

Comments
 (0)