Skip to content

Commit 99ba948

Browse files
committed
Can filter stars
1 parent 773a44a commit 99ba948

3 files changed

Lines changed: 172 additions & 21 deletions

File tree

app/Http/Controllers/ComputerScienceResourceController.php

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ public function index(Request $request)
5151
'general_tags' => $request->query('general_tags'),
5252

5353
'community_rating' => $request->query('community_rating'),
54+
'teaching_clarity' => $request->query('teaching_clarity'),
55+
'engagement' => $request->query('engagement'),
56+
'practicality' => $request->query('practicality'),
57+
'user_friendliness' => $request->query('user_friendliness'),
58+
'updates' => $request->query('updates'),
5459
],
5560
[
5661
'name' => ['nullable', 'string', 'max:100'],
@@ -69,6 +74,11 @@ public function index(Request $request)
6974
'programming_language_tags.*' => ['required', 'distinct', 'string', 'max:50'],
7075

7176
'community_rating' => ['nullable', 'integer', 'between:1,4'],
77+
'teaching_clarity' => ['nullable', 'integer', 'between:1,4'],
78+
'engagement' => ['nullable', 'integer', 'between:1,4'],
79+
'practicality' => ['nullable', 'integer', 'between:1,4'],
80+
'user_friendliness' => ['nullable', 'integer', 'between:1,4'],
81+
'updates' => ['nullable', 'integer', 'between:1,4'],
7282
]);
7383

7484
if (!$validator->validate())
@@ -121,9 +131,21 @@ public function index(Request $request)
121131
$query->withAnyTags((array) $generalTags, 'general_tags');
122132
}
123133

