#23 - wip
This commit is contained in:
parent
a62a428781
commit
5a51f24342
51
app/Helpers/YearPeriodRetriever.php
Normal file
51
app/Helpers/YearPeriodRetriever.php
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Helpers;
|
||||||
|
|
||||||
|
use Toby\Models\YearPeriod;
|
||||||
|
|
||||||
|
class YearPeriodRetriever
|
||||||
|
{
|
||||||
|
public const SESSION_KEY = "selected_year_period";
|
||||||
|
|
||||||
|
public function selected(): YearPeriod
|
||||||
|
{
|
||||||
|
/** @var YearPeriod $yearPeriod */
|
||||||
|
$yearPeriod = YearPeriod::query()->find(session()->get(static::SESSION_KEY));
|
||||||
|
|
||||||
|
return $yearPeriod !== null ? $yearPeriod : $this->current();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function current(): YearPeriod
|
||||||
|
{
|
||||||
|
return YearPeriod::current();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function links(): array
|
||||||
|
{
|
||||||
|
$current = $this->selected();
|
||||||
|
|
||||||
|
$years = YearPeriod::query()->whereIn("year", $this->offset($current->year))->get();
|
||||||
|
$navigation = $years->map(fn(YearPeriod $yearPeriod) => $this->toNavigation($yearPeriod));
|
||||||
|
|
||||||
|
return [
|
||||||
|
"current" => $current->year,
|
||||||
|
"navigation" => $navigation->toArray(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function offset(int $year): array
|
||||||
|
{
|
||||||
|
return range($year - 2, $year + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function toNavigation(YearPeriod $yearPeriod): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
"year" => $yearPeriod->year,
|
||||||
|
"link" => route("year-periods.select", $yearPeriod->id),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -1,40 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Http\Controllers;
|
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Carbon;
|
|
||||||
use Inertia\Response;
|
|
||||||
use Toby\Http\Resources\VacationLimitResource;
|
|
||||||
use Toby\Http\Resources\YearPeriodResource;
|
|
||||||
use Toby\Models\YearPeriod;
|
|
||||||
|
|
||||||
class VacationDaysController extends Controller
|
|
||||||
{
|
|
||||||
public function edit(Request $request): Response
|
|
||||||
{
|
|
||||||
$year = $request->query("year", Carbon::now()->year);
|
|
||||||
|
|
||||||
/** @var YearPeriod $yearPeriod */
|
|
||||||
$yearPeriod = YearPeriod::query()->where("year", $year)->firstOrFail();
|
|
||||||
$previousYearPeriod = YearPeriod::query()->where("year", $year - 1)->first();
|
|
||||||
$nextYearPeriod = YearPeriod::query()->where("year", $year + 1)->first();
|
|
||||||
|
|
||||||
|
|
||||||
return inertia("VacationDays", [
|
|
||||||
"vacationLimits" => VacationLimitResource::collection($yearPeriod->vacationLimits()->with("user")->get()),
|
|
||||||
"yearPeriods" => [
|
|
||||||
"prev" => $previousYearPeriod ? new YearPeriodResource($previousYearPeriod) : null,
|
|
||||||
"current" => new YearPeriodResource($yearPeriod),
|
|
||||||
"next" => $nextYearPeriod ? new YearPeriodResource($nextYearPeriod) : null,
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function update(Request $request)
|
|
||||||
{
|
|
||||||
dump($request->get("items"));
|
|
||||||
}
|
|
||||||
}
|
|
35
app/Http/Controllers/VacationLimitController.php
Normal file
35
app/Http/Controllers/VacationLimitController.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Inertia\Response;
|
||||||
|
use Toby\Http\Requests\VacationLimitRequest;
|
||||||
|
use Toby\Http\Resources\VacationLimitResource;
|
||||||
|
use Toby\Models\VacationLimit;
|
||||||
|
|
||||||
|
class VacationLimitController extends Controller
|
||||||
|
{
|
||||||
|
public function edit(): Response
|
||||||
|
{
|
||||||
|
return inertia("VacationLimits", [
|
||||||
|
"limits" => VacationLimitResource::collection(VacationLimit::query()->with("user")->get()),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(VacationLimitRequest $request): RedirectResponse
|
||||||
|
{
|
||||||
|
foreach ($request->data() as $data) {
|
||||||
|
$limit = VacationLimit::query()->find($data["id"]);
|
||||||
|
|
||||||
|
$limit->update(Arr::only($data, ["has_vacation", "days"]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with("success", __("Vacation limits have been updated"));
|
||||||
|
}
|
||||||
|
}
|
@ -6,10 +6,15 @@ namespace Toby\Http\Middleware;
|
|||||||
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Inertia\Middleware;
|
use Inertia\Middleware;
|
||||||
|
use Toby\Helpers\YearPeriodRetriever;
|
||||||
use Toby\Http\Resources\UserResource;
|
use Toby\Http\Resources\UserResource;
|
||||||
|
|
||||||
class HandleInertiaRequests extends Middleware
|
class HandleInertiaRequests extends Middleware
|
||||||
{
|
{
|
||||||
|
public function __construct(protected YearPeriodRetriever $yearPeriodRetriever)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public function share(Request $request): array
|
public function share(Request $request): array
|
||||||
{
|
{
|
||||||
$user = $request->user();
|
$user = $request->user();
|
||||||
@ -22,6 +27,7 @@ class HandleInertiaRequests extends Middleware
|
|||||||
"success" => $request->session()->get("success"),
|
"success" => $request->session()->get("success"),
|
||||||
"error" => $request->session()->get("error"),
|
"error" => $request->session()->get("error"),
|
||||||
],
|
],
|
||||||
|
"years" => fn() => $user ? $this->yearPeriodRetriever->links() : [],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
30
app/Http/Requests/VacationLimitRequest.php
Normal file
30
app/Http/Requests/VacationLimitRequest.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
class VacationLimitRequest extends FormRequest
|
||||||
|
{
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
"items" => ["required", "array"],
|
||||||
|
"items.*.id" => ["required", "exists:vacation_limits,id"],
|
||||||
|
"items.*.hasVacation" => ["required", "boolean"],
|
||||||
|
"items.*.days" => ["exclude_if:items.*.hasVacation,false", "required", "integer", "min:0"],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function data(): Collection
|
||||||
|
{
|
||||||
|
return $this->collect("items")->map(fn(array $item): array => [
|
||||||
|
"id" => $item["id"],
|
||||||
|
"has_vacation" => $item["hasVacation"],
|
||||||
|
"days" => $item["days"],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -8,11 +8,12 @@ use Illuminate\Http\Resources\Json\JsonResource;
|
|||||||
|
|
||||||
class VacationLimitResource extends JsonResource
|
class VacationLimitResource extends JsonResource
|
||||||
{
|
{
|
||||||
public static $wrap = null;
|
public static $wrap = false;
|
||||||
|
|
||||||
public function toArray($request): array
|
public function toArray($request): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
"id" => $this->id,
|
||||||
"user" => new UserResource($this->user),
|
"user" => new UserResource($this->user),
|
||||||
"hasVacation" => $this->has_vacation,
|
"hasVacation" => $this->has_vacation,
|
||||||
"days" => $this->days,
|
"days" => $this->days,
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Toby\Http\Resources;
|
|
||||||
|
|
||||||
use Illuminate\Http\Resources\Json\JsonResource;
|
|
||||||
|
|
||||||
class YearPeriodResource extends JsonResource
|
|
||||||
{
|
|
||||||
public static $wrap = false;
|
|
||||||
|
|
||||||
public function toArray($request): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
"id" => $this->id,
|
|
||||||
"year" => $this->year,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -19,6 +19,8 @@ class VacationLimit extends Model
|
|||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $guarded = [];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
"has_vacation" => "boolean",
|
"has_vacation" => "boolean",
|
||||||
];
|
];
|
||||||
|
@ -7,7 +7,9 @@ namespace Toby\Providers;
|
|||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use Toby\Models\User;
|
use Toby\Models\User;
|
||||||
|
use Toby\Models\VacationLimit;
|
||||||
use Toby\Observers\UserObserver;
|
use Toby\Observers\UserObserver;
|
||||||
|
use Toby\Scopes\SelectedYearPeriodScope;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
@ -16,5 +18,7 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
User::observe(UserObserver::class);
|
User::observe(UserObserver::class);
|
||||||
|
|
||||||
Carbon::macro("toDisplayString", fn() => $this->translatedFormat("j F Y"));
|
Carbon::macro("toDisplayString", fn() => $this->translatedFormat("j F Y"));
|
||||||
|
|
||||||
|
VacationLimit::addGlobalScope($this->app->make(SelectedYearPeriodScope::class));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
22
app/Scopes/SelectedYearPeriodScope.php
Normal file
22
app/Scopes/SelectedYearPeriodScope.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Toby\Scopes;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Scope;
|
||||||
|
use Toby\Helpers\YearPeriodRetriever;
|
||||||
|
|
||||||
|
class SelectedYearPeriodScope implements Scope
|
||||||
|
{
|
||||||
|
public function __construct(protected YearPeriodRetriever $yearPeriodRetriever)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function apply(Builder $builder, Model $model): Builder
|
||||||
|
{
|
||||||
|
return $builder->where("year_period_id", $this->yearPeriodRetriever->selected()->id);
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,7 @@ class DatabaseSeeder extends Seeder
|
|||||||
{
|
{
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
User::factory(35)->create();
|
User::factory(14)->create();
|
||||||
User::factory([
|
User::factory([
|
||||||
"email" => env("LOCAL_EMAIL_FOR_LOGIN_VIA_GOOGLE"),
|
"email" => env("LOCAL_EMAIL_FOR_LOGIN_VIA_GOOGLE"),
|
||||||
])->create();
|
])->create();
|
||||||
|
2129
package-lock.json
generated
2129
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -32,6 +32,8 @@
|
|||||||
"vue-loader": "^17.0.0"
|
"vue-loader": "^17.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"browser-sync": "^2.27.7",
|
||||||
|
"browser-sync-webpack-plugin": "^2.3.0",
|
||||||
"eslint": "^8.6.0",
|
"eslint": "^8.6.0",
|
||||||
"eslint-plugin-vue": "^8.2.0"
|
"eslint-plugin-vue": "^8.2.0"
|
||||||
}
|
}
|
||||||
|
@ -10,45 +10,6 @@
|
|||||||
Zarządzaj dostepnymi dniami urlopów dla użytkowników.
|
Zarządzaj dostepnymi dniami urlopów dla użytkowników.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-4">
|
|
||||||
<span class="relative z-0 inline-flex shadow-sm rounded-md">
|
|
||||||
<InertiaLink
|
|
||||||
v-if="yearPeriods.prev"
|
|
||||||
:preserve-scroll="true"
|
|
||||||
replace
|
|
||||||
href="/vacation-days"
|
|
||||||
:data="{year: yearPeriods.prev.year}"
|
|
||||||
class="relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50 focus:z-10 focus:outline-none focus:ring-1 focus:ring-blumilk-500 focus:border-blumilk-500"
|
|
||||||
>
|
|
||||||
<ChevronLeftIcon class="h-5 w-5" />
|
|
||||||
</InertiaLink>
|
|
||||||
<span
|
|
||||||
v-else
|
|
||||||
class="relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500"
|
|
||||||
>
|
|
||||||
<ChevronLeftIcon class="h-5 w-5" />
|
|
||||||
</span>
|
|
||||||
<span class="-ml-px relative inline-flex items-center px-2 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-500">
|
|
||||||
{{ yearPeriods.current.year }}
|
|
||||||
</span>
|
|
||||||
<InertiaLink
|
|
||||||
v-if="yearPeriods.next"
|
|
||||||
:preserve-scroll="true"
|
|
||||||
href="/vacation-days"
|
|
||||||
replace
|
|
||||||
:data="{year: yearPeriods.next.year}"
|
|
||||||
class="-ml-px relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50 focus:z-10 focus:outline-none focus:ring-1 focus:ring-blumilk-500 focus:border-blumilk-500"
|
|
||||||
>
|
|
||||||
<ChevronRightIcon class="h-5 w-5" />
|
|
||||||
</InertiaLink>
|
|
||||||
<span
|
|
||||||
v-else
|
|
||||||
class="-ml-px relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500"
|
|
||||||
>
|
|
||||||
<ChevronRightIcon class="h-5 w-5" />
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="border-t border-gray-200">
|
<div class="border-t border-gray-200">
|
||||||
<div class="overflow-x-auto xl:overflow-x-visible overflow-y-auto xl:overflow-y-visible">
|
<div class="overflow-x-auto xl:overflow-x-visible overflow-y-auto xl:overflow-y-visible">
|
||||||
@ -84,7 +45,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody class="bg-white divide-y divide-gray-100">
|
<tbody class="bg-white divide-y divide-gray-100">
|
||||||
<tr
|
<tr
|
||||||
v-for="item in form.items"
|
v-for="(item, index) in form.items"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
class="hover:bg-blumilk-25"
|
class="hover:bg-blumilk-25"
|
||||||
>
|
>
|
||||||
@ -128,14 +89,15 @@
|
|||||||
v-model="item.days"
|
v-model="item.days"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
class="block w-full shadow-sm rounded-md sm:text-sm"
|
class="block w-full shadow-sm rounded-md sm:text-sm disabled:bg-slate-50 disabled:text-slate-500 disabled:border-slate-200 disabled:shadow-none disabled:cursor-not-allowed"
|
||||||
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': false, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': true }"
|
:disabled="!item.hasVacation"
|
||||||
|
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors[`items.${index}.days`], 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors[`items.${index}.days`] }"
|
||||||
>
|
>
|
||||||
<p
|
<p
|
||||||
v-if="false"
|
v-if="form.errors[`items.${index}.days`]"
|
||||||
class="mt-2 text-sm text-red-600"
|
class="mt-2 text-sm text-red-600"
|
||||||
>
|
>
|
||||||
{{ form.errors.name }}
|
{{ form.errors[`items.${index}.days`] }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@ -153,13 +115,6 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="flex justify-end py-3 px-4">
|
<div class="flex justify-end py-3 px-4">
|
||||||
<div class="space-x-3">
|
|
||||||
<InertiaLink
|
|
||||||
href="/users"
|
|
||||||
class="bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500"
|
|
||||||
>
|
|
||||||
Anuluj
|
|
||||||
</InertiaLink>
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blumilk-600 hover:bg-blumilk-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500"
|
class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blumilk-600 hover:bg-blumilk-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500"
|
||||||
@ -167,7 +122,6 @@
|
|||||||
Zapisz
|
Zapisz
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -176,29 +130,26 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {Switch} from '@headlessui/vue';
|
import {Switch} from '@headlessui/vue';
|
||||||
import {ChevronLeftIcon, ChevronRightIcon} from '@heroicons/vue/solid';
|
|
||||||
import {useForm} from '@inertiajs/inertia-vue3';
|
import {useForm} from '@inertiajs/inertia-vue3';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'VacationDays',
|
name: 'VacationLimits',
|
||||||
components: {
|
components: {
|
||||||
Switch,
|
Switch,
|
||||||
ChevronLeftIcon,
|
|
||||||
ChevronRightIcon,
|
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
vacationLimits: {
|
limits: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => null,
|
default: () => null,
|
||||||
},
|
},
|
||||||
yearPeriods: {
|
years: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => null,
|
default: () => null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
items: props.vacationLimits.data,
|
items: props.limits.data,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -209,9 +160,16 @@ export default {
|
|||||||
submitVacationDays() {
|
submitVacationDays() {
|
||||||
this.form
|
this.form
|
||||||
.transform(data => ({
|
.transform(data => ({
|
||||||
data,
|
items: data.items.map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
hasVacation: item.hasVacation,
|
||||||
|
days: item.hasVacation ? item.days : null,
|
||||||
|
})),
|
||||||
}))
|
}))
|
||||||
.put('/vacation-days');
|
.put('/vacation-days', {
|
||||||
|
preserveState: (page) => Object.keys(page.props.errors).length,
|
||||||
|
preserveScroll: true,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
@ -19,6 +19,53 @@
|
|||||||
|
|
||||||
<!-- Right section on desktop -->
|
<!-- Right section on desktop -->
|
||||||
<div class="hidden lg:ml-4 lg:flex lg:items-center lg:py-5 lg:pr-0.5">
|
<div class="hidden lg:ml-4 lg:flex lg:items-center lg:py-5 lg:pr-0.5">
|
||||||
|
<div class="mr-4">
|
||||||
|
<Menu
|
||||||
|
as="div"
|
||||||
|
class="relative inline-block text-left"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<MenuButton class="inline-flex justify-center w-full rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-1 focus:ring-gray-300">
|
||||||
|
{{ years.current }}
|
||||||
|
<ChevronDownIcon class="-mr-1 ml-2 h-5 w-5" />
|
||||||
|
</MenuButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<transition
|
||||||
|
enter-active-class="transition ease-out duration-100"
|
||||||
|
enter-from-class="transform opacity-0 scale-95"
|
||||||
|
enter-to-class="transform opacity-100 scale-100"
|
||||||
|
leave-active-class="transition ease-in duration-75"
|
||||||
|
leave-from-class="transform opacity-100 scale-100"
|
||||||
|
leave-to-class="transform opacity-0 scale-95"
|
||||||
|
>
|
||||||
|
<MenuItems class="origin-top-right absolute right-0 mt-2 w-32 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none">
|
||||||
|
<div class="py-1">
|
||||||
|
<MenuItem
|
||||||
|
v-for="(item, index) in years.navigation"
|
||||||
|
:key="index"
|
||||||
|
v-slot="{ active }"
|
||||||
|
>
|
||||||
|
<InertiaLink
|
||||||
|
:href="item.link"
|
||||||
|
as="button"
|
||||||
|
method="post"
|
||||||
|
:preserve-state="false"
|
||||||
|
:class="[active ? 'bg-gray-100 text-gray-900' : 'text-gray-700', 'flex w-full px-4 py-2 text-sm']"
|
||||||
|
>
|
||||||
|
{{ item.year }}
|
||||||
|
<CheckIcon
|
||||||
|
v-if="item.year === years.current"
|
||||||
|
class="h-5 w-5 text-blumilk-500 ml-2"
|
||||||
|
/>
|
||||||
|
</InertiaLink>
|
||||||
|
</MenuItem>
|
||||||
|
</div>
|
||||||
|
</MenuItems>
|
||||||
|
</transition>
|
||||||
|
</Menu>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="flex-shrink-0 p-1 text-cyan-200 rounded-full hover:text-white hover:bg-white hover:bg-opacity-10 focus:outline-none focus:ring-2 focus:ring-white"
|
class="flex-shrink-0 p-1 text-cyan-200 rounded-full hover:text-white hover:bg-white hover:bg-opacity-10 focus:outline-none focus:ring-2 focus:ring-white"
|
||||||
@ -246,6 +293,7 @@ import {
|
|||||||
import {BellIcon, MenuIcon, XIcon} from '@heroicons/vue/outline';
|
import {BellIcon, MenuIcon, XIcon} from '@heroicons/vue/outline';
|
||||||
import {computed} from 'vue';
|
import {computed} from 'vue';
|
||||||
import {usePage} from '@inertiajs/inertia-vue3';
|
import {usePage} from '@inertiajs/inertia-vue3';
|
||||||
|
import {ChevronDownIcon, CheckIcon} from '@heroicons/vue/solid';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'MainMenu',
|
name: 'MainMenu',
|
||||||
@ -263,9 +311,12 @@ export default {
|
|||||||
BellIcon,
|
BellIcon,
|
||||||
MenuIcon,
|
MenuIcon,
|
||||||
XIcon,
|
XIcon,
|
||||||
|
ChevronDownIcon,
|
||||||
|
CheckIcon,
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const user = computed(() => usePage().props.value.auth.user);
|
const user = computed(() => usePage().props.value.auth.user);
|
||||||
|
const years = computed(() => usePage().props.value.years);
|
||||||
|
|
||||||
const navigation = [
|
const navigation = [
|
||||||
{name: 'Strona główna', href: '/', current: true},
|
{name: 'Strona główna', href: '/', current: true},
|
||||||
@ -282,6 +333,7 @@ export default {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
user,
|
user,
|
||||||
|
years,
|
||||||
navigation,
|
navigation,
|
||||||
userNavigation,
|
userNavigation,
|
||||||
};
|
};
|
||||||
|
@ -2,11 +2,14 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
use Toby\Helpers\YearPeriodRetriever;
|
||||||
use Toby\Http\Controllers\GoogleController;
|
use Toby\Http\Controllers\GoogleController;
|
||||||
use Toby\Http\Controllers\LogoutController;
|
use Toby\Http\Controllers\LogoutController;
|
||||||
use Toby\Http\Controllers\UserController;
|
use Toby\Http\Controllers\UserController;
|
||||||
use Toby\Http\Controllers\VacationDaysController;
|
use Toby\Http\Controllers\VacationLimitController;
|
||||||
|
use Toby\Models\YearPeriod;
|
||||||
|
|
||||||
Route::middleware("auth")->group(function (): void {
|
Route::middleware("auth")->group(function (): void {
|
||||||
Route::get("/", fn() => inertia("Dashboard"))->name("dashboard");
|
Route::get("/", fn() => inertia("Dashboard"))->name("dashboard");
|
||||||
@ -15,8 +18,14 @@ Route::middleware("auth")->group(function (): void {
|
|||||||
Route::resource("users", UserController::class);
|
Route::resource("users", UserController::class);
|
||||||
Route::post("users/{user}/restore", [UserController::class, "restore"])->withTrashed();
|
Route::post("users/{user}/restore", [UserController::class, "restore"])->withTrashed();
|
||||||
|
|
||||||
Route::get("/vacation-days", [VacationDaysController::class, "edit"])->name("vacation.days");
|
Route::get("/vacation-days", [VacationLimitController::class, "edit"])->name("vacation.days");
|
||||||
Route::put("/vacation-days", [VacationDaysController::class, "update"]);
|
Route::put("/vacation-days", [VacationLimitController::class, "update"]);
|
||||||
|
|
||||||
|
Route::post("year-periods/{yearPeriod}/select", function (Request $request, YearPeriod $yearPeriod) {
|
||||||
|
$request->session()->put(YearPeriodRetriever::SESSION_KEY, $yearPeriod->id);
|
||||||
|
|
||||||
|
return redirect()->back();
|
||||||
|
})->name("year-periods.select");
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::middleware("guest")->group(function (): void {
|
Route::middleware("guest")->group(function (): void {
|
||||||
|
@ -5,10 +5,10 @@ declare(strict_types=1);
|
|||||||
namespace Tests\Feature;
|
namespace Tests\Feature;
|
||||||
|
|
||||||
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||||
use Tests\TestCase;
|
use Tests\FeatureTestCase;
|
||||||
use Toby\Models\User;
|
use Toby\Models\User;
|
||||||
|
|
||||||
class AuthenticationTest extends TestCase
|
class AuthenticationTest extends FeatureTestCase
|
||||||
{
|
{
|
||||||
use DatabaseMigrations;
|
use DatabaseMigrations;
|
||||||
|
|
||||||
|
@ -6,10 +6,10 @@ namespace Tests\Feature;
|
|||||||
|
|
||||||
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||||
use Inertia\Testing\AssertableInertia as Assert;
|
use Inertia\Testing\AssertableInertia as Assert;
|
||||||
use Tests\TestCase;
|
use Tests\FeatureTestCase;
|
||||||
use Toby\Models\User;
|
use Toby\Models\User;
|
||||||
|
|
||||||
class InertiaTest extends TestCase
|
class InertiaTest extends FeatureTestCase
|
||||||
{
|
{
|
||||||
use DatabaseMigrations;
|
use DatabaseMigrations;
|
||||||
|
|
||||||
|
@ -8,11 +8,11 @@ use Illuminate\Foundation\Testing\DatabaseMigrations;
|
|||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Inertia\Testing\AssertableInertia as Assert;
|
use Inertia\Testing\AssertableInertia as Assert;
|
||||||
use Tests\TestCase;
|
use Tests\FeatureTestCase;
|
||||||
use Toby\Enums\EmploymentForm;
|
use Toby\Enums\EmploymentForm;
|
||||||
use Toby\Models\User;
|
use Toby\Models\User;
|
||||||
|
|
||||||
class UserTest extends TestCase
|
class UserTest extends FeatureTestCase
|
||||||
{
|
{
|
||||||
use DatabaseMigrations;
|
use DatabaseMigrations;
|
||||||
|
|
||||||
|
21
tests/FeatureTestCase.php
Normal file
21
tests/FeatureTestCase.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Tests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
||||||
|
use Tests\Traits\InteractsWithYearPeriods;
|
||||||
|
|
||||||
|
abstract class FeatureTestCase extends BaseTestCase
|
||||||
|
{
|
||||||
|
use CreatesApplication;
|
||||||
|
use InteractsWithYearPeriods;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->createCurrentYearPeriod();
|
||||||
|
}
|
||||||
|
}
|
38
tests/Traits/InteractsWithYearPeriods.php
Normal file
38
tests/Traits/InteractsWithYearPeriods.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Tests\Traits;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Testing\Concerns\InteractsWithSession;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
use Toby\Helpers\YearPeriodRetriever;
|
||||||
|
use Toby\Models\YearPeriod;
|
||||||
|
|
||||||
|
trait InteractsWithYearPeriods
|
||||||
|
{
|
||||||
|
use InteractsWithSession;
|
||||||
|
|
||||||
|
public function createYearPeriod(int $year): YearPeriod
|
||||||
|
{
|
||||||
|
/** @var YearPeriod $yearPeriod */
|
||||||
|
$yearPeriod = YearPeriod::factory()->create(["year" => $year]);
|
||||||
|
|
||||||
|
return $yearPeriod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createCurrentYearPeriod(): YearPeriod
|
||||||
|
{
|
||||||
|
return $this->createYearPeriod(Carbon::now()->year);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function markYearPeriodAsSelected(YearPeriod $yearPeriod): void
|
||||||
|
{
|
||||||
|
$this->session([YearPeriodRetriever::SESSION_KEY => $yearPeriod->id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clearSelectedYearPeriod(): void
|
||||||
|
{
|
||||||
|
$this->session([]);
|
||||||
|
}
|
||||||
|
}
|
@ -7,12 +7,14 @@ namespace Tests\Unit;
|
|||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
use Tests\Traits\InteractsWithYearPeriods;
|
||||||
use Toby\Jobs\CheckYearPeriod;
|
use Toby\Jobs\CheckYearPeriod;
|
||||||
use Toby\Models\YearPeriod;
|
use Toby\Models\YearPeriod;
|
||||||
|
|
||||||
class CheckYearPeriodTest extends TestCase
|
class CheckYearPeriodTest extends TestCase
|
||||||
{
|
{
|
||||||
use RefreshDatabase;
|
use RefreshDatabase;
|
||||||
|
use InteractsWithYearPeriods;
|
||||||
|
|
||||||
public function testYearPeriodsAreCreatedWhenDontExist(): void
|
public function testYearPeriodsAreCreatedWhenDontExist(): void
|
||||||
{
|
{
|
||||||
@ -54,9 +56,7 @@ class CheckYearPeriodTest extends TestCase
|
|||||||
$now = Carbon::now();
|
$now = Carbon::now();
|
||||||
Carbon::setTestNow($now);
|
Carbon::setTestNow($now);
|
||||||
|
|
||||||
YearPeriod::factory([
|
$this->createCurrentYearPeriod();
|
||||||
"year" => $now->year,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertDatabaseMissing("year_periods", [
|
$this->assertDatabaseMissing("year_periods", [
|
||||||
"year" => $now->year + 1,
|
"year" => $now->year + 1,
|
||||||
@ -74,12 +74,8 @@ class CheckYearPeriodTest extends TestCase
|
|||||||
$now = Carbon::now();
|
$now = Carbon::now();
|
||||||
Carbon::setTestNow($now);
|
Carbon::setTestNow($now);
|
||||||
|
|
||||||
YearPeriod::factory([
|
$this->createCurrentYearPeriod();
|
||||||
"year" => $now->year,
|
$this->createYearPeriod($now->year + 1);
|
||||||
])->create();
|
|
||||||
YearPeriod::factory([
|
|
||||||
"year" => $now->year + 1,
|
|
||||||
])->create();
|
|
||||||
|
|
||||||
$this->assertDatabaseCount("year_periods", 2);
|
$this->assertDatabaseCount("year_periods", 2);
|
||||||
|
|
||||||
|
83
tests/Unit/YearPeriodRetrieverTest.php
Normal file
83
tests/Unit/YearPeriodRetrieverTest.php
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Tests\Unit;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Testing\Concerns\InteractsWithSession;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
use Tests\TestCase;
|
||||||
|
use Tests\Traits\InteractsWithYearPeriods;
|
||||||
|
use Toby\Helpers\YearPeriodRetriever;
|
||||||
|
use Toby\Models\YearPeriod;
|
||||||
|
|
||||||
|
class YearPeriodRetrieverTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
use InteractsWithSession;
|
||||||
|
use InteractsWithYearPeriods;
|
||||||
|
|
||||||
|
public Carbon $current;
|
||||||
|
public YearPeriod $previousYearPeriod;
|
||||||
|
public YearPeriod $currentYearPeriod;
|
||||||
|
public YearPeriod $nextYearPeriod;
|
||||||
|
public YearPeriodRetriever $yearPeriodRetriever;
|
||||||
|
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->current = Carbon::now();
|
||||||
|
Carbon::setTestNow($this->current);
|
||||||
|
|
||||||
|
$this->yearPeriodRetriever = new YearPeriodRetriever();
|
||||||
|
|
||||||
|
$this->previousYearPeriod = $this->createYearPeriod($this->current->year - 1);
|
||||||
|
$this->currentYearPeriod = $this->createCurrentYearPeriod();
|
||||||
|
$this->nextYearPeriod = $this->createYearPeriod($this->current->year + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRetrievesCorrectCurrentYearPeriod(): void
|
||||||
|
{
|
||||||
|
$this->assertSame($this->currentYearPeriod->id, $this->yearPeriodRetriever->current()->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRetrievesCurrentYearPeriodWhenNoSelected(): void
|
||||||
|
{
|
||||||
|
$this->clearSelectedYearPeriod();
|
||||||
|
|
||||||
|
$this->assertSame($this->currentYearPeriod->id, $this->yearPeriodRetriever->selected()->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRetrievesCorrectYearPeriodWhenSelected(): void
|
||||||
|
{
|
||||||
|
$this->markYearPeriodAsSelected($this->nextYearPeriod);
|
||||||
|
|
||||||
|
$this->assertSame($this->nextYearPeriod->id, $this->yearPeriodRetriever->selected()->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLinks(): void
|
||||||
|
{
|
||||||
|
$expected = [
|
||||||
|
"current" => $this->current->year,
|
||||||
|
"navigation" => [
|
||||||
|
[
|
||||||
|
"year" => $this->previousYearPeriod->year,
|
||||||
|
"link" => route("year-periods.select", $this->previousYearPeriod)
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"year" => $this->currentYearPeriod->year,
|
||||||
|
"link" => route("year-periods.select", $this->currentYearPeriod)
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"year" => $this->nextYearPeriod->year,
|
||||||
|
"link" => route("year-periods.select", $this->nextYearPeriod)
|
||||||
|
],
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
$this->assertSame($expected, $this->yearPeriodRetriever->links());
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user