Skip to content

Commit

Permalink
Add more filters
Browse files Browse the repository at this point in the history
  • Loading branch information
GeniJaho committed Mar 19, 2024
1 parent f0bb64c commit 7413eb6
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 21 deletions.
6 changes: 6 additions & 0 deletions app/DTO/PhotoFilters.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ public function __construct(
public ?string $taken_until_local = null,
public ?bool $is_tagged = null,
public ?bool $has_gps = null,
public ?bool $picked_up = null,
public ?bool $recycled = null,
public ?bool $deposit = null,
) {
$this->item_ids = array_map(fn (int|string $id) => (int) $id, $this->item_ids);
$this->tag_ids = array_map(fn (int|string $id) => (int) $id, $this->tag_ids);
Expand All @@ -40,6 +43,9 @@ public static function rules(): array
'taken_until_local' => ['nullable', 'date_format:Y-m-d\\TH:i'],
'is_tagged' => ['nullable', 'boolean'],
'has_gps' => ['nullable', 'boolean'],
'picked_up' => ['nullable', 'boolean'],
'recycled' => ['nullable', 'boolean'],
'deposit' => ['nullable', 'boolean'],
];
}
}
9 changes: 8 additions & 1 deletion app/Models/Photo.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ public function scopeFilter(Builder $query, ?PhotoFilters $filters): void
return;
}

$photoItemProperties = array_filter([
'picked_up' => $filters->picked_up,
'recycled' => $filters->recycled,
'deposit' => $filters->deposit,
], fn ($value) => $value !== null);

