Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
159 commits
Select commit Hold shift + click to select a range
2bb042f
Added basic resume book home page, no searching
jvogt23 Feb 24, 2026
fdb2970
Attempt to fix issue causing unterminated string error
jvogt23 Feb 24, 2026
13c2dd0
Debug error in page load
jvogt23 Feb 27, 2026
602a12c
Remove references to users for debugging purposes
jvogt23 Feb 27, 2026
ad82342
Remove references to users for debugging purposes
jvogt23 Feb 27, 2026
25db39a
Remove references to users for debugging purposes
jvogt23 Feb 27, 2026
31a1d66
fix web.php
jvogt23 Feb 27, 2026
d523709
fix web.php
jvogt23 Feb 27, 2026
8bab0dd
remove unneeded extend
jvogt23 Feb 27, 2026
b5f4a71
use @json
jvogt23 Feb 27, 2026
4b7c369
AAAAAAA
jvogt23 Feb 27, 2026
289b2e1
Merge branch 'main' into resume-home
jvogt23 Feb 27, 2026
749c083
Changed how template receives users
jvogt23 Feb 27, 2026
305f9d7
AAAAAAA
jvogt23 Feb 27, 2026
6fb13ed
AAAAAAA
jvogt23 Feb 27, 2026
3295fe1
get all columns
jvogt23 Feb 27, 2026
1b97c5b
AAAAAAAAA
jvogt23 Feb 27, 2026
cbfbc61
Add filter for majors
jvogt23 Feb 28, 2026
f97369c
Add filter for majors
jvogt23 Feb 28, 2026
50bd83e
Add filter for majors
jvogt23 Feb 28, 2026
bbcc8d0
Add filter for majors
jvogt23 Feb 28, 2026
e597d3d
Add filter for majors
jvogt23 Feb 28, 2026
ff11e56
mockup
jvogt23 Feb 28, 2026
5aa09a9
Merge branch 'main' into resume-home
jvogt23 Feb 28, 2026
6625a97
Trying new UI layout
jvogt23 Mar 2, 2026
2a8d173
Fix new UI
jvogt23 Mar 3, 2026
9905cfa
Fix new UI
jvogt23 Mar 3, 2026
1e32298
Fix new UI
jvogt23 Mar 3, 2026
2e564ea
Fix new UI
jvogt23 Mar 3, 2026
c54edcf
Fix new UI
jvogt23 Mar 3, 2026
f7df670
Fix new UI
jvogt23 Mar 3, 2026
a83df49
Fix new UI
jvogt23 Mar 4, 2026
a8d2757
Merge branch 'main' into resume-home
jvogt23 Mar 4, 2026
73e55b8
Fix new UI
jvogt23 Mar 4, 2026
b08246d
Fix new UI
jvogt23 Mar 4, 2026
e67eb31
Fix new UI
jvogt23 Mar 4, 2026
713c2aa
Add filtering
jvogt23 Mar 6, 2026
b7aad33
use axios like a civilized person
jvogt23 Mar 6, 2026
5461795
add return statement
jvogt23 Mar 6, 2026
7c3e21f
Attempt to set filtering
jvogt23 Mar 6, 2026
b969fc9
Attempt to set filtering
jvogt23 Mar 6, 2026
169e70a
Attempt to set filtering
jvogt23 Mar 6, 2026
526c06a
Attempt to set filtering
jvogt23 Mar 6, 2026
ed99e7f
Attempt to set filtering
jvogt23 Mar 6, 2026
e6ee6e0
Attempt to fix eloquent error
jvogt23 Mar 7, 2026
0f397fb
Attempt to fix eloquent error
jvogt23 Mar 7, 2026
8cedfdd
Attempt to fix eloquent error
jvogt23 Mar 7, 2026
d1b38e5
Attempt to fix eloquent error
jvogt23 Mar 7, 2026
e865730
cleanup
jvogt23 Mar 7, 2026
2b3e2da
cleanup
jvogt23 Mar 7, 2026
c8c5a38
cleanup
jvogt23 Mar 7, 2026
b437b4b
cleanup
jvogt23 Mar 7, 2026
cd5aeb9
Linter appeasement
jvogt23 Mar 7, 2026
c7995ff
Linter appeasement
jvogt23 Mar 8, 2026
0904701
Linter appeasement
jvogt23 Mar 8, 2026
b7961b1
Linter appeasement
jvogt23 Mar 8, 2026
4036976
Linter appeasement
jvogt23 Mar 8, 2026
3591289
Linter appeasement
jvogt23 Mar 8, 2026
f5a3467
Merge branch 'main' into resume-home
jvogt23 Mar 8, 2026
4c7e0b9
Add comments and clean up code
jvogt23 Mar 8, 2026
bf88501
Add comments and clean up code
jvogt23 Mar 8, 2026
16dd754
Remove unused item
jvogt23 Mar 8, 2026
abd61d8
Update app/Http/Controllers/ResumeBookController.php
jvogt23 Mar 11, 2026
3eaa1c4
Update app/Http/Controllers/ResumeBookController.php
jvogt23 Mar 11, 2026
a169aa5
Update resources/js/components/sponsors/ResumeBookIndex.vue
jvogt23 Mar 11, 2026
581a897
Create joint middleware
jvogt23 Mar 11, 2026
f65f856
Merge branch 'resume-home' of github.com:Robojackets/apiary into resu…
jvogt23 Mar 11, 2026
df5c56d
Update app/Http/Controllers/ResumeBookController.php
jvogt23 Mar 11, 2026
0aaad31
Partially revert middleware change and add type hints
jvogt23 Mar 11, 2026
20abfbb
Merge branch 'resume-home' of github.com:Robojackets/apiary into resu…
jvogt23 Mar 11, 2026
8649427
Add redirectOnFail flag
jvogt23 Mar 11, 2026
01f50c8
fix middleware call
jvogt23 Mar 11, 2026
633771f
Attempt to fix auth middleware
jvogt23 Mar 11, 2026
9693f8b
Attempt to fix 401
jvogt23 Mar 13, 2026
f942be8
Attempt to fix 401
jvogt23 Mar 13, 2026
fa0c573
Remove auth guards from majors endpoint
jvogt23 Mar 13, 2026
6fa5fd8
Only pull active users
jvogt23 Mar 13, 2026
ac43b95
Only allow active members
jvogt23 Mar 13, 2026
e567a5b
Only allow active members
jvogt23 Mar 13, 2026
af1858d
Only allow active members
jvogt23 Mar 13, 2026
6a98e3c
add missing library
jvogt23 Mar 13, 2026
8d8ac38
check active but better
jvogt23 Mar 13, 2026
57cb49c
check active but better
jvogt23 Mar 13, 2026
8309162
Added FormRequest for resume search
jvogt23 Mar 13, 2026
531257e
linter appeasement
jvogt23 Mar 13, 2026
7b336e5
linter appeasement
jvogt23 Mar 13, 2026
3b515f4
linter appeasement
jvogt23 Mar 13, 2026
cffad44
Merge branch 'main' into resume-home
jvogt23 Mar 13, 2026
b6b56a9
Remove redundant web route
jvogt23 Mar 14, 2026
12ae85d
Linter appeasement
jvogt23 Mar 14, 2026
dd685eb
Merge branch 'main' into resume-home
jvogt23 Mar 14, 2026
752fb8b
Add flex-shrink-0 to footer
jvogt23 Mar 14, 2026
c8d4883
Merge branch 'resume-home' of github.com:Robojackets/apiary into resu…
jvogt23 Mar 14, 2026
2ab7243
Check for sentrysdk existence
jvogt23 Mar 14, 2026
430ef63
Merge branch 'main' into resume-home
kberzinch Mar 14, 2026
82a0cd6
Fixing based on review
jvogt23 Mar 15, 2026
0d39f53
Merge branch 'resume-home' of github.com:Robojackets/apiary into resu…
jvogt23 Mar 15, 2026
501035d
Return only needed items for user
jvogt23 Mar 15, 2026
82ab79b
Return only needed items for user
jvogt23 Mar 15, 2026
6a77d6b
Sorted grad semesters and updated representation
jvogt23 Mar 16, 2026
d5a655c
Sorted grad semesters and updated representation
jvogt23 Mar 16, 2026
2c706e5
Sorted grad semesters and updated representation
jvogt23 Mar 16, 2026
b379bf8
Change majors to filter on display name
jvogt23 Mar 18, 2026
dbf25c0
Change majors to filter on display name
jvogt23 Mar 18, 2026
97aebec
Located an issue with session collisions
jvogt23 Mar 18, 2026
36f4b85
Located an issue with session collisions
jvogt23 Mar 18, 2026
1e46a24
Located an issue with session collisions
jvogt23 Mar 18, 2026
ec645a4
Located an issue with session collisions
jvogt23 Mar 18, 2026
33b6a5b
Located an issue with session collisions
jvogt23 Mar 18, 2026
1048f26
Located an issue with session collisions
jvogt23 Mar 18, 2026
5f130e6
Located an issue with session collisions
jvogt23 Mar 18, 2026
9a94ab8
Located an issue with session collisions
jvogt23 Mar 18, 2026
5b7c333
Located an issue with session collisions
jvogt23 Mar 18, 2026
5f5d7b8
Located an issue with session collisions
jvogt23 Mar 18, 2026
7ecda39
Merge branch 'main' into resume-home
jvogt23 Mar 18, 2026
6f63914
Located an issue with session collisions
jvogt23 Mar 18, 2026
b11b597
Merge branch 'resume-home' of github.com:Robojackets/apiary into resu…
jvogt23 Mar 18, 2026
d1971f3
Update app/Http/Controllers/ResumeBookController.php
jvogt23 Mar 21, 2026
8b73a00
Update app/Http/Controllers/ResumeBookController.php
jvogt23 Mar 21, 2026
10899f6
Update app/Http/Controllers/ResumeBookController.php
jvogt23 Mar 21, 2026
778dfd4
Update app/Http/Controllers/ResumeBookController.php
jvogt23 Mar 21, 2026
fa18434
Update app/Http/Controllers/ResumeBookController.php
jvogt23 Mar 21, 2026
b29f916
Update app/Http/Controllers/ResumeBookController.php
jvogt23 Mar 21, 2026
0317457
Update app/Http/Controllers/ResumeBookController.php
jvogt23 Mar 21, 2026
5539b8e
Update app/Http/Controllers/ResumeBookController.php
jvogt23 Mar 21, 2026
04f856d
Code review
jvogt23 Mar 21, 2026
3e8ba89
Merge branch 'main' into resume-home
jvogt23 Mar 21, 2026
a5ed895
linter appeasement
jvogt23 Mar 21, 2026
f3d1438
Merge branch 'resume-home' of github.com:Robojackets/apiary into resu…
jvogt23 Mar 21, 2026
62d943b
linter appeasement
jvogt23 Mar 21, 2026
b5d6115
Fix unique major display names
jvogt23 Mar 28, 2026
1efa7c3
Fix toggleMajor
jvogt23 Mar 28, 2026
82eb37f
remove badmethodcallexception
jvogt23 Mar 28, 2026
1ae357b
Remove unused import
jvogt23 Mar 28, 2026
62380e7
Filter for majors that have active users with resumes
jvogt23 Mar 28, 2026
00cf584
Linter appeasement
jvogt23 Mar 28, 2026
8cb1a6e
Linter appeasement
jvogt23 Mar 28, 2026
3f92e33
Linter appeasement
jvogt23 Mar 28, 2026
fbae4a6
Linter appeasement
jvogt23 Mar 28, 2026
a07d503
only allow valid graduation semesters to show up in filters
jvogt23 Mar 28, 2026
27c8037
Only allow grad semesters for students with resumes in filters
jvogt23 Mar 28, 2026
c9a94f3
linter appeasement
jvogt23 Mar 28, 2026
e202158
linter appeasement
jvogt23 Mar 28, 2026
f6e2ecd
Extract getResumeMajors to utility function of Major model
jvogt23 Mar 29, 2026
c9fb002
undo mistake
jvogt23 Mar 29, 2026
db95bf9
linter appeasement
jvogt23 Mar 29, 2026
dfa1477
Add return type to utility function getResumeMajors
jvogt23 Mar 29, 2026
2af8df0
linter appeasement
jvogt23 Mar 29, 2026
d642ab0
linter appeasement
jvogt23 Mar 29, 2026
0f5bb6f
linter appeasement
jvogt23 Mar 29, 2026
5ac2614
linter appeasement
jvogt23 Mar 29, 2026
4d4bf89
Merge branch 'main' into resume-home
jvogt23 Apr 2, 2026
5055c40
Merge branch 'main' into resume-home
jvogt23 Apr 4, 2026
6051341
linter appeasement
jvogt23 Apr 4, 2026
45ea07b
Merge branch 'resume-home' of github.com:Robojackets/apiary into resu…
jvogt23 Apr 4, 2026
f46660d
linter appeasement
jvogt23 Apr 4, 2026
1633f27
Add back ensureclientisresourceowner middleware
jvogt23 Apr 9, 2026
9a02b53
Add back test for major controller API endpoint
jvogt23 Apr 9, 2026
88b0b50
Merge branch 'main' into resume-home
jvogt23 Apr 9, 2026
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/Http/ContentSecurityPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public function configure(): void
'*.googleusercontent.com',
'data: w3.org/svg/2000',
]);
$this->addDirective(Directive::OBJECT, Keyword::NONE);
$this->addDirective(Directive::OBJECT, Keyword::SELF);
$this->addDirective(Directive::WORKER, Keyword::NONE);
$this->addDirective(Directive::FRAME_ANCESTORS, Keyword::SELF);
$this->addDirective(Directive::FRAME, Keyword::SELF);
Expand Down
159 changes: 159 additions & 0 deletions app/Http/Controllers/ResumeBookController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use App\Http\Requests\ResumeSearchRequest;
use App\Models\Major;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;