124-
// Filter by reviews
125-
if ($commmunityRating = $request->query('community_rating')) {
126-
$query = $this->reviewService->applyRatingFilter($query, 'community', $commmunityRating);
134+
135+
/// Filter by reviews
136+
$ratingFilters = [
137+
'community_rating' => 'community',
138+
'teaching_clarity' => 'teaching',
139+
'engagement' => 'engagement',
140+
'practicality' => 'practicality',
141+
'user_friendliness' => 'usability',
142+
'updates' => 'updates',
143+
];
144+
145+
foreach ($ratingFilters as $param => $ratingType) {
146+
if ($rating = $request->query($param)) {
147+
$query = $this->reviewService->applyRatingFilter($query, $ratingType, $rating);
148+
}
127149
}
128150

129151
// Paginate and return

resources/js/Components/Resources/FilterBar.vue

Lines changed: 146 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,36 @@ import { platforms, pricings, difficulties } from "@/Helpers/labels";
55
import InputText from "primevue/inputtext";
66
import MultiSelect from "primevue/multiselect";
77
import Button from "primevue/button";
8+
import Rating from "primevue/rating";
89
910
import TagSelector from "@/Components/Form/TagSelector.vue";
1011
12+
// text filters
1113
const name = ref("");
1214
const description = ref("");
15+
16+
// multi-select filters
1317
const selectedPlatforms = ref([]);
1418
const selectedDifficulty = ref([]);
1519
const selectedPricing = ref([]);
1620
const selectedTopics = ref([]);
1721
const selectedProgrammingLanguages = ref([]);
1822
const selectedGeneralTags = ref([]);
1923
24+
// rating filters
25+
const selectedCommunityRating = ref(null);
26+
const selectedTeachingClarity = ref(null);
27+
const selectedEngagement = ref(null);
28+
const selectedPracticality = ref(null);
29+
const selectedUserFriendliness = ref(null);
30+
const selectedUpdates = ref(null);
31+
2032
onMounted(() => {
2133
const urlParams = new URLSearchParams(window.location.search);
2234
23-
// single‐value fields
2435
name.value = urlParams.get("name") || "";
2536
description.value = urlParams.get("description") || "";
2637
27-
// multi‐value fields that may be named like difficulty[0], difficulty[1], …
2838
selectedPlatforms.value = extractIndexedArray(urlParams, "platforms");
2939
selectedDifficulty.value = extractIndexedArray(urlParams, "difficulty");
3040
selectedPricing.value = extractIndexedArray(urlParams, "pricing");
@@ -34,11 +44,21 @@ onMounted(() => {
3444
"programming_languages"
3545
);
3646
selectedGeneralTags.value = extractIndexedArray(urlParams, "general_tags");
47+
48+
const ratingsMap = {
49+
community_rating: selectedCommunityRating,
50+
teaching_clarity: selectedTeachingClarity,
51+
engagement: selectedEngagement,
52+
practicality: selectedPracticality,
53+
user_friendliness: selectedUserFriendliness,
54+
updates: selectedUpdates,
55+
};
56+
for (const [param, refVar] of Object.entries(ratingsMap)) {
57+
const v = urlParams.get(param);
58+
refVar.value = v ? parseInt(v, 10) : null;
59+
}
3760
});
3861
39-
/**
40-
* Pulls out all params named `${base}[0]`, `${base}[1]`, … into a flat array.
41-
*/
4262
function extractIndexedArray(urlParams, base) {
4363
const result = [];
4464
for (const [key, value] of urlParams) {
@@ -72,11 +92,38 @@ function search() {
7292
general_tags: selectedGeneralTags.value.length
7393
? selectedGeneralTags.value
7494
: undefined,
75-
community_rating: 1,
95+
community_rating: selectedCommunityRating.value || undefined,
96+
teaching_clarity: selectedTeachingClarity.value || undefined,
97+
engagement: selectedEngagement.value || undefined,
98+
practicality: selectedPracticality.value || undefined,
99+
user_friendliness: selectedUserFriendliness.value || undefined,
100+
updates: selectedUpdates.value || undefined,
76101
}),
77102
{ preserveScroll: true }
78103
);
79104
}
105+
106+
function resetFilters() {
107+
// text
108+
name.value = "";
109+
description.value = "";
110+
111+
// arrays
112+
selectedPlatforms.value = [];
113+
selectedDifficulty.value = [];
114+
selectedPricing.value = [];
115+
selectedTopics.value = [];
116+
selectedProgrammingLanguages.value = [];
117+
selectedGeneralTags.value = [];
118+
119+
// ratings
120+
selectedCommunityRating.value = null;
121+
selectedTeachingClarity.value = null;
122+
selectedEngagement.value = null;
123+
selectedPracticality.value = null;
124+
selectedUserFriendliness.value = null;
125+
selectedUpdates.value = null;
126+
}
80127
</script>
81128

82129
<template>
@@ -141,26 +188,108 @@ function search() {
141188
/>
142189
</div>
143190

191+
<!-- Topics -->
192+
<div class="flex-1 min-w-[200px]">
193+
<label class="block text-sm font-medium mb-1">Topics</label>
194+
<TagSelector v-model="selectedTopics" />
195+
</div>
196+
197+
<!-- Programming Languages -->
198+
<div class="flex-1 min-w-[200px]">
199+
<label class="block text-sm font-medium mb-1"
200+
>Programming Languages</label
201+
>
202+
<TagSelector v-model="selectedProgrammingLanguages" />
203+
</div>
204+
205+
<!-- Other Tags -->
144206
<div class="flex-1 min-w-[200px]">
145-
<label class="block text-sm font-medium mb-1">Topics:</label>
146-
<TagSelector
147-
v-model="selectedTopics"/>
207+
<label class="block text-sm font-medium mb-1">Other Tags</label>
208+
<TagSelector v-model="selectedGeneralTags" />
148209
</div>
149210

211+
<!-- Star-rating filters -->
150212
<div class="flex-1 min-w-[200px]">
151-
<label class="block text-sm font-medium mb-1">Programming Languages:</label>
152-
<TagSelector
153-
v-model="selectedProgrammingLanguages"/>
213+
<label class="block text-sm font-medium mb-1"
214+
>Min. Community Rating</label
215+
>
216+
<Rating
217+
v-model="selectedCommunityRating"
218+
:stars="4"
219+
cancel
220+
class="text-yellow-400"
221+
/>
154222
</div>
155223

156224
<div class="flex-1 min-w-[200px]">
157-
<label class="block text-sm font-medium mb-1">Other Tags:</label>
158-
<TagSelector
159-
v-model="selectedGeneralTags"/>
225+
<label class="block text-sm font-medium mb-1"
226+
>Min. Teaching Clarity</label
227+
>
228+
<Rating
229+
v-model="selectedTeachingClarity"
230+
:stars="4"
231+
cancel
232+
class="text-yellow-400"
233+
/>
160234
</div>
161235

162-
<!-- Search Button -->
163-
<div class="flex items-end">
236+
<div class="flex-1 min-w-[200px]">
237+
<label class="block text-sm font-medium mb-1"
238+
>Min. Engagement</label
239+
>
240+
<Rating
241+
v-model="selectedEngagement"
242+
:stars="4"
243+
cancel
244+
class="text-yellow-400"
245+
/>
246+
</div>
247+
248+
<div class="flex-1 min-w-[200px]">
249+
<label class="block text-sm font-medium mb-1"
250+
>Min. Practicality</label
251+
>
252+
<Rating
253+
v-model="selectedPracticality"
254+
:stars="4"
255+
cancel
256+
class="text-yellow-400"
257+
/>
258+
</div>
259+
260+
<div class="flex-1 min-w-[200px]">
261+
<label class="block text-sm font-medium mb-1"
262+
>Min. User Friendliness</label
263+
>
264+
<Rating
265+
v-model="selectedUserFriendliness"
266+
:stars="4"
267+
cancel
268+
class="text-yellow-400"
269+
/>
270+
</div>
271+
272+
<div class="flex-1 min-w-[200px]">
273+
<label class="block text-sm font-medium mb-1"
274+
>Min. Updates</label
275+
>
276+
<Rating
277+
v-model="selectedUpdates"
278+
:stars="4"
279+
cancel
280+
class="text-yellow-400"
281+
/>
282+
</div>
283+
284+
<!-- Action Buttons -->
285+
<div class="flex items-end gap-5">
286+
<Button
287+
label="Reset"
288+
icon="pi pi-refresh"
289+
type="button"
290+
class="p-button-secondary"
291+
@click="resetFilters"
292+
/>
164293
<Button label="Filter" icon="pi pi-search" type="submit" />
165294
</div>
166295
</div>

resources/js/Pages/Resources/Index.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ const newsItems = [
6161
<FilterBar></FilterBar>
6262

6363
<div class="flex gap-4">
64-
64+
6565
<!-- Resources Section -->
6666
<section
6767
class="w-3/4 bg-white dark:bg-gray-800 overflow-hidden shadow-xl sm:rounded-lg p-6"

0 commit comments

Comments
 (0)