RSM-5 Made Vacancies easily linkable to Teams

This commit is contained in:
Miguel Nogueira 2020-10-09 22:27:36 +01:00
parent 6cc99d2ebe
commit 077ead9612
8 changed files with 200 additions and 34 deletions

View File

@ -2,11 +2,14 @@
<module type="WEB_MODULE" version="4"> <module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager"> <component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/database/factories" isTestSource="false" packagePrefix="Database\Factories\" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" packagePrefix="Tests\" /> <sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" packagePrefix="Tests\" />
<sourceFolder url="file://$MODULE_DIR$/database/seeders" isTestSource="false" packagePrefix="Database\Seeders\" />
<sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/app" isTestSource="false" packagePrefix="App\" /> <sourceFolder url="file://$MODULE_DIR$/app" isTestSource="false" packagePrefix="App\" />
<excludeFolder url="file://$MODULE_DIR$/vendor/almasaeed2010/adminlte" /> <excludeFolder url="file://$MODULE_DIR$/vendor/almasaeed2010/adminlte" />
<excludeFolder url="file://$MODULE_DIR$/vendor/asm89/stack-cors" /> <excludeFolder url="file://$MODULE_DIR$/vendor/asm89/stack-cors" />
<excludeFolder url="file://$MODULE_DIR$/vendor/awssat/discord-notification-channel" />
<excludeFolder url="file://$MODULE_DIR$/vendor/barryvdh/laravel-debugbar" /> <excludeFolder url="file://$MODULE_DIR$/vendor/barryvdh/laravel-debugbar" />
<excludeFolder url="file://$MODULE_DIR$/vendor/brick/math" /> <excludeFolder url="file://$MODULE_DIR$/vendor/brick/math" />
<excludeFolder url="file://$MODULE_DIR$/vendor/clue/stream-filter" /> <excludeFolder url="file://$MODULE_DIR$/vendor/clue/stream-filter" />
@ -27,6 +30,7 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/filp/whoops" /> <excludeFolder url="file://$MODULE_DIR$/vendor/filp/whoops" />
<excludeFolder url="file://$MODULE_DIR$/vendor/fruitcake/laravel-cors" /> <excludeFolder url="file://$MODULE_DIR$/vendor/fruitcake/laravel-cors" />
<excludeFolder url="file://$MODULE_DIR$/vendor/fzaninotto/faker" /> <excludeFolder url="file://$MODULE_DIR$/vendor/fzaninotto/faker" />
<excludeFolder url="file://$MODULE_DIR$/vendor/graham-campbell/result-type" />
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/guzzle" /> <excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/guzzle" />
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/promises" /> <excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/promises" />
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/psr7" /> <excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/psr7" />
@ -67,6 +71,7 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/phpspec/prophecy" /> <excludeFolder url="file://$MODULE_DIR$/vendor/phpspec/prophecy" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-code-coverage" /> <excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-code-coverage" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-file-iterator" /> <excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-file-iterator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-invoker" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-text-template" /> <excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-text-template" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-timer" /> <excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-timer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-token-stream" /> <excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-token-stream" />
@ -83,12 +88,16 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/ramsey/collection" /> <excludeFolder url="file://$MODULE_DIR$/vendor/ramsey/collection" />
<excludeFolder url="file://$MODULE_DIR$/vendor/ramsey/uuid" /> <excludeFolder url="file://$MODULE_DIR$/vendor/ramsey/uuid" />
<excludeFolder url="file://$MODULE_DIR$/vendor/scrivo/highlight.php" /> <excludeFolder url="file://$MODULE_DIR$/vendor/scrivo/highlight.php" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/cli-parser" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/code-unit" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/code-unit-reverse-lookup" /> <excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/code-unit-reverse-lookup" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/comparator" /> <excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/comparator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/complexity" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/diff" /> <excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/diff" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/environment" /> <excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/environment" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/exporter" /> <excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/exporter" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/global-state" /> <excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/global-state" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/lines-of-code" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/object-enumerator" /> <excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/object-enumerator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/object-reflector" /> <excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/object-reflector" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/recursion-context" /> <excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/recursion-context" />
@ -107,6 +116,8 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/event-dispatcher" /> <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/event-dispatcher" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/event-dispatcher-contracts" /> <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/event-dispatcher-contracts" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/finder" /> <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/finder" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-client" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-client-contracts" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-foundation" /> <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-foundation" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-kernel" /> <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-kernel" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/mime" /> <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/mime" />

View File