class ResumeBookController
{
/**
* Displays resume book index page, which serves as the home page of the resume book.
*/
public function index()
{
return view('sponsors.home');
}

/**
* Shows a resume given a user ID.
* Will later be adapted to use Resume model when developed.
*
* @param $uid string representing username for user we are getting the resume from.
*/
public function show(string $uid)
{
try {
return response()
->file(Storage::disk('local')
->path('resumes/'.$uid.'.pdf'), ['Content-Type' => 'application/pdf']);
} catch (FileNotFoundException) {
return response()->json(
[
'status' => 'error',
'message' => 'The requested user has no resume.',
],
404
);
}
}

/**
* Searches for resumes matching a query defined in a POST request.
*
* @param $request Request object containing arrays of parameters to search.
*/
public function search(ResumeSearchRequest $request)
{
$validated = $request->validated();

$majors = $validated['majors'] ?? [];
$graduation_semesters = $validated['graduation_semesters'] ?? [];

$users = $this->filterUsers($majors, $graduation_semesters);

return response()->json([
'status' => 'success',
'users' => $users,
]);
}

/**
* Gets all graduation semesters for RoboJackets members.
*/
public function getGraduationSemesters()
{
$semesters = User::select('graduation_semester')
->active()
->whereNotNull('resume_date')
->where('primary_affiliation', 'student')
->where('is_service_account', '=', false)
->whereDoesntHave('duesPackages', static function (Builder $q): void {
$q->where('restricted_to_students', false);
})
->distinct()
->orderByDesc('graduation_semester')
->pluck('graduation_semester')
->filter(static fn (?string $code): bool => $code !== null &&
$code !== '' &&
strlen($code) === 6 &&
in_array(substr($code, 4, 2), ['02', '05', '08'], true))
->mapWithKeys(static fn (?string $code): array => [$code => self::formatGradSemester($code)]);

return response()->json([
'status' => 'success',
'graduation_semesters' => $semesters,
]);
}

public function getMajors()
{
$majors = Major::getResumeMajors();

return response()->json([
'status' => 'success',
'majors' => $majors,
]);
}

/** @psalm-pure */
private static function formatGradSemester(?string $code): array
{
if ($code === null || $code === '') {
return ['code' => null, 'name' => 'Unspecified'];
}
if (strlen($code) !== 6) {
return ['code' => $code, 'name' => $code];
}

$months = [
'02' => 'Spring',
'05' => 'Summer',
'08' => 'Fall',
];

$year = substr($code, 0, 4);
$month = substr($code, 4, 2);

return ['code' => $code, 'name' => ($months[$month] ?? $month).' '.$year];
}

private function filterUsers(array $majors, array $graduation_semesters): array
{
$usernames = collect(Storage::disk('local')->files('resumes'))
->map(static fn (string $path): string => pathinfo($path, PATHINFO_FILENAME))
->toArray();
$users = User::active()->whereIn('uid', $usernames);

if ($majors !== []) {
$users = $users->whereHas(
'majors',
static fn (Builder $query): Builder => $query->whereIn('majors.display_name', $majors)
);
}

if ($graduation_semesters !== []) {
$users = $users->whereIn('graduation_semester', $graduation_semesters);
}

$users = $users->with('majors')
->get(['id', 'uid', 'first_name', 'last_name', 'graduation_semester', 'gt_email'])
->map(static fn (User $user): array => array_merge($user->toArray(), [
'full_name' => $user->first_name.' '.$user->last_name,
'majors' => $user->majors->map(static fn (Major $major): array => [
'id' => $major->id,
'display_name' => $major->display_name ?? $major->gtad_majorgroup_name,
])->toArray(),
'graduation_semester' => self::formatGradSemester($user->graduation_semester),
]))
->toArray();

return $users;
}
}
7 changes: 7 additions & 0 deletions app/Http/Middleware/CasAuthenticate.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ class CasAuthenticate
*/
public function handle(Request $request, Closure $next): Response
{
// Remove existing sponsor session if exists to prevent session conflict
if (Auth::guard('sponsor')->check()) {
Auth::guard('sponsor')->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
}
// Check to ensure the request isn't already authenticated through the API guard
if (! Auth::guard('api')->check()) {
// Run the user update only if they don't have an active session
Expand Down Expand Up @@ -81,6 +87,7 @@ public function handle(Request $request, Closure $next): Response
if ($request->ajax() || $request->wantsJson()) {
return response('Unauthorized', 401);
}

Cas::authenticate();
}

Expand Down
8 changes: 7 additions & 1 deletion app/Http/Middleware/CasCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,15 @@ class CasCheck
*/
public function handle(Request $request, Closure $next): Response
{
// Remove existing sponsor session if exists to prevent session conflict
if (Auth::guard('sponsor')->check()) {
Auth::guard('sponsor')->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
}
Cas::checkAuthentication();
if ($request->user() === null) {
if (Cas::isAuthenticated()) {
if (Cas::isAuthenticated() && $request->user() === null) {
$user = CasUser::createOrUpdate();

Auth::login($user);
Expand Down
37 changes: 37 additions & 0 deletions app/Http/Requests/ResumeSearchRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class ResumeSearchRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @psalm-pure
*/
public function authorize(): true
{
return true;
}

/**
* Get the validation rules that apply to the request.
*
* @psalm-pure
*
* @return array<string,string|list<string>>
*/
public function rules(): array
{
return [
'majors' => 'nullable|array',
'majors.*' => 'sometimes|string|exists:majors,display_name',
'graduation_semesters' => 'nullable|array',
'graduation_semesters.*' => 'sometimes|integer|digits:6',
];
}
}
27 changes: 27 additions & 0 deletions app/Models/Major.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

namespace App\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Query\JoinClause;

/**
* Represents a single major (e.g. Computer Science).
Expand Down Expand Up @@ -73,4 +75,29 @@ public static function findOrCreateFromGtadGroup(string $gtad_group): self

return $major;
}

/**
* Returns a list of majors (display names only) for active users who have uploaded resumes.
*
* @return array<int, string>
*/
public static function getResumeMajors(): array
{
return User::selectRaw('distinct(majors.display_name) as distinct_display_names')
->active()
->whereNotNull('resume_date')
->where('primary_affiliation', 'student')
->where('is_service_account', '=', false)
->whereDoesntHave('duesPackages', static function (Builder $q): void {
$q->where('restricted_to_students', false);
})
->leftJoin('major_user', static function (JoinClause $join): void {
$join->on('users.id', '=', 'major_user.user_id')
->whereNull('major_user.deleted_at');
})
->leftJoin('majors', 'major_user.major_id', '=', 'majors.id')
->orderBy('distinct_display_names')
->pluck('distinct_display_names')
->toArray();
}
}
9 changes: 9 additions & 0 deletions app/Models/SponsorUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,13 @@ public function getShouldReceiveEmailAttribute(): bool
{
return $this->email_suppression_reason === null;
}

/** @psalm-pure */
#[\Override]
public function getRememberTokenName(): string
{
// Needs to return empty string instead of BadMethodCallException.
// Fails to invalidate session when switching to regular user otherwise.
return '';
}
}
29 changes: 5 additions & 24 deletions app/Nova/Actions/ExportFilteredResumes.php
Original file line number Diff line number Diff line change
Expand Up @@ -196,30 +196,11 @@ public function fields(NovaRequest $request): array
$majors = Cache::remember(
'majors_with_resumes',
30,
static fn (): array => User::selectRaw('distinct(majors.display_name) as distinct_display_names')
->active()
->whereNotNull('resume_date')
->where('primary_affiliation', 'student')
->where('is_service_account', '=', false)
->whereDoesntHave('duesPackages', static function (Builder $q): void {
$q->where('restricted_to_students', false);
})
->leftJoin('major_user', static function (JoinClause $join): void {
$join->on('users.id', '=', 'major_user.user_id')
->whereNull('major_user.deleted_at');
})
->leftJoin(
'majors',
'major_user.major_id',
'=',
'majors.id'
)
->orderBy('distinct_display_names')
->pluck('distinct_display_names')
->mapWithKeys(
static fn (?string $name): array => $name === null ? [] : [$name => $name]
)
->toArray()
static function (): array {
$majors = Major::getResumeMajors();

return array_combine($majors, $majors);
}
);

$classStandings = Cache::remember('class_standings_with_resumes', 30, static fn (): array => User::selectRaw(
Expand Down
1 change: 1 addition & 0 deletions resources/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ Vue.component('loading-spinner', require('./components/LoadingSpinner').default)

// Sponsors
Vue.component('sponsor-login', require('./components/sponsors/SponsorLogin.vue').default);
Vue.component('resume-book-index', require('./components/sponsors/ResumeBookIndex.vue').default);

const app = new Vue({
el: '#app',
Expand Down
Loading
Loading