Compare commits
13 Commits
53c4823b22
...
bugfixes/f
| Author | SHA1 | Date | |
|---|---|---|---|
| 2f2b7866c6 | |||
| f85b596a66 | |||
| 9b8c273c21 | |||
| bbfc64031f | |||
| fdc10a0acb | |||
| c60738b532 | |||
| 2034814939 | |||
| 38943743aa | |||
| 866118a86f | |||
| 6c5d0e4a9b | |||
| 886e3993fc | |||
| aab8d33b8d | |||
| eb9c8796de |
@@ -25,10 +25,28 @@ jobs:
|
|||||||
- name: Install Composer dependencies
|
- name: Install Composer dependencies
|
||||||
uses: ramsey/composer-install@v3
|
uses: ramsey/composer-install@v3
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '22'
|
||||||
|
cache: npm
|
||||||
|
|
||||||
|
- name: Install NPM dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
- name: Prepare application
|
- name: Prepare application
|
||||||
run: |
|
run: |
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
php artisan key:generate --ansi
|
php artisan key:generate --ansi
|
||||||
|
|
||||||
|
- name: Check PHP formatting
|
||||||
|
run: composer format:check
|
||||||
|
|
||||||
|
- name: Check Blade formatting
|
||||||
|
run: npm run format:check
|
||||||
|
|
||||||
- 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,12 +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 edit(Education $education)
|
public function edit(Education $education): View
|
||||||
{
|
{
|
||||||
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());
|
||||||
|
|
||||||
@@ -43,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();
|
||||||
|
|||||||
@@ -2,17 +2,21 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Requests\PageVisitRequest;
|
||||||
use App\Jobs\NotifyTelegramAboutContactMessage;
|
use App\Jobs\NotifyTelegramAboutContactMessage;
|
||||||
|
use App\Jobs\NotifyTelegramAboutPageVisit;
|
||||||
use App\Jobs\NotifyTelegramAboutPersonaliaClick;
|
use App\Jobs\NotifyTelegramAboutPersonaliaClick;
|
||||||
use App\Models\Education;
|
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 +27,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',
|
||||||
@@ -57,4 +60,16 @@ class FrontendController extends Controller
|
|||||||
|
|
||||||
return response()->json(['status' => 'success']);
|
return response()->json(['status' => 'success']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function pageVisit(PageVisitRequest $request): JsonResponse
|
||||||
|
{
|
||||||
|
NotifyTelegramAboutPageVisit::dispatch(
|
||||||
|
$request->validated(),
|
||||||
|
$request->ip(),
|
||||||
|
$request->userAgent(),
|
||||||
|
$request->header('Accept-Language')
|
||||||
|
);
|
||||||
|
|
||||||
|
return response()->json(['status' => 'queued']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,24 +2,26 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\Personalia;
|
|
||||||
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();
|
||||||
|
|
||||||
@@ -31,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(PersonaliaRequest $request, Personalia $personalium)
|
public function update(PersonaliaRequest $request, Personalia $personalium): RedirectResponse
|
||||||
{
|
{
|
||||||
$validated = $request->validated();
|
$validated = $request->validated();
|
||||||
|
|
||||||
@@ -45,10 +47,11 @@ 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 $personalium)
|
public function destroy(Personalia $personalium): RedirectResponse
|
||||||
{
|
{
|
||||||
$personalium->delete();
|
$personalium->delete();
|
||||||
|
|
||||||
|
|||||||
@@ -2,23 +2,26 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\Skill;
|
|
||||||
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());
|
||||||
|
|
||||||
@@ -29,12 +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 edit(Skill $skill)
|
public function edit(Skill $skill): View
|
||||||
{
|
{
|
||||||
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());
|
||||||
|
|
||||||
@@ -46,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,24 +2,26 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\WorkExperience;
|
|
||||||
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());
|
||||||
|
|
||||||
@@ -30,12 +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 edit(WorkExperience $workExperience)
|
public function edit(WorkExperience $workExperience): View
|
||||||
{
|
{
|
||||||
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());
|
||||||
|
|
||||||
@@ -47,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 [
|
||||||
|
|||||||
43
app/Http/Requests/PageVisitRequest.php
Normal file
43
app/Http/Requests/PageVisitRequest.php
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class PageVisitRequest extends FormRequest
|
||||||
|
{
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, list<string>>
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'visitor_id' => ['required', 'string', 'max:100'],
|
||||||
|
'url' => ['required', 'url', 'max:2048'],
|
||||||
|
'path' => ['nullable', 'string', 'max:2048'],
|
||||||
|
'title' => ['nullable', 'string', 'max:255'],
|
||||||
|
'referrer' => ['nullable', 'string', 'max:2048'],
|
||||||
|
'language' => ['nullable', 'string', 'max:50'],
|
||||||
|
'timezone' => ['nullable', 'string', 'max:100'],
|
||||||
|
'screen' => ['nullable', 'array'],
|
||||||
|
'screen.width' => ['nullable', 'integer', 'min:0', 'max:100000'],
|
||||||
|
'screen.height' => ['nullable', 'integer', 'min:0', 'max:100000'],
|
||||||
|
'viewport' => ['nullable', 'array'],
|
||||||
|
'viewport.width' => ['nullable', 'integer', 'min:0', 'max:100000'],
|
||||||
|
'viewport.height' => ['nullable', 'integer', 'min:0', 'max:100000'],
|
||||||
|
'device_pixel_ratio' => ['nullable', 'numeric', 'min:0', 'max:100'],
|
||||||
|
'color_depth' => ['nullable', 'integer', 'min:0', 'max:1000'],
|
||||||
|
'platform' => ['nullable', 'string', 'max:255'],
|
||||||
|
'vendor' => ['nullable', 'string', 'max:255'],
|
||||||
|
'hardware_concurrency' => ['nullable', 'integer', 'min:0', 'max:1024'],
|
||||||
|
'device_memory' => ['nullable', 'numeric', 'min:0', 'max:1024'],
|
||||||
|
'cookies_enabled' => ['nullable', 'boolean'],
|
||||||
|
'online' => ['nullable', 'boolean'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 [
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Services\TelegramNotificationService;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Facades\Http;
|
|
||||||
|
|
||||||
class NotifyTelegramAboutContactMessage implements ShouldQueue
|
class NotifyTelegramAboutContactMessage implements ShouldQueue
|
||||||
{
|
{
|
||||||
@@ -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,30 +35,15 @@ class NotifyTelegramAboutContactMessage implements ShouldQueue
|
|||||||
$this->phone = $phone;
|
$this->phone = $phone;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handle()
|
public function handle(TelegramNotificationService $telegram): void
|
||||||
{
|
{
|
||||||
$email = $this->email ?? '–';
|
$telegram->notifyContactMessage(
|
||||||
$phone = $this->phone ?? '–';
|
$this->name,
|
||||||
|
$this->message,
|
||||||
$text = <<<TEXT
|
$this->ip,
|
||||||
📩 *Nieuw contactbericht ontvangen*
|
$this->userAgent,
|
||||||
|
$this->email,
|
||||||
👤 Naam: *{$this->name}*
|
$this->phone
|
||||||
|
);
|
||||||
💬 Bericht:
|
|
||||||
{$this->message}
|
|
||||||
📧 Email: {$email}
|
|
||||||
📱 Telefoon: {$phone}
|
|
||||||
🌐 IP: {$this->ip}
|
|
||||||
🧭 User Agent: `{$this->userAgent}`
|
|
||||||
|
|
||||||
🕒 Tijdstip: *{now()->format('d-m-Y H:i')}*
|
|
||||||
TEXT;
|
|
||||||
|
|
||||||
Http::post('https://api.telegram.org/bot'.config('services.telegram.bot_token').'/sendMessage', [
|
|
||||||
'chat_id' => config('services.telegram.chat_id'),
|
|
||||||
'text' => $text,
|
|
||||||
'parse_mode' => 'Markdown',
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
30
app/Jobs/NotifyTelegramAboutPageVisit.php
Normal file
30
app/Jobs/NotifyTelegramAboutPageVisit.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Services\TelegramNotificationService;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class NotifyTelegramAboutPageVisit implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $visit
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
protected array $visit,
|
||||||
|
protected ?string $ip,
|
||||||
|
protected ?string $userAgent,
|
||||||
|
protected ?string $acceptLanguage
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function handle(TelegramNotificationService $telegram): void
|
||||||
|
{
|
||||||
|
$telegram->notifyPageVisit($this->visit, $this->ip, $this->userAgent, $this->acceptLanguage);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,46 +3,32 @@
|
|||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Models\Personalia;
|
use App\Models\Personalia;
|
||||||
|
use App\Services\TelegramNotificationService;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Facades\Http;
|
|
||||||
|
|
||||||
class NotifyTelegramAboutPersonaliaClick implements ShouldQueue
|
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(TelegramNotificationService $telegram): void
|
||||||
{
|
{
|
||||||
$message = <<<TEXT
|
$telegram->notifyPersonaliaClick($this->personalia, $this->ip, $this->userAgent);
|
||||||
👤 *Persoonlijke gegevens bekeken*
|
|
||||||
|
|
||||||
Naam: {$this->personalia->value}
|
|
||||||
IP: {$this->ip}
|
|
||||||
User Agent: `{$this->userAgent}`
|
|
||||||
|
|
||||||
📅 Tijdstip: *{now()->format('d-m-Y H:i')}*
|
|
||||||
TEXT;
|
|
||||||
|
|
||||||
Http::post('https://api.telegram.org/bot'.config('services.telegram.bot_token').'/sendMessage', [
|
|
||||||
'chat_id' => config('services.telegram.chat_id'),
|
|
||||||
'text' => $message,
|
|
||||||
'parse_mode' => 'Markdown',
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
146
app/Services/TelegramNotificationService.php
Normal file
146
app/Services/TelegramNotificationService.php
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use App\Models\Personalia;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
|
class TelegramNotificationService
|
||||||
|
{
|
||||||
|
public function notifyContactMessage(
|
||||||
|
string $name,
|
||||||
|
string $message,
|
||||||
|
string $ip,
|
||||||
|
string $userAgent,
|
||||||
|
?string $email = null,
|
||||||
|
?string $phone = null
|
||||||
|
): void {
|
||||||
|
$email ??= '-';
|
||||||
|
$phone ??= '-';
|
||||||
|
|
||||||
|
$text = <<<TEXT
|
||||||
|
Nieuw contactbericht ontvangen
|
||||||
|
|
||||||
|
Naam: {$name}
|
||||||
|
Email: {$email}
|
||||||
|
Telefoon: {$phone}
|
||||||
|
IP: {$ip}
|
||||||
|
User Agent: {$userAgent}
|
||||||
|
|
||||||
|
Bericht:
|
||||||
|
{$message}
|
||||||
|
|
||||||
|
Tijdstip: {now()->format('d-m-Y H:i')}
|
||||||
|
TEXT;
|
||||||
|
|
||||||
|
$this->send($text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function notifyPersonaliaClick(Personalia $personalia, ?string $ip, ?string $userAgent): void
|
||||||
|
{
|
||||||
|
$ip ??= '-';
|
||||||
|
$userAgent ??= '-';
|
||||||
|
|
||||||
|
$message = <<<TEXT
|
||||||
|
Persoonlijke gegevens bekeken
|
||||||
|
|
||||||
|
Naam: {$personalia->value}
|
||||||
|
IP: {$ip}
|
||||||
|
User Agent: {$userAgent}
|
||||||
|
|
||||||
|
Tijdstip: {now()->format('d-m-Y H:i')}
|
||||||
|
TEXT;
|
||||||
|
|
||||||
|
$this->send($message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $visit
|
||||||
|
*/
|
||||||
|
public function notifyPageVisit(array $visit, ?string $ip, ?string $userAgent, ?string $acceptLanguage): void
|
||||||
|
{
|
||||||
|
$screen = $this->formatDimensions($visit['screen'] ?? null);
|
||||||
|
$viewport = $this->formatDimensions($visit['viewport'] ?? null);
|
||||||
|
|
||||||
|
$message = <<<TEXT
|
||||||
|
Pagina bezocht
|
||||||
|
|
||||||
|
URL: {$this->value($visit['url'] ?? null)}
|
||||||
|
Pad: {$this->value($visit['path'] ?? null)}
|
||||||
|
Titel: {$this->value($visit['title'] ?? null)}
|
||||||
|
Referrer: {$this->value($visit['referrer'] ?? null)}
|
||||||
|
|
||||||
|
Visitor ID: {$this->value($visit['visitor_id'] ?? null)}
|
||||||
|
IP: {$this->value($ip)}
|
||||||
|
User Agent: {$this->value($userAgent)}
|
||||||
|
Accept-Language: {$this->value($acceptLanguage)}
|
||||||
|
Browser taal: {$this->value($visit['language'] ?? null)}
|
||||||
|
Timezone: {$this->value($visit['timezone'] ?? null)}
|
||||||
|
Platform: {$this->value($visit['platform'] ?? null)}
|
||||||
|
Vendor: {$this->value($visit['vendor'] ?? null)}
|
||||||
|
Scherm: {$screen}
|
||||||
|
Viewport: {$viewport}
|
||||||
|
DPR: {$this->value($visit['device_pixel_ratio'] ?? null)}
|
||||||
|
Color depth: {$this->value($visit['color_depth'] ?? null)}
|
||||||
|
CPU cores: {$this->value($visit['hardware_concurrency'] ?? null)}
|
||||||
|
Device memory: {$this->value($visit['device_memory'] ?? null)}
|
||||||
|
Cookies: {$this->boolean($visit['cookies_enabled'] ?? null)}
|
||||||
|
Online: {$this->boolean($visit['online'] ?? null)}
|
||||||
|
|
||||||
|
Tijdstip: {now()->format('d-m-Y H:i')}
|
||||||
|
TEXT;
|
||||||
|
|
||||||
|
$this->send($message);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function send(string $text): void
|
||||||
|
{
|
||||||
|
$botToken = config('services.telegram.bot_token');
|
||||||
|
$chatId = config('services.telegram.chat_id');
|
||||||
|
|
||||||
|
if (! is_string($botToken) || $botToken === '' || ! is_string($chatId) || $chatId === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Http::post("https://api.telegram.org/bot{$botToken}/sendMessage", [
|
||||||
|
'chat_id' => $chatId,
|
||||||
|
'text' => $text,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function value(mixed $value): string
|
||||||
|
{
|
||||||
|
if (is_bool($value)) {
|
||||||
|
return $this->boolean($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_scalar($value)) {
|
||||||
|
$value = trim((string) $value);
|
||||||
|
|
||||||
|
return $value !== '' ? $value : '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function boolean(mixed $value): string
|
||||||
|
{
|
||||||
|
return match ($value) {
|
||||||
|
true => 'ja',
|
||||||
|
false => 'nee',
|
||||||
|
default => '-',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function formatDimensions(mixed $dimensions): string
|
||||||
|
{
|
||||||
|
if (! is_array($dimensions)) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
$width = $this->value($dimensions['width'] ?? null);
|
||||||
|
$height = $this->value($dimensions['height'] ?? null);
|
||||||
|
|
||||||
|
return "{$width}x{$height}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,15 @@
|
|||||||
"test": [
|
"test": [
|
||||||
"@php artisan config:clear --ansi",
|
"@php artisan config:clear --ansi",
|
||||||
"@php artisan test"
|
"@php artisan test"
|
||||||
|
],
|
||||||
|
"analyse": [
|
||||||
|
"phpstan analyse --memory-limit=1G"
|
||||||
|
],
|
||||||
|
"format": [
|
||||||
|
"pint"
|
||||||
|
],
|
||||||
|
"format:check": [
|
||||||
|
"pint --test"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"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');
|
||||||
|
|||||||
600
package-lock.json
generated
600
package-lock.json
generated
@@ -13,6 +13,7 @@
|
|||||||
"alpinejs": "^3.4.2",
|
"alpinejs": "^3.4.2",
|
||||||
"autoprefixer": "^10.4.2",
|
"autoprefixer": "^10.4.2",
|
||||||
"axios": "^1.8.2",
|
"axios": "^1.8.2",
|
||||||
|
"blade-formatter": "^1.44.4",
|
||||||
"concurrently": "^9.0.1",
|
"concurrently": "^9.0.1",
|
||||||
"laravel-vite-plugin": "^1.2.0",
|
"laravel-vite-plugin": "^1.2.0",
|
||||||
"postcss": "^8.4.31",
|
"postcss": "^8.4.31",
|
||||||
@@ -47,6 +48,19 @@
|
|||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/runtime-corejs3": {
|
||||||
|
"version": "7.29.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.29.7.tgz",
|
||||||
|
"integrity": "sha512-ppj9ouYku+RX0ljtgZd+KMO5mkM2bCqg8H2PYAFWnLsHEIKIdRojqbJ2i3eVHrisuxy7nOFCmngTDdWtUCdXUQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"core-js-pure": "^3.48.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
"version": "0.25.5",
|
"version": "0.25.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
|
||||||
@@ -594,6 +608,13 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@one-ini/wasm": {
|
||||||
|
"version": "0.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz",
|
||||||
|
"integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@pkgjs/parseargs": {
|
"node_modules/@pkgjs/parseargs": {
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||||
@@ -605,6 +626,20 @@
|
|||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@prettier/plugin-php": {
|
||||||
|
"version": "0.24.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prettier/plugin-php/-/plugin-php-0.24.0.tgz",
|
||||||
|
"integrity": "sha512-x9l65fCE/pgoET6RQowgdgG8Xmzs44z6j6Hhg3coINCyCw9JBGJ5ZzMR2XHAM2jmAdbJAIgqB6cUn4/3W3XLTA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"linguist-languages": "^8.0.0",
|
||||||
|
"php-parser": "^3.2.5"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"prettier": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
"version": "4.43.0",
|
"version": "4.43.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.43.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.43.0.tgz",
|
||||||
@@ -885,6 +920,21 @@
|
|||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@shufo/tailwindcss-class-sorter": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@shufo/tailwindcss-class-sorter/-/tailwindcss-class-sorter-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-y9SMobvwElX2G6vdg4odJ6UL6hu/o5RlMsdwEeDLGaqHU3BLSw9CeitGgBus6kadjjDdT2wseG0Tl5yXWdc4UQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"escalade": "^3.1.1",
|
||||||
|
"object-hash": "^3.0.0",
|
||||||
|
"tailwindcss": "^3.3.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tailwindcss/forms": {
|
"node_modules/@tailwindcss/forms": {
|
||||||
"version": "0.5.10",
|
"version": "0.5.10",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz",
|
||||||
@@ -1213,6 +1263,50 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/abbrev": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/aigle": {
|
||||||
|
"version": "1.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/aigle/-/aigle-1.14.1.tgz",
|
||||||
|
"integrity": "sha512-bCmQ65CEebspmpbWFs6ab3S27TNyVH1b5MledX8KoiGxUhsJmPUUGpaoSijhwawNnq5Lt8jbcq7Z7gUAD0nuTw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"aigle-core": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/aigle-core": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/aigle-core/-/aigle-core-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-uGFWPumk5DLvYnUphNnff+kWC8VeAnjPbbU8ovsSHflKXGX77SD7cAN/aSBCLX3xnoJAM9KdtRgxUygRnSSu7A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/ajv": {
|
||||||
|
"version": "8.20.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
|
||||||
|
"integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"fast-uri": "^3.0.1",
|
||||||
|
"json-schema-traverse": "^1.0.0",
|
||||||
|
"require-from-string": "^2.0.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/epoberezkin"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/alpinejs": {
|
"node_modules/alpinejs": {
|
||||||
"version": "3.14.9",
|
"version": "3.14.9",
|
||||||
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.14.9.tgz",
|
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.14.9.tgz",
|
||||||
@@ -1357,6 +1451,126 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/blade-formatter": {
|
||||||
|
"version": "1.44.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/blade-formatter/-/blade-formatter-1.44.4.tgz",
|
||||||
|
"integrity": "sha512-+A7hdXQwIZ21+qWXQPgz+hJKtnjG2iN0Ay2m/E/4zQ5cvdFykTjXXGqae91ZmcKez1x0CSdS80WNObamTwJgFg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@prettier/plugin-php": "^0.24.0",
|
||||||
|
"@shufo/tailwindcss-class-sorter": "3.0.1",
|
||||||
|
"aigle": "^1.14.1",
|
||||||
|
"ajv": "^8.9.0",
|
||||||
|
"chalk": "^4.1.0",
|
||||||
|
"concat-stream": "^2.0.0",
|
||||||
|
"detect-indent": "^6.0.0",
|
||||||
|
"find-config": "^1.0.0",
|
||||||
|
"glob": "^13.0.0",
|
||||||
|
"html-attribute-sorter": "^0.4.3",
|
||||||
|
"ignore": "^6.0.0",
|
||||||
|
"js-beautify": "^1.15.4",
|
||||||
|
"lodash": "^4.17.19",
|
||||||
|
"php-parser": "3.5.0",
|
||||||
|
"prettier": "^3.2.5",
|
||||||
|
"string-replace-async": "^2.0.0",
|
||||||
|
"tailwindcss": "^3.1.8",
|
||||||
|
"vscode-oniguruma": "1.7.0",
|
||||||
|
"vscode-textmate": "^7.0.1",
|
||||||
|
"xregexp": "^5.0.1",
|
||||||
|
"yargs": "^17.3.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"blade-formatter": "bin/blade-formatter.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/blade-formatter/node_modules/balanced-match": {
|
||||||
|
"version": "4.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
||||||
|
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "18 || 20 || >=22"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/blade-formatter/node_modules/brace-expansion": {
|
||||||
|
"version": "5.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
|
||||||
|
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^4.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "18 || 20 || >=22"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/blade-formatter/node_modules/glob": {
|
||||||
|
"version": "13.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
|
||||||
|
"integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BlueOak-1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"minimatch": "^10.2.2",
|
||||||
|
"minipass": "^7.1.3",
|
||||||
|
"path-scurry": "^2.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "18 || 20 || >=22"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/blade-formatter/node_modules/lru-cache": {
|
||||||
|
"version": "11.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz",
|
||||||
|
"integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BlueOak-1.0.0",
|
||||||
|
"engines": {
|
||||||
|
"node": "20 || >=22"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/blade-formatter/node_modules/minimatch": {
|
||||||
|
"version": "10.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
|
||||||
|
"integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BlueOak-1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^5.0.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "18 || 20 || >=22"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/blade-formatter/node_modules/path-scurry": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BlueOak-1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"lru-cache": "^11.0.0",
|
||||||
|
"minipass": "^7.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "18 || 20 || >=22"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||||
@@ -1413,6 +1627,13 @@
|
|||||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/buffer-from": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/call-bind-apply-helpers": {
|
"node_modules/call-bind-apply-helpers": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||||
@@ -1657,6 +1878,22 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/concat-stream": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": [
|
||||||
|
"node >= 6.0"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"buffer-from": "^1.0.0",
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"readable-stream": "^3.0.2",
|
||||||
|
"typedarray": "^0.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/concurrently": {
|
"node_modules/concurrently": {
|
||||||
"version": "9.1.2",
|
"version": "9.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.1.2.tgz",
|
||||||
@@ -1683,6 +1920,29 @@
|
|||||||
"url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
|
"url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/config-chain": {
|
||||||
|
"version": "1.1.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
|
||||||
|
"integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ini": "^1.3.4",
|
||||||
|
"proto-list": "~1.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/core-js-pure": {
|
||||||
|
"version": "3.49.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.49.0.tgz",
|
||||||
|
"integrity": "sha512-XM4RFka59xATyJv/cS3O3Kml72hQXUeGRuuTmMYFxwzc9/7C8OYTaIR/Ji+Yt8DXzsFLNhat15cE/JP15HrCgw==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/core-js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.6",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
@@ -1721,6 +1981,16 @@
|
|||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/detect-indent": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/detect-libc": {
|
"node_modules/detect-libc": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
|
||||||
@@ -1767,6 +2037,35 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/editorconfig": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@one-ini/wasm": "0.1.1",
|
||||||
|
"commander": "^10.0.0",
|
||||||
|
"minimatch": "^9.0.1",
|
||||||
|
"semver": "^7.5.3"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"editorconfig": "bin/editorconfig"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/editorconfig/node_modules/commander": {
|
||||||
|
"version": "10.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
|
||||||
|
"integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.5.170",
|
"version": "1.5.170",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.170.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.170.tgz",
|
||||||
@@ -1895,6 +2194,13 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-deep-equal": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/fast-glob": {
|
"node_modules/fast-glob": {
|
||||||
"version": "3.3.3",
|
"version": "3.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||||
@@ -1925,6 +2231,23 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-uri": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/fastify"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/fastify"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/fastq": {
|
"node_modules/fastq": {
|
||||||
"version": "1.19.1",
|
"version": "1.19.1",
|
||||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
|
||||||
@@ -1948,6 +2271,19 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/find-config": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-config/-/find-config-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-Z+suHH+7LSE40WfUeZPIxSxypCWvrzdVc60xAjUShZeT5eMWM0/FQUduq3HjluyfAHWvC/aOBkT1pTZktyF/jg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"user-home": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/follow-redirects": {
|
"node_modules/follow-redirects": {
|
||||||
"version": "1.15.9",
|
"version": "1.15.9",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||||
@@ -2197,6 +2533,40 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/html-attribute-sorter": {
|
||||||
|
"version": "0.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/html-attribute-sorter/-/html-attribute-sorter-0.4.3.tgz",
|
||||||
|
"integrity": "sha512-HWSvaXJki44tg0uR1t+j5pRdUVpNiZcJaoB/PFhss/YoAw9cxUDLCpIBbLWQmKjBQfWk91P6LaRnredEyabrDw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ignore": {
|
||||||
|
"version": "6.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-6.0.2.tgz",
|
||||||
|
"integrity": "sha512-InwqeHHN2XpumIkMvpl/DCJVrAHgCsG5+cn1XlnLWGwtZBm8QJfSusItfrwx81CTp5agNZqpKU2J/ccC5nGT4A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/ini": {
|
||||||
|
"version": "1.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||||
|
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/is-binary-path": {
|
"node_modules/is-binary-path": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||||
@@ -2302,6 +2672,42 @@
|
|||||||
"jiti": "lib/jiti-cli.mjs"
|
"jiti": "lib/jiti-cli.mjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/js-beautify": {
|
||||||
|
"version": "1.15.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz",
|
||||||
|
"integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"config-chain": "^1.1.13",
|
||||||
|
"editorconfig": "^1.0.4",
|
||||||
|
"glob": "^10.4.2",
|
||||||
|
"js-cookie": "^3.0.5",
|
||||||
|
"nopt": "^7.2.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"css-beautify": "js/bin/css-beautify.js",
|
||||||
|
"html-beautify": "js/bin/html-beautify.js",
|
||||||
|
"js-beautify": "js/bin/js-beautify.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/js-cookie": {
|
||||||
|
"version": "3.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.8.tgz",
|
||||||
|
"integrity": "sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/json-schema-traverse": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/laravel-vite-plugin": {
|
"node_modules/laravel-vite-plugin": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.3.0.tgz",
|
||||||
@@ -2581,6 +2987,13 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/linguist-languages": {
|
||||||
|
"version": "8.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/linguist-languages/-/linguist-languages-8.2.0.tgz",
|
||||||
|
"integrity": "sha512-KCUUH9x97QWYU0SXOCGxUrZR6cSfuQrMhABB7L/0I8N0LXOeaKe7+RZs7FAwvWCV2qKfZ4Wv1luLq4OfMezSJg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/lodash": {
|
"node_modules/lodash": {
|
||||||
"version": "4.17.21",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
@@ -2689,11 +3102,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/minipass": {
|
"node_modules/minipass": {
|
||||||
"version": "7.1.2",
|
"version": "7.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
|
||||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
"integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "BlueOak-1.0.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16 || 14 >=14.17"
|
"node": ">=16 || 14 >=14.17"
|
||||||
}
|
}
|
||||||
@@ -2765,6 +3178,22 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/nopt": {
|
||||||
|
"version": "7.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz",
|
||||||
|
"integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"abbrev": "^2.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"nopt": "bin/nopt.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/normalize-path": {
|
"node_modules/normalize-path": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
@@ -2805,6 +3234,16 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/os-homedir": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/package-json-from-dist": {
|
"node_modules/package-json-from-dist": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
|
||||||
@@ -2846,6 +3285,13 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/php-parser": {
|
||||||
|
"version": "3.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/php-parser/-/php-parser-3.5.0.tgz",
|
||||||
|
"integrity": "sha512-EHdzSckQNP86jQRCEsMYhs+YzS4BfvfxnyhvzHVhVRoRUGEMFi8f3xKfuS9xdChBazZSyvb10SZbqhYQLGBcQg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
@@ -3036,6 +3482,29 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/prettier": {
|
||||||
|
"version": "3.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz",
|
||||||
|
"integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin/prettier.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/proto-list": {
|
||||||
|
"version": "1.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
|
||||||
|
"integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/proxy-from-env": {
|
"node_modules/proxy-from-env": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
@@ -3074,6 +3543,21 @@
|
|||||||
"pify": "^2.3.0"
|
"pify": "^2.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/readable-stream": {
|
||||||
|
"version": "3.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||||
|
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"string_decoder": "^1.1.1",
|
||||||
|
"util-deprecate": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/readdirp": {
|
"node_modules/readdirp": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
@@ -3097,6 +3581,16 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/require-from-string": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/resolve": {
|
"node_modules/resolve": {
|
||||||
"version": "1.22.10",
|
"version": "1.22.10",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
||||||
@@ -3203,6 +3697,40 @@
|
|||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/safe-buffer": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/semver": {
|
||||||
|
"version": "7.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz",
|
||||||
|
"integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/shebang-command": {
|
"node_modules/shebang-command": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
@@ -3268,6 +3796,26 @@
|
|||||||
"integrity": "sha512-WO+ns7BYZqGS4jWVTg5JNhIvNV4LGbUtNTSck4zAkWRQzA1IfxwIkMGc0BbdGy4PGIjK7kKo5CZcN6Sd5dHVlw==",
|
"integrity": "sha512-WO+ns7BYZqGS4jWVTg5JNhIvNV4LGbUtNTSck4zAkWRQzA1IfxwIkMGc0BbdGy4PGIjK7kKo5CZcN6Sd5dHVlw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/string_decoder": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "~5.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/string-replace-async": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-replace-async/-/string-replace-async-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-AHMupZscUiDh07F1QziX7PLoB1DQ/pzu19vc8Xa8LwZcgnOXaw7yCgBuSYrxVEfaM2d8scc3Gtp+i+QJZV+spw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/string-width": {
|
"node_modules/string-width": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||||
@@ -3605,6 +4153,13 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "0BSD"
|
"license": "0BSD"
|
||||||
},
|
},
|
||||||
|
"node_modules/typedarray": {
|
||||||
|
"version": "0.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||||
|
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/update-browserslist-db": {
|
"node_modules/update-browserslist-db": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
|
||||||
@@ -3636,6 +4191,19 @@
|
|||||||
"browserslist": ">= 4.21.0"
|
"browserslist": ">= 4.21.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/user-home": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-KMWqdlOcjCYdtIJpicDSFBQ8nFwS2i9sslAd6f4+CBGcU4gist2REnr2fxj2YocvJFxSF3ZOHLYLVZnUxv4BZQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"os-homedir": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/util-deprecate": {
|
"node_modules/util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
@@ -3757,6 +4325,20 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vscode-oniguruma": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz",
|
||||||
|
"integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/vscode-textmate": {
|
||||||
|
"version": "7.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-7.0.4.tgz",
|
||||||
|
"integrity": "sha512-9hJp0xL7HW1Q5OgGe03NACo7yiCTMEk3WU/rtKXUbncLtdg6rVVNJnHwD88UhbIYU2KoxY0Dih0x+kIsmUKn2A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
@@ -3868,6 +4450,16 @@
|
|||||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/xregexp": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/xregexp/-/xregexp-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-6hGgEMCGhqCTFEJbqmWrNIPqfpdirdGWkqshu7fFZddmTSfgv5Sn9D2SaKloR79s5VUiUlpwzg3CM3G6D3VIlw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime-corejs3": "^7.26.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/y18n": {
|
"node_modules/y18n": {
|
||||||
"version": "5.0.8",
|
"version": "5.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"dev": "vite"
|
"dev": "vite",
|
||||||
|
"format": "blade-formatter --write \"resources/views/**/*.blade.php\"",
|
||||||
|
"format:check": "blade-formatter --check-formatted \"resources/views/**/*.blade.php\""
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/forms": "^0.5.2",
|
"@tailwindcss/forms": "^0.5.2",
|
||||||
@@ -12,6 +14,7 @@
|
|||||||
"alpinejs": "^3.4.2",
|
"alpinejs": "^3.4.2",
|
||||||
"autoprefixer": "^10.4.2",
|
"autoprefixer": "^10.4.2",
|
||||||
"axios": "^1.8.2",
|
"axios": "^1.8.2",
|
||||||
|
"blade-formatter": "^1.44.4",
|
||||||
"concurrently": "^9.0.1",
|
"concurrently": "^9.0.1",
|
||||||
"laravel-vite-plugin": "^1.2.0",
|
"laravel-vite-plugin": "^1.2.0",
|
||||||
"postcss": "^8.4.31",
|
"postcss": "^8.4.31",
|
||||||
|
|||||||
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
|
||||||
@@ -15,14 +15,21 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
window.addEventListener('load', () => {
|
||||||
const cursor = document.getElementById('custom-cursor');
|
const cursor = document.getElementById('custom-cursor');
|
||||||
|
const gsapInstance = window.gsap;
|
||||||
|
|
||||||
|
if (!cursor || !gsapInstance) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
document.addEventListener('mousemove', (e) => {
|
document.addEventListener('mousemove', (e) => {
|
||||||
gsap.to(cursor, {
|
gsapInstance.to(cursor, {
|
||||||
duration: 0.2,
|
duration: 0.2,
|
||||||
x: e.clientX + 20,
|
x: e.clientX + 20,
|
||||||
y: e.clientY - 15,
|
y: e.clientY - 15,
|
||||||
ease: 'power2.out'
|
ease: 'power2.out'
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,8 @@
|
|||||||
<div>
|
<div>
|
||||||
<x-input-label for="password" :value="__('Password')" />
|
<x-input-label for="password" :value="__('Password')" />
|
||||||
|
|
||||||
<x-text-input id="password" class="block mt-1 w-full"
|
<x-text-input id="password" class="block mt-1 w-full" type="password" name="password" required
|
||||||
type="password"
|
autocomplete="current-password" />
|
||||||
name="password"
|
|
||||||
required autocomplete="current-password" />
|
|
||||||
|
|
||||||
<x-input-error :messages="$errors->get('password')" class="mt-2" />
|
<x-input-error :messages="$errors->get('password')" class="mt-2" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
<!-- Email Address -->
|
<!-- Email Address -->
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="email" :value="__('Email')" />
|
<x-input-label for="email" :value="__('Email')" />
|
||||||
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autofocus />
|
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required
|
||||||
|
autofocus />
|
||||||
<x-input-error :messages="$errors->get('email')" class="mt-2" />
|
<x-input-error :messages="$errors->get('email')" class="mt-2" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
<!-- Email Address -->
|
<!-- Email Address -->
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="email" :value="__('Email')" />
|
<x-input-label for="email" :value="__('Email')" />
|
||||||
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autofocus autocomplete="username" />
|
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required
|
||||||
|
autofocus autocomplete="username" />
|
||||||
<x-input-error :messages="$errors->get('email')" class="mt-2" />
|
<x-input-error :messages="$errors->get('email')" class="mt-2" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -16,10 +17,8 @@
|
|||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<x-input-label for="password" :value="__('Password')" />
|
<x-input-label for="password" :value="__('Password')" />
|
||||||
|
|
||||||
<x-text-input id="password" class="block mt-1 w-full"
|
<x-text-input id="password" class="block mt-1 w-full" type="password" name="password" required
|
||||||
type="password"
|
autocomplete="current-password" />
|
||||||
name="password"
|
|
||||||
required autocomplete="current-password" />
|
|
||||||
|
|
||||||
<x-input-error :messages="$errors->get('password')" class="mt-2" />
|
<x-input-error :messages="$errors->get('password')" class="mt-2" />
|
||||||
</div>
|
</div>
|
||||||
@@ -27,14 +26,17 @@
|
|||||||
<!-- Remember Me -->
|
<!-- Remember Me -->
|
||||||
<div class="block mt-4">
|
<div class="block mt-4">
|
||||||
<label for="remember_me" class="inline-flex items-center">
|
<label for="remember_me" class="inline-flex items-center">
|
||||||
<input id="remember_me" type="checkbox" class="rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800" name="remember">
|
<input id="remember_me" type="checkbox"
|
||||||
|
class="rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800"
|
||||||
|
name="remember">
|
||||||
<span class="ms-2 text-sm text-gray-600 dark:text-gray-400">{{ __('Remember me') }}</span>
|
<span class="ms-2 text-sm text-gray-600 dark:text-gray-400">{{ __('Remember me') }}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center justify-end mt-4">
|
<div class="flex items-center justify-end mt-4">
|
||||||
@if (Route::has('password.request'))
|
@if (Route::has('password.request'))
|
||||||
<a class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800" href="{{ route('password.request') }}">
|
<a class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800"
|
||||||
|
href="{{ route('password.request') }}">
|
||||||
{{ __('Forgot your password?') }}
|
{{ __('Forgot your password?') }}
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
@@ -5,14 +5,16 @@
|
|||||||
<!-- Name -->
|
<!-- Name -->
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="name" :value="__('Name')" />
|
<x-input-label for="name" :value="__('Name')" />
|
||||||
<x-text-input id="name" class="block mt-1 w-full" type="text" name="name" :value="old('name')" required autofocus autocomplete="name" />
|
<x-text-input id="name" class="block mt-1 w-full" type="text" name="name" :value="old('name')" required
|
||||||
|
autofocus autocomplete="name" />
|
||||||
<x-input-error :messages="$errors->get('name')" class="mt-2" />
|
<x-input-error :messages="$errors->get('name')" class="mt-2" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Email Address -->
|
<!-- Email Address -->
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<x-input-label for="email" :value="__('Email')" />
|
<x-input-label for="email" :value="__('Email')" />
|
||||||
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autocomplete="username" />
|
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')"
|
||||||
|
required autocomplete="username" />
|
||||||
<x-input-error :messages="$errors->get('email')" class="mt-2" />
|
<x-input-error :messages="$errors->get('email')" class="mt-2" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -20,10 +22,8 @@
|
|||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<x-input-label for="password" :value="__('Password')" />
|
<x-input-label for="password" :value="__('Password')" />
|
||||||
|
|
||||||
<x-text-input id="password" class="block mt-1 w-full"
|
<x-text-input id="password" class="block mt-1 w-full" type="password" name="password" required
|
||||||
type="password"
|
autocomplete="new-password" />
|
||||||
name="password"
|
|
||||||
required autocomplete="new-password" />
|
|
||||||
|
|
||||||
<x-input-error :messages="$errors->get('password')" class="mt-2" />
|
<x-input-error :messages="$errors->get('password')" class="mt-2" />
|
||||||
</div>
|
</div>
|
||||||
@@ -32,15 +32,15 @@
|
|||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<x-input-label for="password_confirmation" :value="__('Confirm Password')" />
|
<x-input-label for="password_confirmation" :value="__('Confirm Password')" />
|
||||||
|
|
||||||
<x-text-input id="password_confirmation" class="block mt-1 w-full"
|
<x-text-input id="password_confirmation" class="block mt-1 w-full" type="password"
|
||||||
type="password"
|
|
||||||
name="password_confirmation" required autocomplete="new-password" />
|
name="password_confirmation" required autocomplete="new-password" />
|
||||||
|
|
||||||
<x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" />
|
<x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center justify-end mt-4">
|
<div class="flex items-center justify-end mt-4">
|
||||||
<a class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800" href="{{ route('login') }}">
|
<a class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800"
|
||||||
|
href="{{ route('login') }}">
|
||||||
{{ __('Already registered?') }}
|
{{ __('Already registered?') }}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|||||||
@@ -8,14 +8,16 @@
|
|||||||
<!-- Email Address -->
|
<!-- Email Address -->
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="email" :value="__('Email')" />
|
<x-input-label for="email" :value="__('Email')" />
|
||||||
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email', $request->email)" required autofocus autocomplete="username" />
|
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email', $request->email)" required
|
||||||
|
autofocus autocomplete="username" />
|
||||||
<x-input-error :messages="$errors->get('email')" class="mt-2" />
|
<x-input-error :messages="$errors->get('email')" class="mt-2" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Password -->
|
<!-- Password -->
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<x-input-label for="password" :value="__('Password')" />
|
<x-input-label for="password" :value="__('Password')" />
|
||||||
<x-text-input id="password" class="block mt-1 w-full" type="password" name="password" required autocomplete="new-password" />
|
<x-text-input id="password" class="block mt-1 w-full" type="password" name="password" required
|
||||||
|
autocomplete="new-password" />
|
||||||
<x-input-error :messages="$errors->get('password')" class="mt-2" />
|
<x-input-error :messages="$errors->get('password')" class="mt-2" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -23,8 +25,7 @@
|
|||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<x-input-label for="password_confirmation" :value="__('Confirm Password')" />
|
<x-input-label for="password_confirmation" :value="__('Confirm Password')" />
|
||||||
|
|
||||||
<x-text-input id="password_confirmation" class="block mt-1 w-full"
|
<x-text-input id="password_confirmation" class="block mt-1 w-full" type="password"
|
||||||
type="password"
|
|
||||||
name="password_confirmation" required autocomplete="new-password" />
|
name="password_confirmation" required autocomplete="new-password" />
|
||||||
|
|
||||||
<x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" />
|
<x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" />
|
||||||
|
|||||||
@@ -23,7 +23,8 @@
|
|||||||
<form method="POST" action="{{ route('logout') }}">
|
<form method="POST" action="{{ route('logout') }}">
|
||||||
@csrf
|
@csrf
|
||||||
|
|
||||||
<button type="submit" class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800">
|
<button type="submit"
|
||||||
|
class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800">
|
||||||
{{ __('Log Out') }}
|
{{ __('Log Out') }}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
<svg viewBox="0 0 316 316" xmlns="http://www.w3.org/2000/svg" {{ $attributes }}>
|
<svg viewBox="0 0 316 316" xmlns="http://www.w3.org/2000/svg" {{ $attributes }}>
|
||||||
<path d="M305.8 81.125C305.77 80.995 305.69 80.885 305.65 80.755C305.56 80.525 305.49 80.285 305.37 80.075C305.29 79.935 305.17 79.815 305.07 79.685C304.94 79.515 304.83 79.325 304.68 79.175C304.55 79.045 304.39 78.955 304.25 78.845C304.09 78.715 303.95 78.575 303.77 78.475L251.32 48.275C249.97 47.495 248.31 47.495 246.96 48.275L194.51 78.475C194.33 78.575 194.19 78.725 194.03 78.845C193.89 78.955 193.73 79.045 193.6 79.175C193.45 79.325 193.34 79.515 193.21 79.685C193.11 79.815 192.99 79.935 192.91 80.075C192.79 80.285 192.71 80.525 192.63 80.755C192.58 80.875 192.51 80.995 192.48 81.125C192.38 81.495 192.33 81.875 192.33 82.265V139.625L148.62 164.795V52.575C148.62 52.185 148.57 51.805 148.47 51.435C148.44 51.305 148.36 51.195 148.32 51.065C148.23 50.835 148.16 50.595 148.04 50.385C147.96 50.245 147.84 50.125 147.74 49.995C147.61 49.825 147.5 49.635 147.35 49.485C147.22 49.355 147.06 49.265 146.92 49.155C146.76 49.025 146.62 48.885 146.44 48.785L93.99 18.585C92.64 17.805 90.98 17.805 89.63 18.585L37.18 48.785C37 48.885 36.86 49.035 36.7 49.155C36.56 49.265 36.4 49.355 36.27 49.485C36.12 49.635 36.01 49.825 35.88 49.995C35.78 50.125 35.66 50.245 35.58 50.385C35.46 50.595 35.38 50.835 35.3 51.065C35.25 51.185 35.18 51.305 35.15 51.435C35.05 51.805 35 52.185 35 52.575V232.235C35 233.795 35.84 235.245 37.19 236.025L142.1 296.425C142.33 296.555 142.58 296.635 142.82 296.725C142.93 296.765 143.04 296.835 143.16 296.865C143.53 296.965 143.9 297.015 144.28 297.015C144.66 297.015 145.03 296.965 145.4 296.865C145.5 296.835 145.59 296.775 145.69 296.745C145.95 296.655 146.21 296.565 146.45 296.435L251.36 236.035C252.72 235.255 253.55 233.815 253.55 232.245V174.885L303.81 145.945C305.17 145.165 306 143.725 306 142.155V82.265C305.95 81.875 305.89 81.495 305.8 81.125ZM144.2 227.205L100.57 202.515L146.39 176.135L196.66 147.195L240.33 172.335L208.29 190.625L144.2 227.205ZM244.75 114.995V164.795L226.39 154.225L201.03 139.625V89.825L219.39 100.395L244.75 114.995ZM249.12 57.105L292.81 82.265L249.12 107.425L205.43 82.265L249.12 57.105ZM114.49 184.425L96.13 194.995V85.305L121.49 70.705L139.85 60.135V169.815L114.49 184.425ZM91.76 27.425L135.45 52.585L91.76 77.745L48.07 52.585L91.76 27.425ZM43.67 60.135L62.03 70.705L87.39 85.305V202.545V202.555V202.565C87.39 202.735 87.44 202.895 87.46 203.055C87.49 203.265 87.49 203.485 87.55 203.695V203.705C87.6 203.875 87.69 204.035 87.76 204.195C87.84 204.375 87.89 204.575 87.99 204.745C87.99 204.745 87.99 204.755 88 204.755C88.09 204.905 88.22 205.035 88.33 205.175C88.45 205.335 88.55 205.495 88.69 205.635L88.7 205.645C88.82 205.765 88.98 205.855 89.12 205.965C89.28 206.085 89.42 206.225 89.59 206.325C89.6 206.325 89.6 206.325 89.61 206.335C89.62 206.335 89.62 206.345 89.63 206.345L139.87 234.775V285.065L43.67 229.705V60.135ZM244.75 229.705L148.58 285.075V234.775L219.8 194.115L244.75 179.875V229.705ZM297.2 139.625L253.49 164.795V114.995L278.85 100.395L297.21 89.825V139.625H297.2Z"/>
|
<path
|
||||||
|
d="M305.8 81.125C305.77 80.995 305.69 80.885 305.65 80.755C305.56 80.525 305.49 80.285 305.37 80.075C305.29 79.935 305.17 79.815 305.07 79.685C304.94 79.515 304.83 79.325 304.68 79.175C304.55 79.045 304.39 78.955 304.25 78.845C304.09 78.715 303.95 78.575 303.77 78.475L251.32 48.275C249.97 47.495 248.31 47.495 246.96 48.275L194.51 78.475C194.33 78.575 194.19 78.725 194.03 78.845C193.89 78.955 193.73 79.045 193.6 79.175C193.45 79.325 193.34 79.515 193.21 79.685C193.11 79.815 192.99 79.935 192.91 80.075C192.79 80.285 192.71 80.525 192.63 80.755C192.58 80.875 192.51 80.995 192.48 81.125C192.38 81.495 192.33 81.875 192.33 82.265V139.625L148.62 164.795V52.575C148.62 52.185 148.57 51.805 148.47 51.435C148.44 51.305 148.36 51.195 148.32 51.065C148.23 50.835 148.16 50.595 148.04 50.385C147.96 50.245 147.84 50.125 147.74 49.995C147.61 49.825 147.5 49.635 147.35 49.485C147.22 49.355 147.06 49.265 146.92 49.155C146.76 49.025 146.62 48.885 146.44 48.785L93.99 18.585C92.64 17.805 90.98 17.805 89.63 18.585L37.18 48.785C37 48.885 36.86 49.035 36.7 49.155C36.56 49.265 36.4 49.355 36.27 49.485C36.12 49.635 36.01 49.825 35.88 49.995C35.78 50.125 35.66 50.245 35.58 50.385C35.46 50.595 35.38 50.835 35.3 51.065C35.25 51.185 35.18 51.305 35.15 51.435C35.05 51.805 35 52.185 35 52.575V232.235C35 233.795 35.84 235.245 37.19 236.025L142.1 296.425C142.33 296.555 142.58 296.635 142.82 296.725C142.93 296.765 143.04 296.835 143.16 296.865C143.53 296.965 143.9 297.015 144.28 297.015C144.66 297.015 145.03 296.965 145.4 296.865C145.5 296.835 145.59 296.775 145.69 296.745C145.95 296.655 146.21 296.565 146.45 296.435L251.36 236.035C252.72 235.255 253.55 233.815 253.55 232.245V174.885L303.81 145.945C305.17 145.165 306 143.725 306 142.155V82.265C305.95 81.875 305.89 81.495 305.8 81.125ZM144.2 227.205L100.57 202.515L146.39 176.135L196.66 147.195L240.33 172.335L208.29 190.625L144.2 227.205ZM244.75 114.995V164.795L226.39 154.225L201.03 139.625V89.825L219.39 100.395L244.75 114.995ZM249.12 57.105L292.81 82.265L249.12 107.425L205.43 82.265L249.12 57.105ZM114.49 184.425L96.13 194.995V85.305L121.49 70.705L139.85 60.135V169.815L114.49 184.425ZM91.76 27.425L135.45 52.585L91.76 77.745L48.07 52.585L91.76 27.425ZM43.67 60.135L62.03 70.705L87.39 85.305V202.545V202.555V202.565C87.39 202.735 87.44 202.895 87.46 203.055C87.49 203.265 87.49 203.485 87.55 203.695V203.705C87.6 203.875 87.69 204.035 87.76 204.195C87.84 204.375 87.89 204.575 87.99 204.745C87.99 204.745 87.99 204.755 88 204.755C88.09 204.905 88.22 205.035 88.33 205.175C88.45 205.335 88.55 205.495 88.69 205.635L88.7 205.645C88.82 205.765 88.98 205.855 89.12 205.965C89.28 206.085 89.42 206.225 89.59 206.325C89.6 206.325 89.6 206.325 89.61 206.335C89.62 206.335 89.62 206.345 89.63 206.345L139.87 234.775V285.065L43.67 229.705V60.135ZM244.75 229.705L148.58 285.075V234.775L219.8 194.115L244.75 179.875V229.705ZM297.2 139.625L253.49 164.795V114.995L278.85 100.395L297.21 89.825V139.625H297.2Z" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
@@ -1,3 +1,4 @@
|
|||||||
<button {{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-red-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-red-500 active:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150']) }}>
|
<button
|
||||||
|
{{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-red-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-red-500 active:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150']) }}>
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
<a {{ $attributes->merge(['class' => 'block w-full px-4 py-2 text-start text-sm leading-5 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-800 transition duration-150 ease-in-out']) }}>{{ $slot }}</a>
|
<a
|
||||||
|
{{ $attributes->merge(['class' => 'block w-full px-4 py-2 text-start text-sm leading-5 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-800 transition duration-150 ease-in-out']) }}>{{ $slot }}</a>
|
||||||
|
|||||||
@@ -18,16 +18,11 @@ $width = match ($width) {
|
|||||||
{{ $trigger }}
|
{{ $trigger }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div x-show="open"
|
<div x-show="open" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0 scale-95"
|
||||||
x-transition:enter="transition ease-out duration-200"
|
x-transition:enter-end="opacity-100 scale-100" x-transition:leave="transition ease-in duration-75"
|
||||||
x-transition:enter-start="opacity-0 scale-95"
|
x-transition:leave-start="opacity-100 scale-100" x-transition:leave-end="opacity-0 scale-95"
|
||||||
x-transition:enter-end="opacity-100 scale-100"
|
|
||||||
x-transition:leave="transition ease-in duration-75"
|
|
||||||
x-transition:leave-start="opacity-100 scale-100"
|
|
||||||
x-transition:leave-end="opacity-0 scale-95"
|
|
||||||
class="absolute z-50 mt-2 {{ $width }} rounded-md shadow-lg {{ $alignmentClasses }}"
|
class="absolute z-50 mt-2 {{ $width }} rounded-md shadow-lg {{ $alignmentClasses }}"
|
||||||
style="display: none;"
|
style="display: none;" @click="open = false">
|
||||||
@click="open = false">
|
|
||||||
<div class="rounded-md ring-1 ring-black ring-opacity-5 {{ $contentClasses }}">
|
<div class="rounded-md ring-1 ring-black ring-opacity-5 {{ $contentClasses }}">
|
||||||
{{ $content }}
|
{{ $content }}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
@props([
|
@props(['name', 'show' => false, 'maxWidth' => '2xl'])
|
||||||
'name',
|
|
||||||
'show' => false,
|
|
||||||
'maxWidth' => '2xl'
|
|
||||||
])
|
|
||||||
|
|
||||||
@php
|
@php
|
||||||
$maxWidth = [
|
$maxWidth = [
|
||||||
@@ -14,8 +10,7 @@ $maxWidth = [
|
|||||||
][$maxWidth];
|
][$maxWidth];
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<div
|
<div x-data="{
|
||||||
x-data="{
|
|
||||||
show: @js($show),
|
show: @js($show),
|
||||||
focusables() {
|
focusables() {
|
||||||
// All focusable element types...
|
// All focusable element types...
|
||||||
@@ -30,8 +25,7 @@ $maxWidth = [
|
|||||||
prevFocusable() { return this.focusables()[this.prevFocusableIndex()] || this.lastFocusable() },
|
prevFocusable() { return this.focusables()[this.prevFocusableIndex()] || this.lastFocusable() },
|
||||||
nextFocusableIndex() { return (this.focusables().indexOf(document.activeElement) + 1) % (this.focusables().length + 1) },
|
nextFocusableIndex() { return (this.focusables().indexOf(document.activeElement) + 1) % (this.focusables().length + 1) },
|
||||||
prevFocusableIndex() { return Math.max(0, this.focusables().indexOf(document.activeElement)) - 1 },
|
prevFocusableIndex() { return Math.max(0, this.focusables().indexOf(document.activeElement)) - 1 },
|
||||||
}"
|
}" x-init="$watch('show', value => {
|
||||||
x-init="$watch('show', value => {
|
|
||||||
if (value) {
|
if (value) {
|
||||||
document.body.classList.add('overflow-y-hidden');
|
document.body.classList.add('overflow-y-hidden');
|
||||||
{{ $attributes->has('focusable') ? 'setTimeout(() => firstFocusable().focus(), 100)' : '' }}
|
{{ $attributes->has('focusable') ? 'setTimeout(() => firstFocusable().focus(), 100)' : '' }}
|
||||||
@@ -40,39 +34,24 @@ $maxWidth = [
|
|||||||
}
|
}
|
||||||
})"
|
})"
|
||||||
x-on:open-modal.window="$event.detail == '{{ $name }}' ? show = true : null"
|
x-on:open-modal.window="$event.detail == '{{ $name }}' ? show = true : null"
|
||||||
x-on:close-modal.window="$event.detail == '{{ $name }}' ? show = false : null"
|
x-on:close-modal.window="$event.detail == '{{ $name }}' ? show = false : null" x-on:close.stop="show = false"
|
||||||
x-on:close.stop="show = false"
|
x-on:keydown.escape.window="show = false" x-on:keydown.tab.prevent="$event.shiftKey || nextFocusable().focus()"
|
||||||
x-on:keydown.escape.window="show = false"
|
x-on:keydown.shift.tab.prevent="prevFocusable().focus()" x-show="show"
|
||||||
x-on:keydown.tab.prevent="$event.shiftKey || nextFocusable().focus()"
|
class="fixed inset-0 overflow-y-auto px-4 py-6 sm:px-0 z-50" style="display: {{ $show ? 'block' : 'none' }};">
|
||||||
x-on:keydown.shift.tab.prevent="prevFocusable().focus()"
|
<div x-show="show" class="fixed inset-0 transform transition-all" x-on:click="show = false"
|
||||||
x-show="show"
|
x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0"
|
||||||
class="fixed inset-0 overflow-y-auto px-4 py-6 sm:px-0 z-50"
|
x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-200"
|
||||||
style="display: {{ $show ? 'block' : 'none' }};"
|
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0">
|
||||||
>
|
|
||||||
<div
|
|
||||||
x-show="show"
|
|
||||||
class="fixed inset-0 transform transition-all"
|
|
||||||
x-on:click="show = false"
|
|
||||||
x-transition:enter="ease-out duration-300"
|
|
||||||
x-transition:enter-start="opacity-0"
|
|
||||||
x-transition:enter-end="opacity-100"
|
|
||||||
x-transition:leave="ease-in duration-200"
|
|
||||||
x-transition:leave-start="opacity-100"
|
|
||||||
x-transition:leave-end="opacity-0"
|
|
||||||
>
|
|
||||||
<div class="absolute inset-0 bg-gray-500 dark:bg-gray-900 opacity-75"></div>
|
<div class="absolute inset-0 bg-gray-500 dark:bg-gray-900 opacity-75"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div x-show="show"
|
||||||
x-show="show"
|
|
||||||
class="mb-6 bg-white dark:bg-gray-800 rounded-lg overflow-hidden shadow-xl transform transition-all sm:w-full {{ $maxWidth }} sm:mx-auto"
|
class="mb-6 bg-white dark:bg-gray-800 rounded-lg overflow-hidden shadow-xl transform transition-all sm:w-full {{ $maxWidth }} sm:mx-auto"
|
||||||
x-transition:enter="ease-out duration-300"
|
x-transition:enter="ease-out duration-300"
|
||||||
x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||||
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
|
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100" x-transition:leave="ease-in duration-200"
|
||||||
x-transition:leave="ease-in duration-200"
|
|
||||||
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
|
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
|
||||||
x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95">
|
||||||
>
|
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
@props(['active'])
|
@props(['active'])
|
||||||
|
|
||||||
@php
|
@php
|
||||||
$classes = ($active ?? false)
|
$classes =
|
||||||
|
$active ?? false
|
||||||
? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 dark:border-indigo-600 text-sm font-medium leading-5 text-gray-900 dark:text-gray-100 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out'
|
? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 dark:border-indigo-600 text-sm font-medium leading-5 text-gray-900 dark:text-gray-100 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out'
|
||||||
: 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 hover:border-gray-300 dark:hover:border-gray-700 focus:outline-none focus:text-gray-700 dark:focus:text-gray-300 focus:border-gray-300 dark:focus:border-gray-700 transition duration-150 ease-in-out';
|
: 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 hover:border-gray-300 dark:hover:border-gray-700 focus:outline-none focus:text-gray-700 dark:focus:text-gray-300 focus:border-gray-300 dark:focus:border-gray-700 transition duration-150 ease-in-out';
|
||||||
@endphp
|
@endphp
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
<button {{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-gray-800 dark:bg-gray-200 border border-transparent rounded-md font-semibold text-xs text-white dark:text-gray-800 uppercase tracking-widest hover:bg-gray-700 dark:hover:bg-white focus:bg-gray-700 dark:focus:bg-white active:bg-gray-900 dark:active:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150']) }}>
|
<button
|
||||||
|
{{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-gray-800 dark:bg-gray-200 border border-transparent rounded-md font-semibold text-xs text-white dark:text-gray-800 uppercase tracking-widest hover:bg-gray-700 dark:hover:bg-white focus:bg-gray-700 dark:focus:bg-white active:bg-gray-900 dark:active:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150']) }}>
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
@props(['active'])
|
@props(['active'])
|
||||||
|
|
||||||
@php
|
@php
|
||||||
$classes = ($active ?? false)
|
$classes =
|
||||||
|
$active ?? false
|
||||||
? 'block w-full ps-3 pe-4 py-2 border-l-4 border-indigo-400 dark:border-indigo-600 text-start text-base font-medium text-indigo-700 dark:text-indigo-300 bg-indigo-50 dark:bg-indigo-900/50 focus:outline-none focus:text-indigo-800 dark:focus:text-indigo-200 focus:bg-indigo-100 dark:focus:bg-indigo-900 focus:border-indigo-700 dark:focus:border-indigo-300 transition duration-150 ease-in-out'
|
? 'block w-full ps-3 pe-4 py-2 border-l-4 border-indigo-400 dark:border-indigo-600 text-start text-base font-medium text-indigo-700 dark:text-indigo-300 bg-indigo-50 dark:bg-indigo-900/50 focus:outline-none focus:text-indigo-800 dark:focus:text-indigo-200 focus:bg-indigo-100 dark:focus:bg-indigo-900 focus:border-indigo-700 dark:focus:border-indigo-300 transition duration-150 ease-in-out'
|
||||||
: 'block w-full ps-3 pe-4 py-2 border-l-4 border-transparent text-start text-base font-medium text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 hover:border-gray-300 dark:hover:border-gray-600 focus:outline-none focus:text-gray-800 dark:focus:text-gray-200 focus:bg-gray-50 dark:focus:bg-gray-700 focus:border-gray-300 dark:focus:border-gray-600 transition duration-150 ease-in-out';
|
: 'block w-full ps-3 pe-4 py-2 border-l-4 border-transparent text-start text-base font-medium text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 hover:border-gray-300 dark:hover:border-gray-600 focus:outline-none focus:text-gray-800 dark:focus:text-gray-200 focus:bg-gray-50 dark:focus:bg-gray-700 focus:border-gray-300 dark:focus:border-gray-600 transition duration-150 ease-in-out';
|
||||||
@endphp
|
@endphp
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
<button {{ $attributes->merge(['type' => 'button', 'class' => 'inline-flex items-center px-4 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-500 rounded-md font-semibold text-xs text-gray-700 dark:text-gray-300 uppercase tracking-widest shadow-sm hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 disabled:opacity-25 transition ease-in-out duration-150']) }}>
|
<button
|
||||||
|
{{ $attributes->merge(['type' => 'button', 'class' => 'inline-flex items-center px-4 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-500 rounded-md font-semibold text-xs text-gray-700 dark:text-gray-300 uppercase tracking-widest shadow-sm hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 disabled:opacity-25 transition ease-in-out duration-150']) }}>
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
@props(['disabled' => false])
|
@props(['disabled' => false])
|
||||||
|
|
||||||
<input @disabled($disabled) {{ $attributes->merge(['class' => 'border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm']) }}>
|
<input @disabled($disabled)
|
||||||
|
{{ $attributes->merge(['class' => 'border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm']) }}>
|
||||||
|
|||||||
@@ -1,33 +1,45 @@
|
|||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="opleiding" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Opleiding</label>
|
<label for="opleiding" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Opleiding</label>
|
||||||
<input type="text" id="opleiding" name="opleiding" value="{{ old('opleiding', $education->opleiding ?? '') }}" class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
<input type="text" id="opleiding" name="opleiding" value="{{ old('opleiding', $education->opleiding ?? '') }}"
|
||||||
|
class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="instituut" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Instituut</label>
|
<label for="instituut" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Instituut</label>
|
||||||
<input type="text" id="instituut" name="instituut" value="{{ old('instituut', $education->instituut ?? '') }}" class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
<input type="text" id="instituut" name="instituut"
|
||||||
|
value="{{ old('instituut', $education->instituut ?? '') }}"
|
||||||
|
class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="startdatum" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Startdatum</label>
|
<label for="startdatum"
|
||||||
<input type="date" id="startdatum" name="startdatum" value="{{ old('startdatum', $education->startdatum ?? '') }}" class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
class="block text-sm font-medium text-gray-700 dark:text-gray-200">Startdatum</label>
|
||||||
|
<input type="date" id="startdatum" name="startdatum"
|
||||||
|
value="{{ old('startdatum', $education->startdatum ?? '') }}"
|
||||||
|
class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="einddatum" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Einddatum</label>
|
<label for="einddatum" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Einddatum</label>
|
||||||
<input type="date" id="einddatum" name="einddatum" value="{{ old('einddatum', $education->einddatum ?? '') }}" class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
<input type="date" id="einddatum" name="einddatum"
|
||||||
|
value="{{ old('einddatum', $education->einddatum ?? '') }}"
|
||||||
|
class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="beschrijving" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Beschrijving</label>
|
<label for="beschrijving"
|
||||||
<textarea id="beschrijving" name="beschrijving" rows="4" class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">{{ old('beschrijving', $education->beschrijving ?? '') }}</textarea>
|
class="block text-sm font-medium text-gray-700 dark:text-gray-200">Beschrijving</label>
|
||||||
|
<textarea id="beschrijving" name="beschrijving" rows="4"
|
||||||
|
class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">{{ old('beschrijving', $education->beschrijving ?? '') }}</textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="afbeelding" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Afbeelding (optioneel)</label>
|
<label for="afbeelding" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Afbeelding
|
||||||
<input type="file" id="afbeelding" name="afbeelding" class="mt-1 block w-full text-sm text-gray-500 file:bg-gray-100 file:border file:border-gray-300 file:rounded file:px-4 file:py-2 dark:file:bg-gray-800 dark:file:text-gray-300 dark:file:border-gray-600">
|
(optioneel)</label>
|
||||||
|
<input type="file" id="afbeelding" name="afbeelding"
|
||||||
|
class="mt-1 block w-full text-sm text-gray-500 file:bg-gray-100 file:border file:border-gray-300 file:rounded file:px-4 file:py-2 dark:file:bg-gray-800 dark:file:text-gray-300 dark:file:border-gray-600">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
<form method="POST" action="{{ route('educations.store') }}" enctype="multipart/form-data">
|
<form method="POST" action="{{ route('educations.store') }}" enctype="multipart/form-data">
|
||||||
@csrf
|
@csrf
|
||||||
@include('educations._form')
|
@include('educations._form')
|
||||||
<button type="submit" class="mt-4 px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700">Opslaan</button>
|
<button type="submit"
|
||||||
|
class="mt-4 px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700">Opslaan</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</x-app-layout>
|
</x-app-layout>
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
@csrf
|
@csrf
|
||||||
@method('PUT')
|
@method('PUT')
|
||||||
@include('educations._form')
|
@include('educations._form')
|
||||||
<button type="submit" class="mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">Bijwerken</button>
|
<button type="submit"
|
||||||
|
class="mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">Bijwerken</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</x-app-layout>
|
</x-app-layout>
|
||||||
|
|||||||
@@ -2,13 +2,15 @@
|
|||||||
<div class="p-6">
|
<div class="p-6">
|
||||||
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-4">Opleidingen</h2>
|
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-4">Opleidingen</h2>
|
||||||
|
|
||||||
<a href="{{ route('educations.create') }}" class="mb-4 inline-block bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700">Nieuwe opleiding</a>
|
<a href="{{ route('educations.create') }}"
|
||||||
|
class="mb-4 inline-block bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700">Nieuwe opleiding</a>
|
||||||
|
|
||||||
@foreach ($educations as $education)
|
@foreach ($educations as $education)
|
||||||
<div class="mb-4 p-4 bg-white dark:bg-gray-800 shadow rounded">
|
<div class="mb-4 p-4 bg-white dark:bg-gray-800 shadow rounded">
|
||||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white">{{ $education->opleiding }}</h3>
|
<h3 class="text-xl font-bold text-gray-900 dark:text-white">{{ $education->opleiding }}</h3>
|
||||||
<p class="text-gray-600 dark:text-gray-300">{{ $education->instituut }}</p>
|
<p class="text-gray-600 dark:text-gray-300">{{ $education->instituut }}</p>
|
||||||
<p class="text-sm text-gray-500 dark:text-gray-400">{{ $education->startdatum }} – {{ $education->einddatum ?? 'heden' }}</p>
|
<p class="text-sm text-gray-500 dark:text-gray-400">{{ $education->startdatum }} –
|
||||||
|
{{ $education->einddatum ?? 'heden' }}</p>
|
||||||
<p class="mt-2 text-gray-700 dark:text-gray-300">{{ $education->beschrijving }}</p>
|
<p class="mt-2 text-gray-700 dark:text-gray-300">{{ $education->beschrijving }}</p>
|
||||||
|
|
||||||
@if ($education->image())
|
@if ($education->image())
|
||||||
@@ -16,9 +18,11 @@
|
|||||||
@endif
|
@endif
|
||||||
|
|
||||||
<div class="mt-4 space-x-2">
|
<div class="mt-4 space-x-2">
|
||||||
<a href="{{ route('educations.edit', $education) }}" class="px-3 py-1 bg-green-400 text-white rounded hover:bg-blue-600">Bewerk</a>
|
<a href="{{ route('educations.edit', $education) }}"
|
||||||
|
class="px-3 py-1 bg-green-400 text-white rounded hover:bg-blue-600">Bewerk</a>
|
||||||
|
|
||||||
<form action="{{ route('educations.destroy', $education) }}" method="POST" class="inline-block" onsubmit="return confirm('Weet je zeker dat je dit wilt verwijderen?')">
|
<form action="{{ route('educations.destroy', $education) }}" method="POST" class="inline-block"
|
||||||
|
onsubmit="return confirm('Weet je zeker dat je dit wilt verwijderen?')">
|
||||||
@csrf
|
@csrf
|
||||||
@method('DELETE')
|
@method('DELETE')
|
||||||
<button class="px-3 py-1 bg-red-600 text-white rounded hover:bg-red-700">Verwijder</button>
|
<button class="px-3 py-1 bg-red-600 text-white rounded hover:bg-red-700">Verwijder</button>
|
||||||
|
|||||||
@@ -10,12 +10,14 @@
|
|||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if ($skill->getFirstMediaUrl('image'))
|
@if ($skill->getFirstMediaUrl('image'))
|
||||||
<img src="{{ $skill->getFirstMediaUrl('image') }}" alt="{{ $skill->title }}" class="mt-2 max-w-full h-32 object-contain rounded">
|
<img src="{{ $skill->getFirstMediaUrl('image') }}" alt="{{ $skill->title }}"
|
||||||
|
class="mt-2 max-w-full h-32 object-contain rounded">
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<div class="mt-4 flex justify-between">
|
<div class="mt-4 flex justify-between">
|
||||||
<a href="{{ route('skills.edit', $skill) }}" class="text-blue-600 hover:underline">Bewerken</a>
|
<a href="{{ route('skills.edit', $skill) }}" class="text-blue-600 hover:underline">Bewerken</a>
|
||||||
<form action="{{ route('skills.destroy', $skill) }}" method="POST" onsubmit="return confirm('Weet je zeker dat je dit wilt verwijderen?')">
|
<form action="{{ route('skills.destroy', $skill) }}" method="POST"
|
||||||
|
onsubmit="return confirm('Weet je zeker dat je dit wilt verwijderen?')">
|
||||||
@csrf
|
@csrf
|
||||||
@method('DELETE')
|
@method('DELETE')
|
||||||
<button class="text-red-600 hover:underline">Verwijderen</button>
|
<button class="text-red-600 hover:underline">Verwijderen</button>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
@@ -14,6 +15,7 @@
|
|||||||
<!-- Scripts -->
|
<!-- Scripts -->
|
||||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="font-sans text-gray-900 antialiased">
|
<body class="font-sans text-gray-900 antialiased">
|
||||||
<div class="min-h-screen flex flex-col sm:justify-center items-center pt-6 sm:pt-0 bg-gray-100 dark:bg-gray-900">
|
<div class="min-h-screen flex flex-col sm:justify-center items-center pt-6 sm:pt-0 bg-gray-100 dark:bg-gray-900">
|
||||||
<div>
|
<div>
|
||||||
@@ -22,9 +24,11 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full sm:max-w-md mt-6 px-6 py-4 bg-white dark:bg-gray-800 shadow-md overflow-hidden sm:rounded-lg">
|
<div
|
||||||
|
class="w-full sm:max-w-md mt-6 px-6 py-4 bg-white dark:bg-gray-800 shadow-md overflow-hidden sm:rounded-lg">
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -35,12 +35,16 @@
|
|||||||
<div class="hidden sm:flex sm:items-center sm:ms-6">
|
<div class="hidden sm:flex sm:items-center sm:ms-6">
|
||||||
<x-dropdown align="right" width="48">
|
<x-dropdown align="right" width="48">
|
||||||
<x-slot name="trigger">
|
<x-slot name="trigger">
|
||||||
<button class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 dark:text-gray-400 bg-white dark:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-300 focus:outline-none transition ease-in-out duration-150">
|
<button
|
||||||
|
class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 dark:text-gray-400 bg-white dark:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-300 focus:outline-none transition ease-in-out duration-150">
|
||||||
<div>{{ Auth::user()->name }}</div>
|
<div>{{ Auth::user()->name }}</div>
|
||||||
|
|
||||||
<div class="ms-1">
|
<div class="ms-1">
|
||||||
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg"
|
||||||
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
|
viewBox="0 0 20 20">
|
||||||
|
<path fill-rule="evenodd"
|
||||||
|
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||||
|
clip-rule="evenodd" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
@@ -82,10 +86,14 @@
|
|||||||
|
|
||||||
<!-- Hamburger -->
|
<!-- Hamburger -->
|
||||||
<div class="-me-2 flex items-center sm:hidden">
|
<div class="-me-2 flex items-center sm:hidden">
|
||||||
<button @click="open = ! open" class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 dark:text-gray-500 hover:text-gray-500 dark:hover:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-900 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-900 focus:text-gray-500 dark:focus:text-gray-400 transition duration-150 ease-in-out">
|
<button @click="open = ! open"
|
||||||
|
class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 dark:text-gray-500 hover:text-gray-500 dark:hover:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-900 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-900 focus:text-gray-500 dark:focus:text-gray-400 transition duration-150 ease-in-out">
|
||||||
<svg class="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
|
<svg class="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
|
||||||
<path :class="{'hidden': open, 'inline-flex': ! open }" class="inline-flex" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
<path :class="{ 'hidden': open, 'inline-flex': !open }" class="inline-flex"
|
||||||
<path :class="{'hidden': ! open, 'inline-flex': open }" class="hidden" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M4 6h16M4 12h16M4 18h16" />
|
||||||
|
<path :class="{ 'hidden': !open, 'inline-flex': open }" class="hidden" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,22 +1,30 @@
|
|||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<label for="key" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Label (bv. Geboortedatum)</label>
|
<label for="key" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Label (bv.
|
||||||
<input type="text" id="key" name="key" value="{{ old('key', $personalia->key ?? '') }}" class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
Geboortedatum)</label>
|
||||||
|
<input type="text" id="key" name="key" value="{{ old('key', $personalia->key ?? '') }}"
|
||||||
|
class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="value" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Waarde (bv. 12 maart 1988)</label>
|
<label for="value" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Waarde (bv. 12 maart
|
||||||
<input type="text" id="value" name="value" value="{{ old('value', $personalia->value ?? '') }}" class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
1988)</label>
|
||||||
|
<input type="text" id="value" name="value" value="{{ old('value', $personalia->value ?? '') }}"
|
||||||
|
class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="icon" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Font Awesome Icon (optioneel)</label>
|
<label for="icon" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Font Awesome Icon
|
||||||
<input type="text" id="icon" name="icon" value="{{ old('icon', $personalia->icon ?? '') }}" class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded" placeholder="fa-solid fa-user">
|
(optioneel)</label>
|
||||||
|
<input type="text" id="icon" name="icon" value="{{ old('icon', $personalia->icon ?? '') }}"
|
||||||
|
class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded"
|
||||||
|
placeholder="fa-solid fa-user">
|
||||||
<p class="text-xs text-gray-500 mt-1">Gebruik een Font Awesome class zoals <code>fa-solid fa-user</code>.</p>
|
<p class="text-xs text-gray-500 mt-1">Gebruik een Font Awesome class zoals <code>fa-solid fa-user</code>.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<input type="checkbox" id="hidden" name="hidden" value="1" {{ old('hidden', $personalia->hidden ?? false) ? 'checked' : '' }} class="form-checkbox text-blue-600">
|
<input type="checkbox" id="hidden" name="hidden" value="1"
|
||||||
|
{{ old('hidden', $personalia->hidden ?? false) ? 'checked' : '' }} class="form-checkbox text-blue-600">
|
||||||
<label for="hidden" class="text-gray-700 dark:text-gray-200">Verbergen voor publiek</label>
|
<label for="hidden" class="text-gray-700 dark:text-gray-200">Verbergen voor publiek</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
<form method="POST" action="{{ route('personalia.store') }}">
|
<form method="POST" action="{{ route('personalia.store') }}">
|
||||||
@csrf
|
@csrf
|
||||||
@include('personalia._form')
|
@include('personalia._form')
|
||||||
<button type="submit" class="mt-4 px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700">Opslaan</button>
|
<button type="submit"
|
||||||
|
class="mt-4 px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700">Opslaan</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</x-app-layout>
|
</x-app-layout>
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
@csrf
|
@csrf
|
||||||
@method('PUT')
|
@method('PUT')
|
||||||
@include('personalia._form')
|
@include('personalia._form')
|
||||||
<button type="submit" class="mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">Bijwerken</button>
|
<button type="submit"
|
||||||
|
class="mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">Bijwerken</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</x-app-layout>
|
</x-app-layout>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<x-app-layout>
|
<x-app-layout>
|
||||||
<div class="p-6">
|
<div class="p-6">
|
||||||
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-4">Personalia</h2>
|
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-4">Personalia</h2>
|
||||||
<a href="{{ route('personalia.create') }}" class="mb-4 inline-block px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">Nieuw item toevoegen</a>
|
<a href="{{ route('personalia.create') }}"
|
||||||
|
class="mb-4 inline-block px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">Nieuw item toevoegen</a>
|
||||||
|
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
@foreach ($personalia as $item)
|
@foreach ($personalia as $item)
|
||||||
@@ -19,8 +20,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex space-x-2">
|
<div class="flex space-x-2">
|
||||||
<a href="{{ route('personalia.edit', $item) }}" class="text-blue-600 hover:underline">Bewerken</a>
|
<a href="{{ route('personalia.edit', $item) }}"
|
||||||
<form action="{{ route('personalia.destroy', $item) }}" method="POST" onsubmit="return confirm('Weet je zeker dat je dit item wilt verwijderen?')">
|
class="text-blue-600 hover:underline">Bewerken</a>
|
||||||
|
<form action="{{ route('personalia.destroy', $item) }}" method="POST"
|
||||||
|
onsubmit="return confirm('Weet je zeker dat je dit item wilt verwijderen?')">
|
||||||
@csrf
|
@csrf
|
||||||
@method('DELETE')
|
@method('DELETE')
|
||||||
<button class="text-red-600 hover:underline">Verwijderen</button>
|
<button class="text-red-600 hover:underline">Verwijderen</button>
|
||||||
|
|||||||
@@ -9,10 +9,8 @@
|
|||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<x-danger-button
|
<x-danger-button x-data=""
|
||||||
x-data=""
|
x-on:click.prevent="$dispatch('open-modal', 'confirm-user-deletion')">{{ __('Delete Account') }}</x-danger-button>
|
||||||
x-on:click.prevent="$dispatch('open-modal', 'confirm-user-deletion')"
|
|
||||||
>{{ __('Delete Account') }}</x-danger-button>
|
|
||||||
|
|
||||||
<x-modal name="confirm-user-deletion" :show="$errors->userDeletion->isNotEmpty()" focusable>
|
<x-modal name="confirm-user-deletion" :show="$errors->userDeletion->isNotEmpty()" focusable>
|
||||||
<form method="post" action="{{ route('profile.destroy') }}" class="p-6">
|
<form method="post" action="{{ route('profile.destroy') }}" class="p-6">
|
||||||
@@ -30,13 +28,8 @@
|
|||||||
<div class="mt-6">
|
<div class="mt-6">
|
||||||
<x-input-label for="password" value="{{ __('Password') }}" class="sr-only" />
|
<x-input-label for="password" value="{{ __('Password') }}" class="sr-only" />
|
||||||
|
|
||||||
<x-text-input
|
<x-text-input id="password" name="password" type="password" class="mt-1 block w-3/4"
|
||||||
id="password"
|
placeholder="{{ __('Password') }}" />
|
||||||
name="password"
|
|
||||||
type="password"
|
|
||||||
class="mt-1 block w-3/4"
|
|
||||||
placeholder="{{ __('Password') }}"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<x-input-error :messages="$errors->userDeletion->get('password')" class="mt-2" />
|
<x-input-error :messages="$errors->userDeletion->get('password')" class="mt-2" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,19 +15,22 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="update_password_current_password" :value="__('Current Password')" />
|
<x-input-label for="update_password_current_password" :value="__('Current Password')" />
|
||||||
<x-text-input id="update_password_current_password" name="current_password" type="password" class="mt-1 block w-full" autocomplete="current-password" />
|
<x-text-input id="update_password_current_password" name="current_password" type="password"
|
||||||
|
class="mt-1 block w-full" autocomplete="current-password" />
|
||||||
<x-input-error :messages="$errors->updatePassword->get('current_password')" class="mt-2" />
|
<x-input-error :messages="$errors->updatePassword->get('current_password')" class="mt-2" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="update_password_password" :value="__('New Password')" />
|
<x-input-label for="update_password_password" :value="__('New Password')" />
|
||||||
<x-text-input id="update_password_password" name="password" type="password" class="mt-1 block w-full" autocomplete="new-password" />
|
<x-text-input id="update_password_password" name="password" type="password" class="mt-1 block w-full"
|
||||||
|
autocomplete="new-password" />
|
||||||
<x-input-error :messages="$errors->updatePassword->get('password')" class="mt-2" />
|
<x-input-error :messages="$errors->updatePassword->get('password')" class="mt-2" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="update_password_password_confirmation" :value="__('Confirm Password')" />
|
<x-input-label for="update_password_password_confirmation" :value="__('Confirm Password')" />
|
||||||
<x-text-input id="update_password_password_confirmation" name="password_confirmation" type="password" class="mt-1 block w-full" autocomplete="new-password" />
|
<x-text-input id="update_password_password_confirmation" name="password_confirmation" type="password"
|
||||||
|
class="mt-1 block w-full" autocomplete="new-password" />
|
||||||
<x-input-error :messages="$errors->updatePassword->get('password_confirmation')" class="mt-2" />
|
<x-input-error :messages="$errors->updatePassword->get('password_confirmation')" class="mt-2" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -35,13 +38,8 @@
|
|||||||
<x-primary-button>{{ __('Save') }}</x-primary-button>
|
<x-primary-button>{{ __('Save') }}</x-primary-button>
|
||||||
|
|
||||||
@if (session('status') === 'password-updated')
|
@if (session('status') === 'password-updated')
|
||||||
<p
|
<p x-data="{ show: true }" x-show="show" x-transition x-init="setTimeout(() => show = false, 2000)"
|
||||||
x-data="{ show: true }"
|
class="text-sm text-gray-600 dark:text-gray-400">{{ __('Saved.') }}</p>
|
||||||
x-show="show"
|
|
||||||
x-transition
|
|
||||||
x-init="setTimeout(() => show = false, 2000)"
|
|
||||||
class="text-sm text-gray-600 dark:text-gray-400"
|
|
||||||
>{{ __('Saved.') }}</p>
|
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -19,13 +19,15 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="name" :value="__('Name')" />
|
<x-input-label for="name" :value="__('Name')" />
|
||||||
<x-text-input id="name" name="name" type="text" class="mt-1 block w-full" :value="old('name', $user->name)" required autofocus autocomplete="name" />
|
<x-text-input id="name" name="name" type="text" class="mt-1 block w-full" :value="old('name', $user->name)"
|
||||||
|
required autofocus autocomplete="name" />
|
||||||
<x-input-error class="mt-2" :messages="$errors->get('name')" />
|
<x-input-error class="mt-2" :messages="$errors->get('name')" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="email" :value="__('Email')" />
|
<x-input-label for="email" :value="__('Email')" />
|
||||||
<x-text-input id="email" name="email" type="email" class="mt-1 block w-full" :value="old('email', $user->email)" required autocomplete="username" />
|
<x-text-input id="email" name="email" type="email" class="mt-1 block w-full" :value="old('email', $user->email)"
|
||||||
|
required autocomplete="username" />
|
||||||
<x-input-error class="mt-2" :messages="$errors->get('email')" />
|
<x-input-error class="mt-2" :messages="$errors->get('email')" />
|
||||||
|
|
||||||
@if ($user instanceof \Illuminate\Contracts\Auth\MustVerifyEmail && !$user->hasVerifiedEmail())
|
@if ($user instanceof \Illuminate\Contracts\Auth\MustVerifyEmail && !$user->hasVerifiedEmail())
|
||||||
@@ -33,7 +35,8 @@
|
|||||||
<p class="text-sm mt-2 text-gray-800 dark:text-gray-200">
|
<p class="text-sm mt-2 text-gray-800 dark:text-gray-200">
|
||||||
{{ __('Your email address is unverified.') }}
|
{{ __('Your email address is unverified.') }}
|
||||||
|
|
||||||
<button form="send-verification" class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800">
|
<button form="send-verification"
|
||||||
|
class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800">
|
||||||
{{ __('Click here to re-send the verification email.') }}
|
{{ __('Click here to re-send the verification email.') }}
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
@@ -51,13 +54,8 @@
|
|||||||
<x-primary-button>{{ __('Save') }}</x-primary-button>
|
<x-primary-button>{{ __('Save') }}</x-primary-button>
|
||||||
|
|
||||||
@if (session('status') === 'profile-updated')
|
@if (session('status') === 'profile-updated')
|
||||||
<p
|
<p x-data="{ show: true }" x-show="show" x-transition x-init="setTimeout(() => show = false, 2000)"
|
||||||
x-data="{ show: true }"
|
class="text-sm text-gray-600 dark:text-gray-400">{{ __('Saved.') }}</p>
|
||||||
x-show="show"
|
|
||||||
x-transition
|
|
||||||
x-init="setTimeout(() => show = false, 2000)"
|
|
||||||
class="text-sm text-gray-600 dark:text-gray-400"
|
|
||||||
>{{ __('Saved.') }}</p>
|
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<label for="type" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Type</label>
|
<label for="type" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Type</label>
|
||||||
<select id="type" name="type" class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
<select id="type" name="type"
|
||||||
|
class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
||||||
<option value="rating" @selected(old('type', $skill->type ?? 'rating') === 'rating')>Rating</option>
|
<option value="rating" @selected(old('type', $skill->type ?? 'rating') === 'rating')>Rating</option>
|
||||||
<option value="tag" @selected(old('type', $skill->type ?? '') === 'tag')>Tag</option>
|
<option value="tag" @selected(old('type', $skill->type ?? '') === 'tag')>Tag</option>
|
||||||
<option value="other" @selected(old('type', $skill->type ?? '') === 'other')>Overig</option>
|
<option value="other" @selected(old('type', $skill->type ?? '') === 'other')>Overig</option>
|
||||||
@@ -9,18 +10,23 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="title" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Titel</label>
|
<label for="title" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Titel</label>
|
||||||
<input type="text" id="title" name="title" value="{{ old('title', $skill->title ?? '') }}" class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
<input type="text" id="title" name="title" value="{{ old('title', $skill->title ?? '') }}"
|
||||||
|
class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="description-field">
|
<div id="description-field">
|
||||||
|
|
||||||
<label for="description" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Beschrijving</label>
|
<label for="description" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Beschrijving</label>
|
||||||
<textarea id="description" name="description" rows="4" class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">{{ old('description', $skill->description ?? '') }}</textarea>
|
<textarea id="description" name="description" rows="4"
|
||||||
|
class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">{{ old('description', $skill->description ?? '') }}</textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="rating-field">
|
<div id="rating-field">
|
||||||
<label for="rating" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Beoordeling (1–10)</label>
|
<label for="rating" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Beoordeling
|
||||||
<input type="number" id="rating" name="rating" min="1" max="10" value="{{ old('rating', $skill->rating ?? 5) }}" class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
(1–10)</label>
|
||||||
|
<input type="number" id="rating" name="rating" min="1" max="10"
|
||||||
|
value="{{ old('rating', $skill->rating ?? 5) }}"
|
||||||
|
class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="image-field">
|
<div id="image-field">
|
||||||
@@ -33,7 +39,6 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
@push('scripts')
|
@push('scripts')
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function toggleFields() {
|
function toggleFields() {
|
||||||
const type = document.getElementById('type').value;
|
const type = document.getElementById('type').value;
|
||||||
@@ -42,7 +47,8 @@
|
|||||||
document.getElementById('rating-field').style.display = (type === 'rating') ? 'block' : 'none';
|
document.getElementById('rating-field').style.display = (type === 'rating') ? 'block' : 'none';
|
||||||
|
|
||||||
// Image tonen bij rating en other
|
// Image tonen bij rating en other
|
||||||
document.getElementById('image-field').style.display = (type === 'rating' || type === 'other') ? 'block' : 'none';
|
document.getElementById('image-field').style.display = (type === 'rating' || type === 'other') ? 'block' :
|
||||||
|
'none';
|
||||||
|
|
||||||
// Beschrijving alleen tonen bij rating
|
// Beschrijving alleen tonen bij rating
|
||||||
document.getElementById('description-field').style.display = (type === 'rating') ? 'block' : 'none';
|
document.getElementById('description-field').style.display = (type === 'rating') ? 'block' : 'none';
|
||||||
@@ -53,5 +59,4 @@
|
|||||||
toggleFields(); // initial
|
toggleFields(); // initial
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@endpush
|
@endpush
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
<form method="POST" action="{{ route('skills.store') }}" enctype="multipart/form-data">
|
<form method="POST" action="{{ route('skills.store') }}" enctype="multipart/form-data">
|
||||||
@csrf
|
@csrf
|
||||||
@include('skills._form')
|
@include('skills._form')
|
||||||
<button type="submit" class="mt-4 px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700">Opslaan</button>
|
<button type="submit"
|
||||||
|
class="mt-4 px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700">Opslaan</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</x-app-layout>
|
</x-app-layout>
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
@csrf
|
@csrf
|
||||||
@method('PUT')
|
@method('PUT')
|
||||||
@include('skills._form')
|
@include('skills._form')
|
||||||
<button type="submit" class="mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">Bijwerken</button>
|
<button type="submit"
|
||||||
|
class="mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">Bijwerken</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</x-app-layout>
|
</x-app-layout>
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
<div class="p-6 space-y-10">
|
<div class="p-6 space-y-10">
|
||||||
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white">Vaardigheden</h2>
|
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white">Vaardigheden</h2>
|
||||||
|
|
||||||
<a href="{{ route('skills.create') }}" class="inline-block px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
|
<a href="{{ route('skills.create') }}"
|
||||||
|
class="inline-block px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
|
||||||
Nieuwe vaardigheid
|
Nieuwe vaardigheid
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -28,7 +29,8 @@
|
|||||||
<h3 class="text-xl font-bold text-gray-700 dark:text-gray-200 mb-4">Tags</h3>
|
<h3 class="text-xl font-bold text-gray-700 dark:text-gray-200 mb-4">Tags</h3>
|
||||||
<div class="flex flex-wrap gap-2">
|
<div class="flex flex-wrap gap-2">
|
||||||
@foreach ($groupedSkills['tag'] as $skill)
|
@foreach ($groupedSkills['tag'] as $skill)
|
||||||
<span class="inline-block bg-blue-100 dark:bg-blue-800 text-blue-800 dark:text-blue-100 text-sm px-3 py-1 rounded">
|
<span
|
||||||
|
class="inline-block bg-blue-100 dark:bg-blue-800 text-blue-800 dark:text-blue-100 text-sm px-3 py-1 rounded">
|
||||||
{{ $skill->title }}
|
{{ $skill->title }}
|
||||||
</span>
|
</span>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
|||||||
@@ -170,6 +170,63 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(() => {
|
||||||
|
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
|
||||||
|
|
||||||
|
if (!csrfToken || !window.fetch) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const visitorKey = 'cv_visitor_id';
|
||||||
|
const createVisitorId = () => window.crypto?.randomUUID?.() ??
|
||||||
|
`${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
||||||
|
let visitorId = createVisitorId();
|
||||||
|
|
||||||
|
try {
|
||||||
|
visitorId = window.localStorage.getItem(visitorKey) || visitorId;
|
||||||
|
window.localStorage.setItem(visitorKey, visitorId);
|
||||||
|
} catch {
|
||||||
|
// Tracking should never break the page when storage is blocked.
|
||||||
|
}
|
||||||
|
|
||||||
|
window.fetch('{{ route('page-visits.store') }}', {
|
||||||
|
method: 'POST',
|
||||||
|
keepalive: true,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'X-CSRF-TOKEN': csrfToken,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
visitor_id: visitorId,
|
||||||
|
url: window.location.href,
|
||||||
|
path: window.location.pathname,
|
||||||
|
title: document.title,
|
||||||
|
referrer: document.referrer,
|
||||||
|
language: navigator.language,
|
||||||
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||||
|
screen: {
|
||||||
|
width: window.screen?.width,
|
||||||
|
height: window.screen?.height,
|
||||||
|
},
|
||||||
|
viewport: {
|
||||||
|
width: window.innerWidth,
|
||||||
|
height: window.innerHeight,
|
||||||
|
},
|
||||||
|
device_pixel_ratio: window.devicePixelRatio,
|
||||||
|
color_depth: window.screen?.colorDepth,
|
||||||
|
platform: navigator.platform,
|
||||||
|
vendor: navigator.vendor,
|
||||||
|
hardware_concurrency: navigator.hardwareConcurrency,
|
||||||
|
device_memory: navigator.deviceMemory,
|
||||||
|
cookies_enabled: navigator.cookieEnabled,
|
||||||
|
online: navigator.onLine,
|
||||||
|
}),
|
||||||
|
}).catch(() => {});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
@stack('scripts')
|
@stack('scripts')
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -4,24 +4,33 @@
|
|||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<div>
|
<div>
|
||||||
<label for="werkgever" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Werkgever</label>
|
<label for="werkgever" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Werkgever</label>
|
||||||
<input type="text" id="werkgever" name="werkgever" value="{{ old('werkgever', $workExperience->werkgever ?? '') }}" class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
<input type="text" id="werkgever" name="werkgever"
|
||||||
|
value="{{ old('werkgever', $workExperience->werkgever ?? '') }}"
|
||||||
|
class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="functie" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Functie</label>
|
<label for="functie" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Functie</label>
|
||||||
<input type="text" id="functie" name="functie" value="{{ old('functie', $workExperience->functie ?? '') }}" class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
<input type="text" id="functie" name="functie"
|
||||||
|
value="{{ old('functie', $workExperience->functie ?? '') }}"
|
||||||
|
class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<div>
|
<div>
|
||||||
<label for="startdatum" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Startdatum</label>
|
<label for="startdatum"
|
||||||
<input type="date" id="startdatum" name="startdatum" value="{{ old('startdatum', $workExperience->startdatum ?? '') }}" class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
class="block text-sm font-medium text-gray-700 dark:text-gray-200">Startdatum</label>
|
||||||
|
<input type="date" id="startdatum" name="startdatum"
|
||||||
|
value="{{ old('startdatum', $workExperience->startdatum ?? '') }}"
|
||||||
|
class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="einddatum-container">
|
<div id="einddatum-container">
|
||||||
<label for="einddatum" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Einddatum</label>
|
<label for="einddatum" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Einddatum</label>
|
||||||
<input type="date" id="einddatum" name="einddatum" value="{{ old('einddatum', $workExperience->einddatum ?? '') }}" class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
<input type="date" id="einddatum" name="einddatum"
|
||||||
|
value="{{ old('einddatum', $workExperience->einddatum ?? '') }}"
|
||||||
|
class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -35,8 +44,10 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
<label for="beschrijving" class="block text-sm font-medium text-gray-700 dark:text-gray-200">Beschrijving</label>
|
<label for="beschrijving"
|
||||||
<textarea id="beschrijving" name="beschrijving" rows="4" class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">{{ old('beschrijving', $workExperience->beschrijving ?? '') }}</textarea>
|
class="block text-sm font-medium text-gray-700 dark:text-gray-200">Beschrijving</label>
|
||||||
|
<textarea id="beschrijving" name="beschrijving" rows="4"
|
||||||
|
class="mt-1 w-full px-3 py-2 bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded">{{ old('beschrijving', $workExperience->beschrijving ?? '') }}</textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
<form method="POST" action="{{ route('work-experiences.store') }}" enctype="multipart/form-data">
|
<form method="POST" action="{{ route('work-experiences.store') }}" enctype="multipart/form-data">
|
||||||
@csrf
|
@csrf
|
||||||
@include('work_experiences._form')
|
@include('work_experiences._form')
|
||||||
<button type="submit" class="mt-4 px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700">Opslaan</button>
|
<button type="submit"
|
||||||
|
class="mt-4 px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700">Opslaan</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</x-app-layout>
|
</x-app-layout>
|
||||||
|
|||||||
@@ -2,11 +2,13 @@
|
|||||||
<x-app-layout>
|
<x-app-layout>
|
||||||
<div class="p-6">
|
<div class="p-6">
|
||||||
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-4">Werkervaring Bewerken</h2>
|
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-4">Werkervaring Bewerken</h2>
|
||||||
<form method="POST" action="{{ route('work-experiences.update', $workExperience) }}" enctype="multipart/form-data">
|
<form method="POST" action="{{ route('work-experiences.update', $workExperience) }}"
|
||||||
|
enctype="multipart/form-data">
|
||||||
@csrf
|
@csrf
|
||||||
@method('PUT')
|
@method('PUT')
|
||||||
@include('work_experiences._form')
|
@include('work_experiences._form')
|
||||||
<x-primary-button type="submit" class="mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">Bijwerken</x-primary-button>
|
<x-primary-button type="submit"
|
||||||
|
class="mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">Bijwerken</x-primary-button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</x-app-layout>
|
</x-app-layout>
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
<div class="p-6">
|
<div class="p-6">
|
||||||
<div class="flex justify-between items-center mb-4">
|
<div class="flex justify-between items-center mb-4">
|
||||||
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white">Werkervaring</h2>
|
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white">Werkervaring</h2>
|
||||||
<a href="{{ route('work-experiences.create') }}" class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">+ Nieuw</a>
|
<a href="{{ route('work-experiences.create') }}"
|
||||||
|
class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">+ Nieuw</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (session('success'))
|
@if (session('success'))
|
||||||
@@ -15,16 +16,20 @@
|
|||||||
<div class="p-4 bg-white dark:bg-gray-800 rounded shadow">
|
<div class="p-4 bg-white dark:bg-gray-800 rounded shadow">
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">{{ $experience->functie }} bij {{ $experience->werkgever }}</h3>
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">{{ $experience->functie }}
|
||||||
<p class="text-sm text-gray-600 dark:text-gray-300">{{ $experience->startdatum }} – {{ $experience->einddatum ?? 'heden' }}</p>
|
bij {{ $experience->werkgever }}</h3>
|
||||||
|
<p class="text-sm text-gray-600 dark:text-gray-300">{{ $experience->startdatum }} –
|
||||||
|
{{ $experience->einddatum ?? 'heden' }}</p>
|
||||||
<p class="mt-2 text-gray-700 dark:text-gray-200">{{ $experience->beschrijving }}</p>
|
<p class="mt-2 text-gray-700 dark:text-gray-200">{{ $experience->beschrijving }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<a href="{{ route('work-experiences.edit', $experience) }}" class="text-sitiweb-green hover:underline">Bewerken</a>
|
<a href="{{ route('work-experiences.edit', $experience) }}"
|
||||||
|
class="text-sitiweb-green hover:underline">Bewerken</a>
|
||||||
<form method="POST" action="{{ route('work-experiences.destroy', $experience) }}">
|
<form method="POST" action="{{ route('work-experiences.destroy', $experience) }}">
|
||||||
@csrf
|
@csrf
|
||||||
@method('DELETE')
|
@method('DELETE')
|
||||||
<button type="submit" class="text-red-500 hover:underline" onclick="return confirm('Weet je zeker dat je dit wilt verwijderen?')">Verwijderen</button>
|
<button type="submit" class="text-red-500 hover:underline"
|
||||||
|
onclick="return confirm('Weet je zeker dat je dit wilt verwijderen?')">Verwijderen</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ 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::post('/page-visits', [FrontendController::class, 'pageVisit'])->name('page-visits.store');
|
||||||
|
|
||||||
Route::middleware('auth')->group(function () {
|
Route::middleware('auth')->group(function () {
|
||||||
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
|
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Jobs\NotifyTelegramAboutContactMessage;
|
use App\Jobs\NotifyTelegramAboutContactMessage;
|
||||||
|
use App\Jobs\NotifyTelegramAboutPageVisit;
|
||||||
use App\Jobs\NotifyTelegramAboutPersonaliaClick;
|
use App\Jobs\NotifyTelegramAboutPersonaliaClick;
|
||||||
use App\Models\Education;
|
use App\Models\Education;
|
||||||
use App\Models\Personalia;
|
use App\Models\Personalia;
|
||||||
@@ -99,3 +100,55 @@ test('a contact message requires a name and message', function () {
|
|||||||
|
|
||||||
Queue::assertNotPushed(NotifyTelegramAboutContactMessage::class);
|
Queue::assertNotPushed(NotifyTelegramAboutContactMessage::class);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('a page visit can be submitted and is queued for notification', function () {
|
||||||
|
Queue::fake();
|
||||||
|
|
||||||
|
$this->withHeaders([
|
||||||
|
'User-Agent' => 'Pest Browser',
|
||||||
|
'Accept-Language' => 'nl-NL,nl;q=0.9',
|
||||||
|
])->postJson(route('page-visits.store'), [
|
||||||
|
'visitor_id' => 'visitor-123',
|
||||||
|
'url' => 'https://cv.robert.ooo/',
|
||||||
|
'path' => '/',
|
||||||
|
'title' => 'CV Roberto',
|
||||||
|
'referrer' => 'https://example.com',
|
||||||
|
'language' => 'nl-NL',
|
||||||
|
'timezone' => 'Europe/Amsterdam',
|
||||||
|
'screen' => [
|
||||||
|
'width' => 1920,
|
||||||
|
'height' => 1080,
|
||||||
|
],
|
||||||
|
'viewport' => [
|
||||||
|
'width' => 1440,
|
||||||
|
'height' => 900,
|
||||||
|
],
|
||||||
|
'device_pixel_ratio' => 1,
|
||||||
|
'color_depth' => 24,
|
||||||
|
'platform' => 'Linux x86_64',
|
||||||
|
'vendor' => 'Google Inc.',
|
||||||
|
'hardware_concurrency' => 8,
|
||||||
|
'device_memory' => 8,
|
||||||
|
'cookies_enabled' => true,
|
||||||
|
'online' => true,
|
||||||
|
])
|
||||||
|
->assertOk()
|
||||||
|
->assertJson([
|
||||||
|
'status' => 'queued',
|
||||||
|
]);
|
||||||
|
|
||||||
|
Queue::assertPushed(NotifyTelegramAboutPageVisit::class);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a page visit requires a visitor id and valid url', function () {
|
||||||
|
Queue::fake();
|
||||||
|
|
||||||
|
$this->postJson(route('page-visits.store'), [
|
||||||
|
'visitor_id' => '',
|
||||||
|
'url' => 'not-a-url',
|
||||||
|
])
|
||||||
|
->assertUnprocessable()
|
||||||
|
->assertJsonValidationErrors(['visitor_id', 'url']);
|
||||||
|
|
||||||
|
Queue::assertNotPushed(NotifyTelegramAboutPageVisit::class);
|
||||||
|
});
|
||||||
|
|||||||
78
tests/Feature/TelegramNotificationServiceTest.php
Normal file
78
tests/Feature/TelegramNotificationServiceTest.php
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Personalia;
|
||||||
|
use App\Services\TelegramNotificationService;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
|
test('telegram service sends a contact message notification', function () {
|
||||||
|
config([
|
||||||
|
'services.telegram.bot_token' => 'telegram-token',
|
||||||
|
'services.telegram.chat_id' => 'telegram-chat',
|
||||||
|
]);
|
||||||
|
|
||||||
|
Http::fake();
|
||||||
|
|
||||||
|
app(TelegramNotificationService::class)->notifyContactMessage(
|
||||||
|
name: 'Roberto',
|
||||||
|
message: 'Hoi, ik wil graag contact opnemen.',
|
||||||
|
ip: '127.0.0.1',
|
||||||
|
userAgent: 'Pest Browser',
|
||||||
|
email: 'roberto@example.com',
|
||||||
|
phone: '+31612345678'
|
||||||
|
);
|
||||||
|
|
||||||
|
Http::assertSent(fn ($request) => $request->url() === 'https://api.telegram.org/bottelegram-token/sendMessage'
|
||||||
|
&& $request['chat_id'] === 'telegram-chat'
|
||||||
|
&& str_contains($request['text'], 'Nieuw contactbericht ontvangen')
|
||||||
|
&& str_contains($request['text'], 'roberto@example.com'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('telegram service sends a personalia click notification', function () {
|
||||||
|
config([
|
||||||
|
'services.telegram.bot_token' => 'telegram-token',
|
||||||
|
'services.telegram.chat_id' => 'telegram-chat',
|
||||||
|
]);
|
||||||
|
|
||||||
|
Http::fake();
|
||||||
|
|
||||||
|
$personalia = Personalia::factory()->create([
|
||||||
|
'value' => 'roberto@example.com',
|
||||||
|
]);
|
||||||
|
|
||||||
|
app(TelegramNotificationService::class)->notifyPersonaliaClick($personalia, '127.0.0.1', 'Pest Browser');
|
||||||
|
|
||||||
|
Http::assertSent(fn ($request) => str_contains($request['text'], 'Persoonlijke gegevens bekeken')
|
||||||
|
&& str_contains($request['text'], 'roberto@example.com'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('telegram service sends a page visit notification', function () {
|
||||||
|
config([
|
||||||
|
'services.telegram.bot_token' => 'telegram-token',
|
||||||
|
'services.telegram.chat_id' => 'telegram-chat',
|
||||||
|
]);
|
||||||
|
|
||||||
|
Http::fake();
|
||||||
|
|
||||||
|
app(TelegramNotificationService::class)->notifyPageVisit([
|
||||||
|
'visitor_id' => 'visitor-123',
|
||||||
|
'url' => 'https://cv.robert.ooo/',
|
||||||
|
'path' => '/',
|
||||||
|
'title' => 'CV Roberto',
|
||||||
|
'referrer' => 'https://example.com',
|
||||||
|
'language' => 'nl-NL',
|
||||||
|
'timezone' => 'Europe/Amsterdam',
|
||||||
|
'screen' => [
|
||||||
|
'width' => 1920,
|
||||||
|
'height' => 1080,
|
||||||
|
],
|
||||||
|
'viewport' => [
|
||||||
|
'width' => 1440,
|
||||||
|
'height' => 900,
|
||||||
|
],
|
||||||
|
], '127.0.0.1', 'Pest Browser', 'nl-NL,nl;q=0.9');
|
||||||
|
|
||||||
|
Http::assertSent(fn ($request) => str_contains($request['text'], 'Pagina bezocht')
|
||||||
|
&& str_contains($request['text'], 'Visitor ID: visitor-123')
|
||||||
|
&& str_contains($request['text'], 'Scherm: 1920x1080')
|
||||||
|
&& str_contains($request['text'], 'Viewport: 1440x900'));
|
||||||
|
});
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
namespace Tests;
|
namespace Tests;
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Storage;
|
|
||||||
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
abstract class TestCase extends BaseTestCase
|
abstract class TestCase extends BaseTestCase
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user