Compare commits
7 Commits
feature/ad
...
featute/re
| Author | SHA1 | Date | |
|---|---|---|---|
| 38943743aa | |||
| 866118a86f | |||
| aab8d33b8d | |||
| eb9c8796de | |||
| 53c4823b22 | |||
| fe47b79a25 | |||
| 27449eabf0 |
@@ -32,3 +32,6 @@ jobs:
|
|||||||
|
|
||||||
- name: Run test suite
|
- name: Run test suite
|
||||||
run: php artisan test
|
run: php artisan test
|
||||||
|
|
||||||
|
- name: Run static analysis
|
||||||
|
run: composer analyse
|
||||||
|
|||||||
123
README.md
123
README.md
@@ -1,79 +1,106 @@
|
|||||||
# Interactieve Laravel CV Applicatie
|
# CV Roberto
|
||||||
|
|
||||||
Deze Laravel-applicatie is gebouwd als mijn interactieve en dynamische cv. In plaats van een statisch pdf-bestand, kun je hier real-time mijn:
|
Dit project is mijn interactieve CV en tegelijk een voorbeeld van hoe ik een Laravel-app opzet.
|
||||||
|
|
||||||
- **Vaardigheden** (met beoordeling en iconen)
|
Live: [cv.robert.ooo](https://cv.robert.ooo)
|
||||||
- **Werkervaring**
|
|
||||||
- **Opleidingen**
|
|
||||||
- **Persoonlijke gegevens**
|
|
||||||
- **Tags & kernkwaliteiten**
|
|
||||||
|
|
||||||
zien — inclusief slimme automatisering, logging en Telegram-notificaties voor recruiterinteracties.
|
## Wat zit erin
|
||||||
|
|
||||||
---
|
- Publieke CV-pagina met werkervaring, opleidingen, skills en personalia.
|
||||||
|
- Adminomgeving om CV-data te beheren.
|
||||||
|
- Afbeeldingsuploads via Spatie Media Library.
|
||||||
|
- Contactformulier met queue job voor Telegram-notificaties.
|
||||||
|
- Klikbare verborgen personalia, zodat bots de waarde niet direct in de HTML zien.
|
||||||
|
- Feature tests voor de belangrijkste controllerflows.
|
||||||
|
- CI-checks voor tests, Larastan en formatting.
|
||||||
|
|
||||||
## 🧰 Techniek & Stack
|
## Stack
|
||||||
|
|
||||||
- **Framework:** Laravel 12
|
- Laravel 12
|
||||||
- **Frontend:** Tailwind CSS, Blade
|
- PHP 8.2+
|
||||||
- **DevOps-integraties:** Telegram alerts, Healthchecks, custom logging
|
- Blade
|
||||||
- **CI/CD-ready:** Ondersteuning voor deploy hooks en jobs
|
- Tailwind CSS
|
||||||
- **Overige tools:** Docker, Nginx, Git, Redis, Cron, Promtail, Grafana
|
- Pest
|
||||||
|
- Larastan level 7
|
||||||
|
- Laravel Pint
|
||||||
|
- Blade Formatter
|
||||||
|
- Docker / Laravel Sail
|
||||||
|
|
||||||
---
|
## Kwaliteitschecks
|
||||||
|
|
||||||
## ⚙️ Installatie
|
Deze checks horen groen te zijn voordat een merge logisch is:
|
||||||
|
|
||||||
1. **Clone deze repo:**
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/roberto-guagliardo/cv-app.git
|
composer test
|
||||||
cd cv-app
|
composer analyse
|
||||||
|
composer format:check
|
||||||
|
npm run format:check
|
||||||
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Installeer dependencies:**
|
In de workflow worden dezelfde checks afgedwongen:
|
||||||
|
|
||||||
```bash
|
- PHPUnit/Pest feature tests
|
||||||
composer install
|
- Larastan op level 7
|
||||||
npm install && npm run build
|
- PHP formatting via Pint
|
||||||
```
|
- Blade formatting via Blade Formatter
|
||||||
|
|
||||||
3. **Maak .env aan en configureer je database, storage en Telegram:**
|
Skipped tests zijn alleen acceptabel als dat bewust is, zoals disabled registratieflows. Larastan en formatting moeten gewoon groen zijn.
|
||||||
|
|
||||||
|
## Lokaal draaien
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
|
composer install
|
||||||
|
npm ci
|
||||||
php artisan key:generate
|
php artisan key:generate
|
||||||
```
|
|
||||||
|
|
||||||
4. **Voer migraties & seeders uit:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
php artisan migrate --seed
|
php artisan migrate --seed
|
||||||
```
|
npm run build
|
||||||
|
|
||||||
5. **Geniet van de app:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
php artisan serve
|
php artisan serve
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
Met Sail:
|
||||||
|
|
||||||
## 🌐 Live Demo
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
./vendor/bin/sail up -d
|
||||||
|
./vendor/bin/sail artisan key:generate
|
||||||
|
./vendor/bin/sail artisan migrate --seed
|
||||||
|
npm ci
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
Wil je zien hoe het eruitziet? Bekijk mijn live cv op:
|
Voor de admin login kun je in `.env` deze waardes zetten en daarna opnieuw seeden:
|
||||||
|
|
||||||
➡️ [cv.robert.ooo](https://cv.robert.ooo)
|
```env
|
||||||
|
ADMIN_NAME="Admin"
|
||||||
|
ADMIN_EMAIL=admin@example.com
|
||||||
|
ADMIN_PASSWORD=changeme123
|
||||||
|
```
|
||||||
|
|
||||||
---
|
## Development
|
||||||
|
|
||||||
## 📊 Bijdragen
|
PHP formatter:
|
||||||
|
|
||||||
Deze applicatie is persoonlijk en niet bedoeld voor publieke bijdragen, maar voel je vrij om de structuur of ideeën te gebruiken voor je eigen showcase-app.
|
```bash
|
||||||
|
composer format
|
||||||
|
composer format:check
|
||||||
|
```
|
||||||
|
|
||||||
---
|
Blade formatter:
|
||||||
|
|
||||||
## 💌 Contact
|
```bash
|
||||||
|
npm run format
|
||||||
|
npm run format:check
|
||||||
|
```
|
||||||
|
|
||||||
Wil je mij benaderen voor een samenwerking of opdracht?
|
Tests en analyse:
|
||||||
Gebruik het contactformulier op de site of stuur direct een bericht via [Telegram](https://t.me/robertguagliardo).
|
|
||||||
|
```bash
|
||||||
|
composer test
|
||||||
|
composer analyse
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contact
|
||||||
|
|
||||||
|
Gebruik het contactformulier op de site of stuur me een bericht via [Telegram](https://t.me/robertguagliardo).
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Auth;
|
|||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Auth\Events\Verified;
|
use Illuminate\Auth\Events\Verified;
|
||||||
|
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||||
use Illuminate\Foundation\Auth\EmailVerificationRequest;
|
use Illuminate\Foundation\Auth\EmailVerificationRequest;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
|
||||||
@@ -14,12 +15,18 @@ class VerifyEmailController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function __invoke(EmailVerificationRequest $request): RedirectResponse
|
public function __invoke(EmailVerificationRequest $request): RedirectResponse
|
||||||
{
|
{
|
||||||
if ($request->user()->hasVerifiedEmail()) {
|
$user = $request->user();
|
||||||
|
|
||||||
|
if (! $user instanceof MustVerifyEmail) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->hasVerifiedEmail()) {
|
||||||
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
|
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($request->user()->markEmailAsVerified()) {
|
if ($user->markEmailAsVerified()) {
|
||||||
event(new Verified($request->user()));
|
event(new Verified($user));
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
|
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
|
||||||
|
|||||||
@@ -4,23 +4,25 @@ namespace App\Http\Controllers;
|
|||||||
|
|
||||||
use App\Http\Requests\EducationRequest;
|
use App\Http\Requests\EducationRequest;
|
||||||
use App\Models\Education;
|
use App\Models\Education;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
class EducationController extends Controller
|
class EducationController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
public function index(): View
|
||||||
{
|
{
|
||||||
$educations = Education::with('media')->latest()->get();
|
$educations = Education::with('media')->latest()->get();
|
||||||
|
|
||||||
return view('educations.index', compact('educations'));
|
return view('educations.index', compact('educations'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create()
|
public function create(): View
|
||||||
{
|
{
|
||||||
return view('educations.create');
|
return view('educations.create');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(EducationRequest $request)
|
public function store(EducationRequest $request): RedirectResponse
|
||||||
{
|
{
|
||||||
$education = Education::create($request->validated());
|
$education = Education::create($request->validated());
|
||||||
|
|
||||||
@@ -29,17 +31,12 @@ class EducationController extends Controller
|
|||||||
return redirect()->route('educations.index')->with('success', 'Opleiding toegevoegd.');
|
return redirect()->route('educations.index')->with('success', 'Opleiding toegevoegd.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function show(Education $education)
|
public function edit(Education $education): View
|
||||||
{
|
|
||||||
return view('educations.show', compact('education'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function edit(Education $education)
|
|
||||||
{
|
{
|
||||||
return view('educations.edit', compact('education'));
|
return view('educations.edit', compact('education'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(EducationRequest $request, Education $education)
|
public function update(EducationRequest $request, Education $education): RedirectResponse
|
||||||
{
|
{
|
||||||
$education->update($request->validated());
|
$education->update($request->validated());
|
||||||
|
|
||||||
@@ -48,7 +45,7 @@ class EducationController extends Controller
|
|||||||
return redirect()->route('educations.index')->with('success', 'Opleiding bijgewerkt.');
|
return redirect()->route('educations.index')->with('success', 'Opleiding bijgewerkt.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy(Education $education)
|
public function destroy(Education $education): RedirectResponse
|
||||||
{
|
{
|
||||||
$education->clearMediaCollection('image');
|
$education->clearMediaCollection('image');
|
||||||
$education->delete();
|
$education->delete();
|
||||||
|
|||||||
@@ -8,11 +8,13 @@ use App\Models\Education;
|
|||||||
use App\Models\Personalia;
|
use App\Models\Personalia;
|
||||||
use App\Models\Skill;
|
use App\Models\Skill;
|
||||||
use App\Models\WorkExperience;
|
use App\Models\WorkExperience;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
class FrontendController extends Controller
|
class FrontendController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
public function index(): View
|
||||||
{
|
{
|
||||||
$skills = Skill::all()->groupBy('type');
|
$skills = Skill::all()->groupBy('type');
|
||||||
|
|
||||||
@@ -23,21 +25,20 @@ class FrontendController extends Controller
|
|||||||
return view('welcome', compact('skills', 'personalia', 'education', 'experience'));
|
return view('welcome', compact('skills', 'personalia', 'education', 'experience'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPersonalia($id)
|
public function getPersonalia(Personalia $personalia): JsonResponse
|
||||||
{
|
{
|
||||||
$item = Personalia::findOrFail($id);
|
|
||||||
NotifyTelegramAboutPersonaliaClick::dispatch(
|
NotifyTelegramAboutPersonaliaClick::dispatch(
|
||||||
$item,
|
$personalia,
|
||||||
request()->ip(),
|
request()->ip(),
|
||||||
request()->userAgent()
|
request()->userAgent()
|
||||||
);
|
);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'value' => $item->value,
|
'value' => $personalia->value,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function message(Request $request)
|
public function message(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$validated = $request->validate([
|
$validated = $request->validate([
|
||||||
'name' => 'required|string|max:255',
|
'name' => 'required|string|max:255',
|
||||||
|
|||||||
@@ -2,25 +2,26 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\Personalia;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use App\Http\Requests\PersonaliaRequest;
|
use App\Http\Requests\PersonaliaRequest;
|
||||||
|
use App\Models\Personalia;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
class PersonaliaController extends Controller
|
class PersonaliaController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
public function index(): View
|
||||||
{
|
{
|
||||||
$personalia = Personalia::all();
|
$personalia = Personalia::all();
|
||||||
|
|
||||||
return view('personalia.index', compact('personalia'));
|
return view('personalia.index', compact('personalia'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create()
|
public function create(): View
|
||||||
{
|
{
|
||||||
return view('personalia.create');
|
return view('personalia.create');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(PersonaliaRequest $request)
|
public function store(PersonaliaRequest $request): RedirectResponse
|
||||||
{
|
{
|
||||||
$validated = $request->validated();
|
$validated = $request->validated();
|
||||||
|
|
||||||
@@ -32,13 +33,13 @@ class PersonaliaController extends Controller
|
|||||||
return redirect()->route('personalia.index')->with('success', 'Persoonlijk item toegevoegd.');
|
return redirect()->route('personalia.index')->with('success', 'Persoonlijk item toegevoegd.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function edit(Personalia $personalium)
|
public function edit(Personalia $personalium): View
|
||||||
{
|
{
|
||||||
|
|
||||||
return view('personalia.edit', ['personalia' => $personalium]);
|
return view('personalia.edit', ['personalia' => $personalium]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(Request $request, Personalia $personalium)
|
public function update(PersonaliaRequest $request, Personalia $personalium): RedirectResponse
|
||||||
{
|
{
|
||||||
$validated = $request->validated();
|
$validated = $request->validated();
|
||||||
|
|
||||||
@@ -46,12 +47,13 @@ class PersonaliaController extends Controller
|
|||||||
...$validated,
|
...$validated,
|
||||||
'hidden' => $request->boolean('hidden'),
|
'hidden' => $request->boolean('hidden'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return redirect()->route('personalia.index')->with('success', 'Persoonlijk item bijgewerkt.');
|
return redirect()->route('personalia.index')->with('success', 'Persoonlijk item bijgewerkt.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy(Personalia $personalia)
|
public function destroy(Personalia $personalium): RedirectResponse
|
||||||
{
|
{
|
||||||
$personalia->delete();
|
$personalium->delete();
|
||||||
|
|
||||||
return redirect()->route('personalia.index')->with('success', 'Persoonlijk item verwijderd.');
|
return redirect()->route('personalia.index')->with('success', 'Persoonlijk item verwijderd.');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,25 +2,26 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\Skill;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use App\Http\Requests\SkillRequest;
|
use App\Http\Requests\SkillRequest;
|
||||||
|
use App\Models\Skill;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
class SkillController extends Controller
|
class SkillController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
public function index(): View
|
||||||
{
|
{
|
||||||
$skills = Skill::latest()->get();
|
$skills = Skill::latest()->get();
|
||||||
|
|
||||||
return view('skills.index', compact('skills'));
|
return view('skills.index', compact('skills'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create()
|
public function create(): View
|
||||||
{
|
{
|
||||||
return view('skills.create');
|
return view('skills.create');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(SkillRequest $request)
|
public function store(SkillRequest $request): RedirectResponse
|
||||||
{
|
{
|
||||||
$skill = Skill::create($request->validated());
|
$skill = Skill::create($request->validated());
|
||||||
|
|
||||||
@@ -31,17 +32,12 @@ class SkillController extends Controller
|
|||||||
return redirect()->route('skills.index')->with('success', 'Vaardigheid toegevoegd.');
|
return redirect()->route('skills.index')->with('success', 'Vaardigheid toegevoegd.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function show(Skill $skill)
|
public function edit(Skill $skill): View
|
||||||
{
|
|
||||||
return view('skills.show', compact('skill'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function edit(Skill $skill)
|
|
||||||
{
|
{
|
||||||
return view('skills.edit', compact('skill'));
|
return view('skills.edit', compact('skill'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(SkillRequest $request, Skill $skill)
|
public function update(SkillRequest $request, Skill $skill): RedirectResponse
|
||||||
{
|
{
|
||||||
$skill->update($request->validated());
|
$skill->update($request->validated());
|
||||||
|
|
||||||
@@ -53,7 +49,7 @@ class SkillController extends Controller
|
|||||||
return redirect()->route('skills.index')->with('success', 'Vaardigheid bijgewerkt.');
|
return redirect()->route('skills.index')->with('success', 'Vaardigheid bijgewerkt.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy(Skill $skill)
|
public function destroy(Skill $skill): RedirectResponse
|
||||||
{
|
{
|
||||||
$skill->clearMediaCollection('image');
|
$skill->clearMediaCollection('image');
|
||||||
$skill->delete();
|
$skill->delete();
|
||||||
|
|||||||
@@ -2,26 +2,26 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\WorkExperience;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use App\Http\Requests\WorkExperienceRequest;
|
use App\Http\Requests\WorkExperienceRequest;
|
||||||
|
use App\Models\WorkExperience;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
class WorkExperienceController extends Controller
|
class WorkExperienceController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
public function index(): View
|
||||||
{
|
{
|
||||||
$experiences = WorkExperience::with('media')->latest()->get();
|
$experiences = WorkExperience::with('media')->latest()->get();
|
||||||
|
|
||||||
return view('work_experiences.index', compact('experiences'));
|
return view('work_experiences.index', compact('experiences'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create()
|
public function create(): View
|
||||||
{
|
{
|
||||||
return view('work_experiences.create');
|
return view('work_experiences.create');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(WorkExperienceRequest $request)
|
public function store(WorkExperienceRequest $request): RedirectResponse
|
||||||
{
|
{
|
||||||
$experience = WorkExperience::create($request->validated());
|
$experience = WorkExperience::create($request->validated());
|
||||||
|
|
||||||
@@ -32,17 +32,12 @@ class WorkExperienceController extends Controller
|
|||||||
return redirect()->route('work-experiences.index')->with('success', 'Ervaring toegevoegd.');
|
return redirect()->route('work-experiences.index')->with('success', 'Ervaring toegevoegd.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function show(WorkExperience $workExperience)
|
public function edit(WorkExperience $workExperience): View
|
||||||
{
|
|
||||||
return view('work_experiences.show', compact('workExperience'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function edit(WorkExperience $workExperience)
|
|
||||||
{
|
{
|
||||||
return view('work_experiences.edit', compact('workExperience'));
|
return view('work_experiences.edit', compact('workExperience'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(WorkExperienceRequest $request, WorkExperience $workExperience)
|
public function update(WorkExperienceRequest $request, WorkExperience $workExperience): RedirectResponse
|
||||||
{
|
{
|
||||||
$workExperience->update($request->validated());
|
$workExperience->update($request->validated());
|
||||||
|
|
||||||
@@ -54,7 +49,7 @@ class WorkExperienceController extends Controller
|
|||||||
return redirect()->route('work-experiences.index')->with('success', 'Ervaring bijgewerkt.');
|
return redirect()->route('work-experiences.index')->with('success', 'Ervaring bijgewerkt.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy(WorkExperience $workExperience)
|
public function destroy(WorkExperience $workExperience): RedirectResponse
|
||||||
{
|
{
|
||||||
$workExperience->clearMediaCollection('image');
|
$workExperience->clearMediaCollection('image');
|
||||||
$workExperience->delete();
|
$workExperience->delete();
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ class EducationRequest extends FormRequest
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, list<string>>
|
||||||
|
*/
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@@ -23,6 +26,9 @@ class EducationRequest extends FormRequest
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, string>
|
||||||
|
*/
|
||||||
public function messages(): array
|
public function messages(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ class PersonaliaRequest extends FormRequest
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, list<string>>
|
||||||
|
*/
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@@ -21,6 +24,9 @@ class PersonaliaRequest extends FormRequest
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, string>
|
||||||
|
*/
|
||||||
public function messages(): array
|
public function messages(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@@ -28,5 +34,4 @@ class PersonaliaRequest extends FormRequest
|
|||||||
'value.required' => 'Een waarde is verplicht.',
|
'value.required' => 'Een waarde is verplicht.',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ class SkillRequest extends FormRequest
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, list<string>>
|
||||||
|
*/
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@@ -21,6 +24,10 @@ class SkillRequest extends FormRequest
|
|||||||
'type' => ['required', 'in:rating,tag,other'],
|
'type' => ['required', 'in:rating,tag,other'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, string>
|
||||||
|
*/
|
||||||
public function messages(): array
|
public function messages(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@@ -29,5 +36,4 @@ class SkillRequest extends FormRequest
|
|||||||
'type.in' => 'Het type moet rating, tag of other zijn.',
|
'type.in' => 'Het type moet rating, tag of other zijn.',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ class WorkExperienceRequest extends FormRequest
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, list<string>>
|
||||||
|
*/
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@@ -23,6 +26,9 @@ class WorkExperienceRequest extends FormRequest
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, string>
|
||||||
|
*/
|
||||||
public function messages(): array
|
public function messages(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ class NotifyTelegramAboutContactMessage implements ShouldQueue
|
|||||||
|
|
||||||
protected string $userAgent;
|
protected string $userAgent;
|
||||||
|
|
||||||
protected string $email;
|
protected ?string $email;
|
||||||
|
|
||||||
protected string $phone;
|
protected ?string $phone;
|
||||||
|
|
||||||
public function __construct(string $name, string $message, string $ip, string $userAgent, ?string $email = null, ?string $phone = null)
|
public function __construct(string $name, string $message, string $ip, string $userAgent, ?string $email = null, ?string $phone = null)
|
||||||
{
|
{
|
||||||
@@ -35,7 +35,7 @@ class NotifyTelegramAboutContactMessage implements ShouldQueue
|
|||||||
$this->phone = $phone;
|
$this->phone = $phone;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handle()
|
public function handle(): void
|
||||||
{
|
{
|
||||||
$email = $this->email ?? '–';
|
$email = $this->email ?? '–';
|
||||||
$phone = $this->phone ?? '–';
|
$phone = $this->phone ?? '–';
|
||||||
|
|||||||
@@ -14,27 +14,30 @@ class NotifyTelegramAboutPersonaliaClick implements ShouldQueue
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
protected $personalia;
|
protected Personalia $personalia;
|
||||||
|
|
||||||
protected $ip;
|
protected ?string $ip;
|
||||||
|
|
||||||
protected $userAgent;
|
protected ?string $userAgent;
|
||||||
|
|
||||||
public function __construct(Personalia $personalia, $ip, $userAgent)
|
public function __construct(Personalia $personalia, ?string $ip, ?string $userAgent)
|
||||||
{
|
{
|
||||||
$this->personalia = $personalia;
|
$this->personalia = $personalia;
|
||||||
$this->ip = $ip;
|
$this->ip = $ip;
|
||||||
$this->userAgent = $userAgent;
|
$this->userAgent = $userAgent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handle()
|
public function handle(): void
|
||||||
{
|
{
|
||||||
|
$ip = $this->ip ?? '–';
|
||||||
|
$userAgent = $this->userAgent ?? '–';
|
||||||
|
|
||||||
$message = <<<TEXT
|
$message = <<<TEXT
|
||||||
👤 *Persoonlijke gegevens bekeken*
|
👤 *Persoonlijke gegevens bekeken*
|
||||||
|
|
||||||
Naam: {$this->personalia->value}
|
Naam: {$this->personalia->value}
|
||||||
IP: {$this->ip}
|
IP: {$ip}
|
||||||
User Agent: `{$this->userAgent}`
|
User Agent: `{$userAgent}`
|
||||||
|
|
||||||
📅 Tijdstip: *{now()->format('d-m-Y H:i')}*
|
📅 Tijdstip: *{now()->format('d-m-Y H:i')}*
|
||||||
TEXT;
|
TEXT;
|
||||||
|
|||||||
@@ -6,10 +6,13 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Spatie\MediaLibrary\HasMedia;
|
use Spatie\MediaLibrary\HasMedia;
|
||||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||||
|
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||||
|
|
||||||
class Education extends Model implements HasMedia
|
class Education extends Model implements HasMedia
|
||||||
{
|
{
|
||||||
|
/** @use HasFactory<\Database\Factories\EducationFactory> */
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
use InteractsWithMedia;
|
use InteractsWithMedia;
|
||||||
|
|
||||||
protected $table = 'education';
|
protected $table = 'education';
|
||||||
@@ -22,12 +25,12 @@ class Education extends Model implements HasMedia
|
|||||||
'beschrijving',
|
'beschrijving',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function image()
|
public function image(): ?Media
|
||||||
{
|
{
|
||||||
return $this->getFirstMedia('image');
|
return $this->getFirstMedia('image');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function imageUrl()
|
public function imageUrl(): ?string
|
||||||
{
|
{
|
||||||
return $this->image() ? $this->image()->getUrl() : null;
|
return $this->image() ? $this->image()->getUrl() : null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Model;
|
|||||||
|
|
||||||
class Personalia extends Model
|
class Personalia extends Model
|
||||||
{
|
{
|
||||||
|
/** @use HasFactory<\Database\Factories\PersonaliaFactory> */
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected $fillable = ['key', 'value', 'hidden', 'icon'];
|
protected $fillable = ['key', 'value', 'hidden', 'icon'];
|
||||||
@@ -16,14 +17,4 @@ class Personalia extends Model
|
|||||||
protected $casts = [
|
protected $casts = [
|
||||||
'hidden' => 'boolean',
|
'hidden' => 'boolean',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function image()
|
|
||||||
{
|
|
||||||
return $this->getFirstMedia('image');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function imageUrl()
|
|
||||||
{
|
|
||||||
return $this->image() ? $this->image()->getUrl() : null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,20 +6,23 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Spatie\MediaLibrary\HasMedia;
|
use Spatie\MediaLibrary\HasMedia;
|
||||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||||
|
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||||
|
|
||||||
class Skill extends Model implements HasMedia
|
class Skill extends Model implements HasMedia
|
||||||
{
|
{
|
||||||
|
/** @use HasFactory<\Database\Factories\SkillFactory> */
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
use InteractsWithMedia;
|
use InteractsWithMedia;
|
||||||
|
|
||||||
protected $fillable = ['title', 'description', 'rating', 'type'];
|
protected $fillable = ['title', 'description', 'rating', 'type'];
|
||||||
|
|
||||||
public function image()
|
public function image(): ?Media
|
||||||
{
|
{
|
||||||
return $this->getFirstMedia('image');
|
return $this->getFirstMedia('image');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function imageUrl()
|
public function imageUrl(): ?string
|
||||||
{
|
{
|
||||||
return $this->image() ? $this->image()->getUrl() : null;
|
return $this->image() ? $this->image()->getUrl() : null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
|
|
||||||
class User extends Authenticatable
|
class User extends Authenticatable implements MustVerifyEmail
|
||||||
{
|
{
|
||||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
/** @use HasFactory<\Database\Factories\UserFactory> */
|
||||||
use HasFactory, Notifiable;
|
use HasFactory, Notifiable;
|
||||||
|
|||||||
@@ -6,10 +6,13 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Spatie\MediaLibrary\HasMedia;
|
use Spatie\MediaLibrary\HasMedia;
|
||||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||||
|
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||||
|
|
||||||
class WorkExperience extends Model implements HasMedia
|
class WorkExperience extends Model implements HasMedia
|
||||||
{
|
{
|
||||||
|
/** @use HasFactory<\Database\Factories\WorkExperienceFactory> */
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
use InteractsWithMedia;
|
use InteractsWithMedia;
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
@@ -20,12 +23,12 @@ class WorkExperience extends Model implements HasMedia
|
|||||||
'beschrijving',
|
'beschrijving',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function image()
|
public function image(): ?Media
|
||||||
{
|
{
|
||||||
return $this->getFirstMedia('image');
|
return $this->getFirstMedia('image');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function imageUrl()
|
public function imageUrl(): ?string
|
||||||
{
|
{
|
||||||
return $this->image() ? $this->image()->getUrl() : null;
|
return $this->image() ? $this->image()->getUrl() : null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"fakerphp/faker": "^1.23",
|
"fakerphp/faker": "^1.23",
|
||||||
|
"larastan/larastan": "^3.10",
|
||||||
"laravel/breeze": "^2.3",
|
"laravel/breeze": "^2.3",
|
||||||
"laravel/pail": "^1.2.2",
|
"laravel/pail": "^1.2.2",
|
||||||
"laravel/pint": "^1.13",
|
"laravel/pint": "^1.13",
|
||||||
@@ -57,6 +58,9 @@
|
|||||||
"test": [
|
"test": [
|
||||||
"@php artisan config:clear --ansi",
|
"@php artisan config:clear --ansi",
|
||||||
"@php artisan test"
|
"@php artisan test"
|
||||||
|
],
|
||||||
|
"analyse": [
|
||||||
|
"phpstan analyse --memory-limit=1G"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
|
|||||||
199
composer.lock
generated
199
composer.lock
generated
@@ -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": "0d27ba8c7d653ba6d0cbd49b2eec5d2d",
|
"content-hash": "2f5e7cf541f786348723b475b25a79c5",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
@@ -6685,6 +6685,47 @@
|
|||||||
},
|
},
|
||||||
"time": "2025-04-30T06:54:44+00:00"
|
"time": "2025-04-30T06:54:44+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "iamcal/sql-parser",
|
||||||
|
"version": "v0.7",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/iamcal/SQLParser.git",
|
||||||
|
"reference": "610392f38de49a44dab08dc1659960a29874c4b8"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/iamcal/SQLParser/zipball/610392f38de49a44dab08dc1659960a29874c4b8",
|
||||||
|
"reference": "610392f38de49a44dab08dc1659960a29874c4b8",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"php-coveralls/php-coveralls": "^1.0",
|
||||||
|
"phpunit/phpunit": "^5|^6|^7|^8|^9"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"iamcal\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Cal Henderson",
|
||||||
|
"email": "cal@iamcal.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "MySQL schema parser",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/iamcal/SQLParser/issues",
|
||||||
|
"source": "https://github.com/iamcal/SQLParser/tree/v0.7"
|
||||||
|
},
|
||||||
|
"time": "2026-01-28T22:20:33+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "jean85/pretty-package-versions",
|
"name": "jean85/pretty-package-versions",
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
@@ -6745,6 +6786,96 @@
|
|||||||
},
|
},
|
||||||
"time": "2025-03-19T14:43:43+00:00"
|
"time": "2025-03-19T14:43:43+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "larastan/larastan",
|
||||||
|
"version": "v3.10.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/larastan/larastan.git",
|
||||||
|
"reference": "2970f83398154178a739609c244577267c7ee8eb"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/larastan/larastan/zipball/2970f83398154178a739609c244577267c7ee8eb",
|
||||||
|
"reference": "2970f83398154178a739609c244577267c7ee8eb",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-json": "*",
|
||||||
|
"iamcal/sql-parser": "^0.7.0",
|
||||||
|
"illuminate/console": "^11.44.2 || ^12.4.1 || ^13",
|
||||||
|
"illuminate/container": "^11.44.2 || ^12.4.1 || ^13",
|
||||||
|
"illuminate/contracts": "^11.44.2 || ^12.4.1 || ^13",
|
||||||
|
"illuminate/database": "^11.44.2 || ^12.4.1 || ^13",
|
||||||
|
"illuminate/http": "^11.44.2 || ^12.4.1 || ^13",
|
||||||
|
"illuminate/pipeline": "^11.44.2 || ^12.4.1 || ^13",
|
||||||
|
"illuminate/support": "^11.44.2 || ^12.4.1 || ^13",
|
||||||
|
"php": "^8.2",
|
||||||
|
"phpstan/phpstan": "^2.2.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"doctrine/coding-standard": "^14",
|
||||||
|
"laravel/framework": "^11.44.2 || ^12.7.2 || ^13",
|
||||||
|
"mockery/mockery": "^1.6.12",
|
||||||
|
"nikic/php-parser": "^5.4",
|
||||||
|
"orchestra/canvas": "^v9.2.2 || ^10.0.1 || ^11",
|
||||||
|
"orchestra/testbench-core": "^9.12.0 || ^10.1 || ^11",
|
||||||
|
"phpstan/phpstan-deprecation-rules": "^2.0.1",
|
||||||
|
"phpunit/phpunit": "^10.5.35 || ^11.5.15 || ^12.5.8 || ^13.1.8"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"orchestra/testbench": "Using Larastan for analysing a package needs Testbench",
|
||||||
|
"phpmyadmin/sql-parser": "Install to enable Larastan's optional phpMyAdmin-based SQL parser automatically"
|
||||||
|
},
|
||||||
|
"type": "phpstan-extension",
|
||||||
|
"extra": {
|
||||||
|
"phpstan": {
|
||||||
|
"includes": [
|
||||||
|
"extension.neon"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "3.0-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Larastan\\Larastan\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Can Vural",
|
||||||
|
"email": "can9119@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel",
|
||||||
|
"keywords": [
|
||||||
|
"PHPStan",
|
||||||
|
"code analyse",
|
||||||
|
"code analysis",
|
||||||
|
"larastan",
|
||||||
|
"laravel",
|
||||||
|
"package",
|
||||||
|
"php",
|
||||||
|
"static analysis"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/larastan/larastan/issues",
|
||||||
|
"source": "https://github.com/larastan/larastan/tree/v3.10.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/canvural",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2026-05-28T08:00:58+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "laravel/breeze",
|
"name": "laravel/breeze",
|
||||||
"version": "v2.3.7",
|
"version": "v2.3.7",
|
||||||
@@ -7994,6 +8125,70 @@
|
|||||||
},
|
},
|
||||||
"time": "2025-02-19T13:28:12+00:00"
|
"time": "2025-02-19T13:28:12+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "phpstan/phpstan",
|
||||||
|
"version": "2.2.1",
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/dea9c8f2d25cc849391042b71e429c1a4bf82660",
|
||||||
|
"reference": "dea9c8f2d25cc849391042b71e429c1a4bf82660",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.4|^8.0"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"phpstan/phpstan-shim": "*"
|
||||||
|
},
|
||||||
|
"bin": [
|
||||||
|
"phpstan",
|
||||||
|
"phpstan.phar"
|
||||||
|
],
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Ondřej Mirtes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Markus Staab"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Vincent Langlet"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PHPStan - PHP Static Analysis Tool",
|
||||||
|
"keywords": [
|
||||||
|
"dev",
|
||||||
|
"static analysis"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"docs": "https://phpstan.org/user-guide/getting-started",
|
||||||
|
"forum": "https://github.com/phpstan/phpstan/discussions",
|
||||||
|
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||||
|
"security": "https://github.com/phpstan/phpstan/security/policy",
|
||||||
|
"source": "https://github.com/phpstan/phpstan-src"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/ondrejmirtes",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/phpstan",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2026-05-28T14:44:12+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-code-coverage",
|
"name": "phpunit/php-code-coverage",
|
||||||
"version": "11.0.10",
|
"version": "11.0.10",
|
||||||
@@ -9611,5 +9806,5 @@
|
|||||||
"php": "^8.2"
|
"php": "^8.2"
|
||||||
},
|
},
|
||||||
"platform-dev": {},
|
"platform-dev": {},
|
||||||
"plugin-api-version": "2.6.0"
|
"plugin-api-version": "2.9.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ use Illuminate\Database\Eloquent\Factories\Factory;
|
|||||||
*/
|
*/
|
||||||
class EducationFactory extends Factory
|
class EducationFactory extends Factory
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
public function definition(): array
|
public function definition(): array
|
||||||
{
|
{
|
||||||
$startDate = fake()->dateTimeBetween('-8 years', '-2 years');
|
$startDate = fake()->dateTimeBetween('-8 years', '-2 years');
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ use Illuminate\Database\Eloquent\Factories\Factory;
|
|||||||
*/
|
*/
|
||||||
class PersonaliaFactory extends Factory
|
class PersonaliaFactory extends Factory
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
public function definition(): array
|
public function definition(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ use Illuminate\Database\Eloquent\Factories\Factory;
|
|||||||
*/
|
*/
|
||||||
class SkillFactory extends Factory
|
class SkillFactory extends Factory
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
public function definition(): array
|
public function definition(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ use Illuminate\Database\Eloquent\Factories\Factory;
|
|||||||
*/
|
*/
|
||||||
class WorkExperienceFactory extends Factory
|
class WorkExperienceFactory extends Factory
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
public function definition(): array
|
public function definition(): array
|
||||||
{
|
{
|
||||||
$startDate = fake()->dateTimeBetween('-8 years', '-1 year');
|
$startDate = fake()->dateTimeBetween('-8 years', '-1 year');
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ return new class extends Migration
|
|||||||
/**
|
/**
|
||||||
* Run the migrations.
|
* Run the migrations.
|
||||||
*/
|
*/
|
||||||
public function up()
|
public function up(): void
|
||||||
{
|
{
|
||||||
Schema::table('skills', function (Blueprint $table) {
|
Schema::table('skills', function (Blueprint $table) {
|
||||||
$table->string('type')->default('rating')->after('id');
|
$table->string('type')->default('rating')->after('id');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function down()
|
public function down(): void
|
||||||
{
|
{
|
||||||
Schema::table('skills', function (Blueprint $table) {
|
Schema::table('skills', function (Blueprint $table) {
|
||||||
$table->dropColumn('type');
|
$table->dropColumn('type');
|
||||||
|
|||||||
11
phpstan.neon
Normal file
11
phpstan.neon
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
includes:
|
||||||
|
- vendor/larastan/larastan/extension.neon
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
level: 7
|
||||||
|
paths:
|
||||||
|
- app
|
||||||
|
- database
|
||||||
|
- routes
|
||||||
|
|
||||||
|
tmpDir: storage/framework/phpstan
|
||||||
2
resources/images/sitiweb.svg
Normal file
2
resources/images/sitiweb.svg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.1" id="svg2" xml:space="preserve" viewBox="0 0 406.66666 473.33334" sodipodi:docname="Beeldmerk.ai"><metadata id="metadata8"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/></cc:Work></rdf:RDF></metadata><defs id="defs6"/><sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="640" inkscape:window-height="480" id="namedview4"/><g id="g10" inkscape:groupmode="layer" inkscape:label="Beeldmerk" transform="matrix(1.3333333,0,0,-1.3333333,0,473.33333)"><g id="g12" transform="translate(192.9405,297.8561)"><path d="M 0,0 3.661,-204.853 95.917,-260.941 95.291,47.32 Z" style="fill:#009000;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path14"/></g><g id="g16" transform="translate(287.7484,238.091)"><path d="M 0,0 -183.898,-87.583 -275.154,-31.495 1.239,108.114 Z" style="fill:#00ab00;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path18"/></g><g id="g20" transform="translate(172.7637,52.3648)"><path d="M 0,0 -93.429,-43.886 -96.399,59 -2.97,102.886 Z" style="fill:#ee0004;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path22"/></g><g id="g24" transform="translate(169.794,155.2513)"><path d="m 0,0 2.97,-102.886 -93.429,-43.887" style="fill:#d8000e;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path26"/></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -134,7 +134,7 @@
|
|||||||
© {{ date('Y') }} Roberto Guagliardo. Alle rechten voorbehouden.
|
© {{ date('Y') }} Roberto Guagliardo. Alle rechten voorbehouden.
|
||||||
</footer>
|
</footer>
|
||||||
<div id="custom-cursor">
|
<div id="custom-cursor">
|
||||||
{!! file_get_contents(public_path('storage/sitiweb.svg')) !!}
|
{!! file_get_contents(resource_path('images/sitiweb.svg')) !!}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ use App\Http\Controllers\SkillController;
|
|||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
Route::get('/', [FrontendController::class, 'index'])->name('home');
|
Route::get('/', [FrontendController::class, 'index'])->name('home');
|
||||||
Route::get('/dashboard', function () {
|
Route::get('/dashboard', function (): \Illuminate\View\View {
|
||||||
return view('dashboard');
|
return view('dashboard');
|
||||||
})->middleware(['auth', 'verified'])->name('dashboard');
|
})->middleware(['auth', 'verified'])->name('dashboard');
|
||||||
Route::get('/getPersonalia/{id}', [FrontendController::class, 'getPersonalia'])->name('personalia');
|
Route::get('/getPersonalia/{personalia}', [FrontendController::class, 'getPersonalia'])->name('personalia');
|
||||||
Route::post('/contact', [FrontendController::class, 'message'])->name('contact');
|
Route::post('/contact', [FrontendController::class, 'message'])->name('contact');
|
||||||
|
|
||||||
Route::middleware('auth')->group(function () {
|
Route::middleware('auth')->group(function () {
|
||||||
@@ -19,10 +19,10 @@ Route::middleware('auth')->group(function () {
|
|||||||
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
|
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
|
||||||
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
|
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
|
||||||
|
|
||||||
Route::resource('work-experiences', \App\Http\Controllers\WorkExperienceController::class);
|
Route::resource('work-experiences', \App\Http\Controllers\WorkExperienceController::class)->except(['show']);
|
||||||
Route::resource('skills', SkillController::class);
|
Route::resource('skills', SkillController::class)->except(['show']);
|
||||||
Route::resource('personalia', PersonaliaController::class);
|
Route::resource('personalia', PersonaliaController::class)->except(['show']);
|
||||||
Route::resource('educations', EducationController::class);
|
Route::resource('educations', EducationController::class)->except(['show']);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ test('the homepage shows the public cv data', function () {
|
|||||||
->assertViewHas('personalia', fn ($personalia) => $personalia->contains($personalium))
|
->assertViewHas('personalia', fn ($personalia) => $personalia->contains($personalium))
|
||||||
->assertViewHas('education', fn ($educations) => $educations->contains($education))
|
->assertViewHas('education', fn ($educations) => $educations->contains($education))
|
||||||
->assertViewHas('experience', fn ($experiences) => $experiences->contains($experience));
|
->assertViewHas('experience', fn ($experiences) => $experiences->contains($experience));
|
||||||
})->skip('Homepage currently depends on missing public/storage/sitiweb.svg.');
|
});
|
||||||
|
|
||||||
test('a hidden personalia value can be requested and the click is queued for notification', function () {
|
test('a hidden personalia value can be requested and the click is queued for notification', function () {
|
||||||
Queue::fake();
|
Queue::fake();
|
||||||
|
|||||||
@@ -88,11 +88,12 @@ test('an authenticated user can update personalia', function () {
|
|||||||
->assertSessionHasNoErrors()
|
->assertSessionHasNoErrors()
|
||||||
->assertRedirect(route('personalia.index'));
|
->assertRedirect(route('personalia.index'));
|
||||||
|
|
||||||
expect($personalium->refresh())
|
$personalium->refresh();
|
||||||
->value->toBe('new@example.com')
|
|
||||||
->hidden->toBeFalse()
|
expect($personalium->value)->toBe('new@example.com');
|
||||||
->icon->toBe('fa-regular fa-envelope');
|
expect($personalium->hidden)->toBeFalse();
|
||||||
})->skip('PersonaliaController::update currently uses Request instead of PersonaliaRequest.');
|
expect($personalium->icon)->toBe('fa-regular fa-envelope');
|
||||||
|
});
|
||||||
|
|
||||||
test('an authenticated user can delete personalia', function () {
|
test('an authenticated user can delete personalia', function () {
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
@@ -104,4 +105,4 @@ test('an authenticated user can delete personalia', function () {
|
|||||||
->assertRedirect(route('personalia.index'));
|
->assertRedirect(route('personalia.index'));
|
||||||
|
|
||||||
$this->assertDatabaseMissing('personalia', ['id' => $personalium->id]);
|
$this->assertDatabaseMissing('personalia', ['id' => $personalium->id]);
|
||||||
})->skip('PersonaliaController::destroy currently does not match the resource route parameter binding.');
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user