@ -142,9 +142,18 @@
<path value="$PROJECT_DIR$/vendor/league/mime-type-detection" /> <path value="$PROJECT_DIR$/vendor/league/mime-type-detection" />
<path value="$PROJECT_DIR$/vendor/mcamara/laravel-localization" /> <path value="$PROJECT_DIR$/vendor/mcamara/laravel-localization" />
<path value="$PROJECT_DIR$/vendor/mpociot/teamwork" /> <path value="$PROJECT_DIR$/vendor/mpociot/teamwork" />
<path value="$PROJECT_DIR$/vendor/sebastian/code-unit" />
<path value="$PROJECT_DIR$/vendor/sebastian/cli-parser" />
<path value="$PROJECT_DIR$/vendor/sebastian/complexity" />
<path value="$PROJECT_DIR$/vendor/sebastian/lines-of-code" />
<path value="$PROJECT_DIR$/vendor/graham-campbell/result-type" />
<path value="$PROJECT_DIR$/vendor/symfony/http-client" />
<path value="$PROJECT_DIR$/vendor/symfony/http-client-contracts" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-invoker" />
<path value="$PROJECT_DIR$/vendor/awssat/discord-notification-channel" />
</include_path> </include_path>
</component> </component>
<component name="PhpProjectSharedConfiguration" php_language_level="7.2" /> <component name="PhpProjectSharedConfiguration" php_language_level="7.3" />
<component name="PhpUnit"> <component name="PhpUnit">
<phpunit_settings> <phpunit_settings>
<PhpUnitSettings configuration_file_path="$PROJECT_DIR$/phpunit.xml" custom_loader_path="$PROJECT_DIR$/vendor/autoload.php" use_configuration_file="true" /> <PhpUnitSettings configuration_file_path="$PROJECT_DIR$/phpunit.xml" custom_loader_path="$PROJECT_DIR$/vendor/autoload.php" use_configuration_file="true" />

View File

@ -16,6 +16,7 @@ use Mpociot\Teamwork\Exceptions\UserNotInTeamException;
use Mpociot\Teamwork\Facades\Teamwork; use Mpociot\Teamwork\Facades\Teamwork;
use Mpociot\Teamwork\TeamInvite; use Mpociot\Teamwork\TeamInvite;
class TeamController extends Controller class TeamController extends Controller
{ {
/** /**
@ -82,7 +83,7 @@ class TeamController extends Controller
return view('dashboard.teams.edit-team') return view('dashboard.teams.edit-team')
->with('team', $team) ->with('team', $team)
->with('users', User::all()) ->with('users', User::all())
->with('vacancies', Vacancy::all()); ->with('vacancies', Vacancy::with('teams')->get()->all());
} }
/** /**
@ -209,4 +210,53 @@ class TeamController extends Controller
return redirect()->back(); return redirect()->back();
} }
// Since it's a separate form, we shouldn't use the same update method
public function assignVacancies(Request $request, Team $team)
{
// P.S. To future developers
// This method gave me a lot of trouble lol. It's hard to write code when you're half asleep.
// There may be an n+1 query in the view and I don't think there's a way to avoid that without writing a lot of extra code.
$requestVacancies = $request->assocVacancies;
$currentVacancies = $team->vacancies->pluck('id')->all();
if (is_null($requestVacancies))
{
foreach ($team->vacancies as $vacancy)
{
$team->vacancies()->detach($vacancy->id);
}
$request->session()->flash('success', 'Removed all vacancy associations.');
return redirect()->back();
}
$vacancyDiff = array_diff($requestVacancies, $currentVacancies);
$deselectedDiff = array_diff($currentVacancies, $requestVacancies);
if (!empty($vacancyDiff) || !empty($deselectedDiff))
{
foreach ($vacancyDiff as $selectedVacancy)
{
$team->vacancies()->attach($selectedVacancy);
}
foreach ($deselectedDiff as $deselectedVacancy)
{
$team->vacancies()->detach($deselectedVacancy);
}
}
else
{
$team->vacancies()->attach($requestVacancies);
}
$request->session()->flash('success', 'Assignments changed successfully.');
return redirect()->back();
}
} }

View File

@ -80,4 +80,32 @@ class Vacancy extends Model
} }
/**
* Check if the Modal is attached to the $checkingTeam Model
*
* @param Team $checkingTeam The mdoel you want to check against
* @return boolean Whether the models are attached
*/
public function hasTeam(Team $checkingTeam): bool
{
$myTeams = $this->teams;
if (empty($myTeams))
{
// no associated teams
return false;
}
foreach($myTeams as $team)
{
if ($team->id === $checkingTeam->id)
{
return true;
}
}
return false;
}
} }

View File

@ -12,6 +12,7 @@
"ext-imagick": "*", "ext-imagick": "*",
"ext-json": "*", "ext-json": "*",
"arcanedev/log-viewer": "^8.0", "arcanedev/log-viewer": "^8.0",
"awssat/discord-notification-channel": "^1.4",
"doctrine/dbal": "^2.10", "doctrine/dbal": "^2.10",
"fideloper/proxy": "^4.2", "fideloper/proxy": "^4.2",
"fruitcake/laravel-cors": "^1.0", "fruitcake/laravel-cors": "^1.0",

