Compare commits
8 Commits
#152-dashb
...
61add6b1e8
Author | SHA1 | Date | |
---|---|---|---|
61add6b1e8
|
|||
cbe70315eb
|
|||
62c3c11ebf
|
|||
af25d80d2e
|
|||
|
902af64833 | ||
|
d1047e8262 | ||
643f546142 | |||
995c0b6696 |
@@ -103,6 +103,15 @@ class UserVacationStatsRetriever
|
||||
return $limit ?? 0;
|
||||
}
|
||||
|
||||
public function hasVacationDaysLimit(User $user, YearPeriod $yearPeriod): bool
|
||||
{
|
||||
$limit = $user->vacationLimits()
|
||||
->whereBelongsTo($yearPeriod)
|
||||
->first()?->days;
|
||||
|
||||
return $limit !== null;
|
||||
}
|
||||
|
||||
protected function getLimitableVacationTypes(): Collection
|
||||
{
|
||||
$types = VacationType::all();
|
||||
|
@@ -31,6 +31,7 @@ class Resume extends Model
|
||||
"technologies" => AsCollection::class,
|
||||
"projects" => AsCollection::class,
|
||||
];
|
||||
protected $perPage = 50;
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
|
@@ -46,6 +46,7 @@ class User extends Authenticatable implements NotifiableInterface
|
||||
protected $with = [
|
||||
"profile",
|
||||
];
|
||||
protected $perPage = 50;
|
||||
|
||||
public function profile(): HasOne
|
||||
{
|
||||
|
@@ -49,6 +49,7 @@ class VacationRequest extends Model
|
||||
"to" => "date",
|
||||
"event_ids" => AsCollection::class,
|
||||
];
|
||||
protected $perPage = 50;
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
|
@@ -74,6 +74,7 @@ class DashboardController extends Controller
|
||||
->get();
|
||||
|
||||
$limit = $vacationStatsRetriever->getVacationDaysLimit($user, $yearPeriod);
|
||||
$hasLimit = $vacationStatsRetriever->hasVacationDaysLimit($user, $yearPeriod);
|
||||
$used = $vacationStatsRetriever->getUsedVacationDays($user, $yearPeriod);
|
||||
$pending = $vacationStatsRetriever->getPendingVacationDays($user, $yearPeriod);
|
||||
$homeOffice = $vacationStatsRetriever->getHomeOfficeDays($user, $yearPeriod);
|
||||
@@ -99,6 +100,7 @@ class DashboardController extends Controller
|
||||
],
|
||||
),
|
||||
"stats" => [
|
||||
"hasLimit" => $hasLimit,
|
||||
"limit" => $limit,
|
||||
"remaining" => $remaining,
|
||||
"used" => $used,
|
||||
|
@@ -9,7 +9,7 @@
|
||||
"ext-pdo": "*",
|
||||
"ext-redis": "*",
|
||||
"azuyalabs/yasumi": "^2.4",
|
||||
"barryvdh/laravel-dompdf": "^1.0",
|
||||
"barryvdh/laravel-dompdf": "^2.0",
|
||||
"fruitcake/laravel-cors": "^3.0",
|
||||
"guzzlehttp/guzzle": "^7.0.1",
|
||||
"inertiajs/inertia-laravel": "^0.6.2",
|
||||
|
508
composer.lock
generated
508
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@ version: '3.8'
|
||||
|
||||
services:
|
||||
web:
|
||||
image: nginx:1.21-alpine
|
||||
image: nginx:1.23-alpine
|
||||
container_name: toby-web
|
||||
working_dir: /application
|
||||
volumes:
|
||||
@@ -76,7 +76,7 @@ services:
|
||||
restart: unless-stopped
|
||||
|
||||
node:
|
||||
image: node:18.1.0-alpine3.14
|
||||
image: node:18.4.0-alpine3.16
|
||||
container_name: toby-node
|
||||
working_dir: /application
|
||||
volumes:
|
||||
|
741
package-lock.json
generated
741
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
22
package.json
22
package.json
@@ -13,7 +13,7 @@
|
||||
"lintf": "./node_modules/.bin/eslint resources/js --ext .js,.vue --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/vue": "^1.6.4",
|
||||
"@headlessui/vue": "^1.6.5",
|
||||
"@heroicons/vue": "^1.0.6",
|
||||
"@inertiajs/inertia": "^0.11.0",
|
||||
"@inertiajs/inertia-vue3": "^0.6.0",
|
||||
@@ -21,27 +21,27 @@
|
||||
"@tailwindcss/forms": "^0.5.2",
|
||||
"@tailwindcss/line-clamp": "^0.4.0",
|
||||
"@tailwindcss/typography": "^0.5.2",
|
||||
"@vue/compiler-sfc": "^3.2.31",
|
||||
"@vue/compiler-sfc": "^3.2.37",
|
||||
"autoprefixer": "^10.4.7",
|
||||
"axios": "^0.27.2",
|
||||
"echarts": "^5.3.2",
|
||||
"echarts": "^5.3.3",
|
||||
"flatpickr": "^4.6.13",
|
||||
"laravel-mix": "^6.0.44",
|
||||
"laravel-mix": "^6.0.49",
|
||||
"lodash": "^4.17.21",
|
||||
"luxon": "^2.3.2",
|
||||
"luxon": "^2.4.0",
|
||||
"postcss": "^8.4.14",
|
||||
"tailwindcss": "^3.0.24",
|
||||
"vue": "3.2.21",
|
||||
"vue-echarts": "^6.0.2",
|
||||
"tailwindcss": "^3.1.4",
|
||||
"vue": "3.2.37",
|
||||
"vue-echarts": "^6.1.0",
|
||||
"vue-flatpickr-component": "^9.0.6",
|
||||
"vue-loader": "^17.0.0",
|
||||
"vue-material-design-icons": "^5.0.0",
|
||||
"vue-material-design-icons": "^5.1.1",
|
||||
"vue-toastification": "^2.0.0-rc.5",
|
||||
"vue3-popper": "^1.5.0",
|
||||
"vuedraggable": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.16.0",
|
||||
"eslint-plugin-vue": "^9.1.0"
|
||||
"eslint": "^8.19.0",
|
||||
"eslint-plugin-vue": "^9.1.1"
|
||||
}
|
||||
}
|
||||
|
@@ -8,9 +8,6 @@
|
||||
:approved-vacations="approvedVacations"
|
||||
:pending-vacations="pendingVacations"
|
||||
/>
|
||||
<VacationStats :stats="stats" />
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
<PendingVacationRequests
|
||||
v-if="can.listAllVacationRequests"
|
||||
:requests="vacationRequests.data"
|
||||
@@ -19,6 +16,9 @@
|
||||
v-else
|
||||
:requests="vacationRequests.data"
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
<VacationStats :stats="stats" />
|
||||
<AbsenceList
|
||||
v-if="years.current.year === years.selected.year && absences.data.length"
|
||||
:absences="absences.data"
|
||||
|
@@ -297,8 +297,8 @@
|
||||
<button
|
||||
type="submit"
|
||||
class="inline-flex justify-center py-2 px-4 text-sm font-medium text-white bg-blumilk-600 rounded-md border border-transparent focus:outline-none focus:ring-2 focus:ring-blumilk-500 focus:ring-offset-2 shadow-sm"
|
||||
:class="[form.processing || !form.isDirty ? 'disabled:opacity-60' : 'hover:bg-blumilk-700']"
|
||||
:disabled="form.processing || !form.isDirty"
|
||||
:class="[form.processing || !isDirty ? 'disabled:opacity-60' : 'hover:bg-blumilk-700']"
|
||||
:disabled="form.processing || !isDirty"
|
||||
>
|
||||
Zapisz
|
||||
</button>
|
||||
@@ -359,6 +359,13 @@ const form = useForm({
|
||||
flowSkipped: false,
|
||||
})
|
||||
|
||||
let isDirty = ref(false)
|
||||
|
||||
watch(form, formData => {
|
||||
const { from, to } = formData.data()
|
||||
isDirty.value = formData.isDirty || from !== null || to !== null
|
||||
}, { immediate: true, deep: true })
|
||||
|
||||
refreshEstimatedDays(form.from, form.to)
|
||||
|
||||
const estimatedDays = ref([])
|
||||
|
@@ -167,7 +167,7 @@ const selectedYear = useCurrentYearPeriodInfo().year.value
|
||||
const weeksInYear = DateTime.fromObject({ year: selectedYear, month: 12, day: 31 }).weekNumber
|
||||
|
||||
const calendar = {
|
||||
viewMode: ref('week'),
|
||||
viewMode: ref(localStorage.getItem('calendarViewMode') ?? 'week'),
|
||||
currents: reactive({
|
||||
year: selectedYear,
|
||||
month: selectedYear === currentDate.year ? currentDate.month : 1,
|
||||
@@ -336,6 +336,7 @@ function updateViewMode(type) {
|
||||
else
|
||||
resetCalendar()
|
||||
calendar.viewMode.value = type
|
||||
localStorage.setItem('calendarViewMode', type)
|
||||
}
|
||||
|
||||
function isInCurrentMonth(date) {
|
||||
|
@@ -1,84 +1,83 @@
|
||||
<template>
|
||||
<section class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div class="hidden p-4 bg-white shadow-md md:block">
|
||||
<VacationChart :stats="stats" />
|
||||
<section>
|
||||
<div
|
||||
v-if="stats.hasLimit"
|
||||
class="grid grid-cols-2 gap-2 h-full"
|
||||
>
|
||||
<div class="py-5 px-4 bg-white shadow-md sm:p-6">
|
||||
<dd class="mt-1 text-4xl font-semibold text-blumilk-500">
|
||||
{{ stats.remaining }}
|
||||
</dd>
|
||||
<dt class="font-medium text-gray-700 truncate text-md">
|
||||
Pozostało
|
||||
</dt>
|
||||
</div>
|
||||
<div class="py-5 px-4 bg-white shadow-md sm:p-6">
|
||||
<dd class="mt-1 text-4xl font-semibold text-blumilk-700">
|
||||
{{ stats.used }}
|
||||
</dd>
|
||||
<dt class="font-medium text-gray-700 truncate text-md">
|
||||
Wykorzystane
|
||||
</dt>
|
||||
</div>
|
||||
<div class="py-5 px-4 bg-white shadow-md sm:p-6">
|
||||
<dt class="mt-1 text-4xl font-semibold text-blumilk-200">
|
||||
{{ stats.pending }}
|
||||
</dt>
|
||||
<dd class="font-medium text-gray-700 truncate text-md">
|
||||
Rozpatrywane
|
||||
</dd>
|
||||
</div>
|
||||
<div class="py-5 px-4 bg-white shadow-md sm:p-6">
|
||||
<dt class="mt-1 text-4xl font-semibold text-blumilk-800">
|
||||
{{ stats.limit }}
|
||||
</dt>
|
||||
<dd class="font-medium text-gray-700 truncate text-md">
|
||||
Limit urlopu
|
||||
</dd>
|
||||
</div>
|
||||
<div class="py-5 px-4 bg-white shadow-md sm:p-6">
|
||||
<dt class="mt-1 text-4xl font-semibold text-gray-500">
|
||||
{{ stats.other }}
|
||||
</dt>
|
||||
<dd class="font-medium text-gray-700 truncate text-md">
|
||||
Inne urlopy
|
||||
</dd>
|
||||
</div>
|
||||
<div class="py-5 px-4 bg-white shadow-md sm:p-6">
|
||||
<dt class="mt-1 text-4xl font-semibold text-lime-500">
|
||||
{{ stats.homeOffice }}
|
||||
</dt>
|
||||
<dd class="font-medium text-gray-700 truncate text-md">
|
||||
Praca zdalna
|
||||
</dd>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-full">
|
||||
<div class="grid grid-cols-2 gap-4 h-full">
|
||||
<div class="py-5 px-4 bg-white shadow-md sm:p-6">
|
||||
<dd class="mt-1 text-4xl font-semibold text-blumilk-500">
|
||||
{{ stats.remaining }}
|
||||
</dd>
|
||||
<dt class="font-medium text-gray-700 truncate text-md">
|
||||
Pozostało
|
||||
</dt>
|
||||
<dt class="mt-2 text-sm font-medium text-gray-500">
|
||||
Dni do wykorzystania teraz.
|
||||
</dt>
|
||||
</div>
|
||||
<div class="py-5 px-4 bg-white shadow-md sm:p-6">
|
||||
<dd class="mt-1 text-4xl font-semibold text-blumilk-700">
|
||||
{{ stats.used }}
|
||||
</dd>
|
||||
<dt class="font-medium text-gray-700 truncate text-md">
|
||||
Wykorzystane
|
||||
</dt>
|
||||
<dt class="mt-2 text-sm font-medium text-gray-500">
|
||||
Dni, które zostały już wykorzystane na urlop wypoczynkowy.
|
||||
</dt>
|
||||
</div>
|
||||
<div class="py-5 px-4 bg-white shadow-md sm:p-6">
|
||||
<dt class="mt-1 text-4xl font-semibold text-blumilk-200">
|
||||
{{ stats.pending }}
|
||||
</dt>
|
||||
<dd class="font-medium text-gray-700 truncate text-md">
|
||||
Rozpatrywane
|
||||
</dd>
|
||||
<dt class="mt-2 text-sm font-medium text-gray-500">
|
||||
Dni czekające na akceptację przełożonych.
|
||||
</dt>
|
||||
</div>
|
||||
<div class="py-5 px-4 bg-white shadow-md sm:p-6">
|
||||
<dt class="mt-1 text-4xl font-semibold text-blumilk-800">
|
||||
{{ stats.limit }}
|
||||
</dt>
|
||||
<dd class="font-medium text-gray-700 truncate text-md">
|
||||
Limit urlopu
|
||||
</dd>
|
||||
<dt class="mt-2 text-sm font-medium text-gray-500">
|
||||
Twój roczny limit urlopu wypoczynkowego.
|
||||
</dt>
|
||||
</div>
|
||||
<div class="py-5 px-4 bg-white shadow-md sm:p-6">
|
||||
<dt class="mt-1 text-4xl font-semibold text-gray-500">
|
||||
{{ stats.other }}
|
||||
</dt>
|
||||
<dd class="font-medium text-gray-700 truncate text-md">
|
||||
Inne urlopy
|
||||
</dd>
|
||||
<dt class="mt-2 text-sm font-medium text-gray-500">
|
||||
Urlopy bezpłatne, okolicznościowe, zwolnienia lekarskie, itd., które zostały już zatwierdzone.
|
||||
</dt>
|
||||
</div>
|
||||
<div class="py-5 px-4 bg-white shadow-md sm:p-6">
|
||||
<dt class="mt-1 text-4xl font-semibold text-lime-500">
|
||||
{{ stats.homeOffice }}
|
||||
</dt>
|
||||
<dd class="font-medium text-gray-700 truncate text-md">
|
||||
Praca zdalna
|
||||
</dd>
|
||||
<dt class="mt-2 text-sm font-medium text-gray-500">
|
||||
Dni przepracowane poza biurem.
|
||||
</dt>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="h-full grid grid-cols-2 gap-2 h-full"
|
||||
>
|
||||
<div class="py-5 px-4 bg-white shadow-md sm:p-6">
|
||||
<dt class="mt-1 text-4xl font-semibold text-gray-500">
|
||||
{{ stats.other }}
|
||||
</dt>
|
||||
<dd class="font-medium text-gray-700 truncate text-md">
|
||||
Inne urlopy
|
||||
</dd>
|
||||
</div>
|
||||
<div class="py-5 px-4 bg-white shadow-md sm:p-6">
|
||||
<dt class="mt-1 text-4xl font-semibold text-lime-500">
|
||||
{{ stats.homeOffice }}
|
||||
</dt>
|
||||
<dd class="font-medium text-gray-700 truncate text-md">
|
||||
Praca zdalna
|
||||
</dd>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import VacationChart from '@/Shared/VacationChart'
|
||||
|
||||
defineProps({
|
||||
stats: Object,
|
||||
})
|
||||
|
@@ -73,10 +73,10 @@ class UserTest extends FeatureTestCase
|
||||
|
||||
public function testUserListIsPaginated(): void
|
||||
{
|
||||
User::factory()->count(15)->create();
|
||||
User::factory()->count(50)->create();
|
||||
$admin = User::factory()->admin()->create();
|
||||
|
||||
$this->assertDatabaseCount("users", 16);
|
||||
$this->assertDatabaseCount("users", 51);
|
||||
|
||||
$this->actingAs($admin)
|
||||
->get("/users?page=2")
|
||||
|
Reference in New Issue
Block a user