$query
->when($filters->item_ids !== [], fn ($query) => $query
->whereHas('items', fn ($query) => $query
Expand All @@ -103,6 +109,7 @@ public function scopeFilter(Builder $query, ?PhotoFilters $filters): void
->when($filters->has_gps === true, fn ($query) => $query->whereNotNull('latitude')->whereNotNull('longitude'))
->when($filters->has_gps === false, fn ($query) => $query->where(fn ($query) => $query->whereNull('latitude')->orWhereNull('longitude')))
->when($filters->is_tagged === true, fn ($query) => $query->whereHas('items'))
->when($filters->is_tagged === false, fn ($query) => $query->whereDoesntHave('items'));
->when($filters->is_tagged === false, fn ($query) => $query->whereDoesntHave('items'))
->when($photoItemProperties !== [], fn ($query) => $query->whereHas('photoItems', fn ($query) => $query->where($photoItemProperties)));
}
}
82 changes: 62 additions & 20 deletions resources/js/Components/Filters.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ const filters = ref({
taken_until_local: props.defaultFilters?.taken_until_local ?? null,
has_gps: props.defaultFilters?.has_gps ?? null,
is_tagged: props.defaultFilters?.is_tagged ?? null,
picked_up: props.defaultFilters?.picked_up ?? null,
recycled: props.defaultFilters?.recycled ?? null,
deposit: props.defaultFilters?.deposit ?? null,
});
const selectedItems = ref(props.defaultFilters?.item_ids.map(id => props.items.find(item => item.id === parseInt(id))) ?? []);
Expand All @@ -42,6 +45,9 @@ const selectedContents = ref(props.tags.content.filter(content => filters.value.
const selectedSizes = ref(props.tags.size.filter(size => filters.value.tag_ids.includes(size.id)));
const hasGPS = ref(yesOrNoOptions.find(option => option.value === filters.value.has_gps));
const isTagged = ref(yesOrNoOptions.find(option => option.value === filters.value.is_tagged));
const pickedUp = ref(yesOrNoOptions.find(option => option.value === filters.value.picked_up));
const recycled = ref(yesOrNoOptions.find(option => option.value === filters.value.recycled));
const deposit = ref(yesOrNoOptions.find(option => option.value === filters.value.deposit));
const emit = defineEmits(['change']);
Expand All @@ -57,6 +63,9 @@ const filter = () => {
];
filters.value.has_gps = hasGPS.value.value !== null ? (hasGPS.value.value === true ? 1 : 0) : null;
filters.value.is_tagged = isTagged.value.value !== null ? (isTagged.value.value === true ? 1 : 0) : null;
filters.value.picked_up = pickedUp.value.value !== null ? (pickedUp.value.value === true ? 1 : 0) : null;
filters.value.recycled = recycled.value.value !== null ? (recycled.value.value === true ? 1 : 0) : null;
filters.value.deposit = deposit.value.value !== null ? (deposit.value.value === true ? 1 : 0) : null;
emit('change', {...filters.value, store_filters: 1});
}
Expand All @@ -70,6 +79,9 @@ const clear = () => {
selectedSizes.value = [];
hasGPS.value = yesOrNoOptions[0];
isTagged.value = yesOrNoOptions[0];
pickedUp.value = yesOrNoOptions[0];
recycled.value = yesOrNoOptions[0];
deposit.value = yesOrNoOptions[0];
filters.value = {
item_ids: [],
Expand All @@ -80,6 +92,9 @@ const clear = () => {
taken_until_local: null,
has_gps: null,
is_tagged: null,
picked_up: null,
recycled: null,
deposit: null,
};
emit('change', {...filters.value, clear_filters: 1});
Expand All @@ -90,7 +105,7 @@ const clear = () => {
<div class="flex flex-col lg:flex-row lg:space-x-4 w-full px-4 sm:p-0">
<div class="w-full">
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
<div class="col-span-1 lg:col-span-2">
<div>
<InputLabel for="item-filter" value="Items" />
<TagBox
id="item-filter"
Expand Down Expand Up @@ -121,45 +136,54 @@ const clear = () => {
></TagBox>
</div>
<div>
<InputLabel for="event-filter" value="Events" />
<InputLabel for="content-filter" value="Contents" />
<TagBox
id="event-filter"
v-model="selectedEvents"
:items="tags.event"
id="content-filter"
v-model="selectedContents"
:items="tags.content"
:multiple="true"
class="mt-1 block w-full"
></TagBox>
</div>
<div>
<InputLabel for="state-filter" value="States" />
<InputLabel for="size-filter" value="Sizes" />
<TagBox
id="state-filter"
v-model="selectedStates"
:items="tags.state"
id="size-filter"
v-model="selectedSizes"
:items="tags.size"
:multiple="true"
class="mt-1 block w-full"
></TagBox>
</div>
<div>
<InputLabel for="content-filter" value="Contents" />
<InputLabel for="state-filter" value="States" />
<TagBox
id="content-filter"
v-model="selectedContents"
:items="tags.content"
id="state-filter"
v-model="selectedStates"
:items="tags.state"
:multiple="true"
class="mt-1 block w-full"
></TagBox>
</div>
<div>
<InputLabel for="size-filter" value="Sizes" />
<InputLabel for="event-filter" value="Events" />
<TagBox
id="size-filter"
v-model="selectedSizes"
:items="tags.size"
id="event-filter"
v-model="selectedEvents"
:items="tags.event"
:multiple="true"
class="mt-1 block w-full"
></TagBox>
</div>
<div>
<InputLabel for="is-tagged" value="Is Tagged" />
<SelectInput
v-model="isTagged"
:options="yesOrNoOptions"
id="is-tagged"
class="block w-full mt-1"
></SelectInput>
</div>
<div>
<InputLabel for="uploaded-from" value="Uploaded From" />
<TextInput
Expand Down Expand Up @@ -206,11 +230,29 @@ const clear = () => {
></SelectInput>
</div>
<div>
<InputLabel for="is-tagged" value="Is Tagged" />
<InputLabel for="picked_up" value="Picked Up" />
<SelectInput
v-model="isTagged"
v-model="pickedUp"
:options="yesOrNoOptions"
id="is-tagged"
id="picked_up"
class="block w-full mt-1"
></SelectInput>
</div>
<div>
<InputLabel for="recycled" value="Recycled" />
<SelectInput
v-model="recycled"
:options="yesOrNoOptions"
id="recycled"
class="block w-full mt-1"
></SelectInput>
</div>
<div>
<InputLabel for="deposit" value="Deposit" />
<SelectInput
v-model="deposit"
:options="yesOrNoOptions"
id="deposit"
class="block w-full mt-1"
></SelectInput>
</div>
Expand Down
129 changes: 129 additions & 0 deletions tests/Feature/Photos/ListPhotosTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -290,3 +290,132 @@
->etc()
);
});

test('a user can filter their photos by being picked up or not', function () {
$this->actingAs($user = User::factory()->create());

$photoA = Photo::factory()->for($user)->create();
$photoB = Photo::factory()->for($user)->create();
$item = Item::factory()->create();
PhotoItem::factory()->for($item)->for($photoA)->create([
'picked_up' => false,
]);
PhotoItem::factory()->for($item)->for($photoB)->create([
'picked_up' => true,
]);

$response = $this->get('/my-photos?store_filters=1&picked_up=1');

$response->assertOk();
$response->assertInertia(fn (AssertableInertia $page) => $page
->component('Photos/Index')
->has('photos.data', 1)
->where('photos.data.0.id', $photoB->id)
->etc()
);

$response = $this->get('/my-photos?store_filters=1&picked_up=0');

$response->assertOk();
$response->assertInertia(fn (AssertableInertia $page) => $page
->component('Photos/Index')
->has('photos.data', 1)
->where('photos.data.0.id', $photoA->id)
->etc()
);

$response = $this->get('/my-photos?store_filters=1&picked_up=');

$response->assertOk();
$response->assertInertia(fn (AssertableInertia $page) => $page
->component('Photos/Index')
->has('photos.data', 2)
->etc()
);
});

test('a user can filter their photos by being recycled or not', function () {
$this->actingAs($user = User::factory()->create());

$photoA = Photo::factory()->for($user)->create();
$photoB = Photo::factory()->for($user)->create();
$item = Item::factory()->create();
PhotoItem::factory()->for($item)->for($photoA)->create([
'recycled' => false,
]);
PhotoItem::factory()->for($item)->for($photoB)->create([
'recycled' => true,
]);

$response = $this->get('/my-photos?store_filters=1&recycled=1');

$response->assertOk();
$response->assertInertia(fn (AssertableInertia $page) => $page
->component('Photos/Index')
->has('photos.data', 1)
->where('photos.data.0.id', $photoB->id)
->etc()
);

$response = $this->get('/my-photos?store_filters=1&recycled=0');

$response->assertOk();
$response->assertInertia(fn (AssertableInertia $page) => $page
->component('Photos/Index')
->has('photos.data', 1)
->where('photos.data.0.id', $photoA->id)
->etc()
);

$response = $this->get('/my-photos?store_filters=1&recycled=');

$response->assertOk();
$response->assertInertia(fn (AssertableInertia $page) => $page
->component('Photos/Index')
->has('photos.data', 2)
->etc()
);
});

test('a user can filter their photos by being deposit or not', function () {
$this->actingAs($user = User::factory()->create());

$photoA = Photo::factory()->for($user)->create();
$photoB = Photo::factory()->for($user)->create();
$item = Item::factory()->create();
PhotoItem::factory()->for($item)->for($photoA)->create([
'deposit' => false,
]);
PhotoItem::factory()->for($item)->for($photoB)->create([
'deposit' => true,
]);

$response = $this->get('/my-photos?store_filters=1&deposit=1');

$response->assertOk();
$response->assertInertia(fn (AssertableInertia $page) => $page
->component('Photos/Index')
->has('photos.data', 1)
->where('photos.data.0.id', $photoB->id)
->etc()
);

$response = $this->get('/my-photos?store_filters=1&deposit=0');

$response->assertOk();
$response->assertInertia(fn (AssertableInertia $page) => $page
->component('Photos/Index')
->has('photos.data', 1)
->where('photos.data.0.id', $photoA->id)
->etc()
);

$response = $this->get('/my-photos?store_filters=1&deposit=');

$response->assertOk();
$response->assertInertia(fn (AssertableInertia $page) => $page
->component('Photos/Index')
->has('photos.data', 2)
->etc()
);
});

0 comments on commit 7413eb6

Please sign in to comment.