57
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "db269f8f20690fd92cc9b51dbbca3fc5", "content-hash": "b91b3c69e28100abbffdb9bb256025fd",
"packages": [ "packages": [
{ {
"name": "almasaeed2010/adminlte", "name": "almasaeed2010/adminlte",
@ -225,6 +225,61 @@
], ],
"time": "2019-12-24T22:41:47+00:00" "time": "2019-12-24T22:41:47+00:00"
}, },
{
"name": "awssat/discord-notification-channel",
"version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/awssat/discord-notification-channel.git",
"reference": "514df4bb5db48f624658240d6b1f9b8ee468a46f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/awssat/discord-notification-channel/zipball/514df4bb5db48f624658240d6b1f9b8ee468a46f",
"reference": "514df4bb5db48f624658240d6b1f9b8ee468a46f",
"shasum": ""
},
"require": {
"guzzlehttp/guzzle": "^6.0|^7.0",
"illuminate/notifications": "~5.8.0|^6.0|^7.0|^8.0",
"laravel/slack-notification-channel": "^2.0",
"php": "^7.1.3"
},
"require-dev": {
"mockery/mockery": "^1.0",
"phpunit/phpunit": "^7.0|^8.0"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Awssat\\Notifications\\DiscordChannelServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Awssat\\Notifications\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Bader Almutairi",
"email": "phpfalcon@gmail.com"
}
],
"description": "Discord Notification Channel for laravel.",
"keywords": [
"discord",
"laravel",
"notifications"
],
"time": "2020-09-09T20:42:43+00:00"
},
{ {
"name": "bacon/bacon-qr-code", "name": "bacon/bacon-qr-code",
"version": "2.0.2", "version": "2.0.2",

View File

@ -177,13 +177,17 @@
<span class="text-muted"><i class="fas fa-info-circle"></i> The vacancies you select determine what applications your team members see. All applications under the vacancies you choose will be displayed.</span> <span class="text-muted"><i class="fas fa-info-circle"></i> The vacancies you select determine what applications your team members see. All applications under the vacancies you choose will be displayed.</span>
<form> <form method="POST" id="vacancyChangeForm" action="{{ route('assignVacancies', ['team' => $team->id]) }}">
<select id="assocVacancies" name="assocVacancies" multiple="multiple"> @csrf
@method('PATCH')
<select id="assocVacancies" name="assocVacancies[]" multiple="multiple">
@foreach($vacancies as $vacancy) @foreach($vacancies as $vacancy)
<option value="{{ $vacancy->id }}">{{ $vacancy->vacancyName }}</option> <!-- fixme: n+1 query here -->
<option {{ ($vacancy->hasTeam($team)) ? 'selected' : '' }} value="{{ $vacancy->id }}">{{ $vacancy->vacancyName }}</option>
@endforeach @endforeach
@ -193,6 +197,12 @@
</div> </div>
<div class="card-footer">
<button onclick="$('#vacancyChangeForm').submit()" type="button" class="btn btn-success"><i class="far fa-save"></i> Update Assignments</button>
</div>
</div> </div>
</div> </div>

View File

@ -61,21 +61,23 @@ Route::group(['prefix' => LaravelLocalization::setLocale(), 'middleware' => [ 'l
Route::resource('teams', TeamController::class);
Route::post('teams/{team}/invites/send', [TeamController::class, 'invite']) Route::post('teams/{team}/invites/send', [TeamController::class, 'invite'])
->name('sendInvite'); ->name('sendInvite');
Route::get('teams/{team}/switch', [TeamController::class, 'switchTeam']) Route::get('teams/{team}/switch', [TeamController::class, 'switchTeam'])
->name('switchTeam'); ->name('switchTeam');
Route::patch('teams/{team}/vacancies/update', [TeamController::class, 'assignVacancies'])
->name('assignVacancies');
Route::get('teams/invites/{action}/{token}', [TeamController::class, 'processInviteAction']) Route::get('teams/invites/{action}/{token}', [TeamController::class, 'processInviteAction'])
->name('processInvite'); ->name('processInvite');
// WARNING: This is a resource, might not work under laravel 8.
Route::resource('teams', TeamController::class);
Route::group(['prefix' => '/applications'], function (){ Route::group(['prefix' => '/applications'], function (){
@ -251,7 +253,7 @@ Route::group(['prefix' => LaravelLocalization::setLocale(), 'middleware' => [ 'l
Route::delete('forms/destroy/{form}', [FormController::class, 'destroy']) Route::delete('forms/destroy/{form}', [FormController::class, 'destroy'])
->name('destroyForm'); ->name('destroyForm');
Route::get('forms', [FormController::class, 'showFormBuilder']) Route::get('forms', [FormController::class, 'index'])
->name('showForms'); ->name('showForms');
Route::get('forms/preview/{form}', [FormController::class, 'preview']) Route::get('forms/preview/{form}', [FormController::class, 'preview'])