caf7ad7fe2
- Wizard d'installation en 5 étapes (/setup) : prérequis PHP, base de données (PostgreSQL/MySQL avec test de connexion AJAX), paramètres app, compte admin, résultat — génère le .env, migre et crée l'administrateur - CheckInstallation middleware : redirige vers /setup si non installé, protège /setup si déjà installé ; storage/installed comme marqueur - Menu Administration : remplacé par le composant x-dropdown Breeze (même positionnement que le menu utilisateur — corrige le débordement en haut) - Logo navbar : adaptatif via h-full/py-1.5 (s'adapte à la hauteur de la barre) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
161 lines
7.4 KiB
PHP
161 lines
7.4 KiB
PHP
@extends('setup.layout')
|
|
@php $currentStep = 2; @endphp
|
|
@section('title', 'Base de données')
|
|
|
|
@section('content')
|
|
<div class="p-8"
|
|
x-data="{
|
|
driver: '{{ old('driver', $saved['driver'] ?? 'pgsql') }}',
|
|
host: '{{ old('host', $saved['host'] ?? '127.0.0.1') }}',
|
|
port: '{{ old('port', $saved['port'] ?? '5432') }}',
|
|
database: '{{ old('database', $saved['database'] ?? 'mesreleves') }}',
|
|
username: '{{ old('username', $saved['username'] ?? 'mesreleves') }}',
|
|
password: '',
|
|
testing: false,
|
|
tested: false,
|
|
testOk: false,
|
|
testMsg: '',
|
|
|
|
setDriver(d) {
|
|
this.driver = d;
|
|
this.port = d === 'pgsql' ? '5432' : '3306';
|
|
},
|
|
|
|
async testConnection() {
|
|
this.testing = true;
|
|
this.tested = false;
|
|
try {
|
|
const resp = await fetch('{{ route('setup.testDatabase') }}', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]').content,
|
|
},
|
|
body: JSON.stringify({
|
|
driver: this.driver,
|
|
host: this.host,
|
|
port: parseInt(this.port),
|
|
database: this.database,
|
|
username: this.username,
|
|
password: this.password,
|
|
}),
|
|
});
|
|
const data = await resp.json();
|
|
this.testOk = data.ok;
|
|
this.testMsg = data.message;
|
|
} catch (e) {
|
|
this.testOk = false;
|
|
this.testMsg = 'Erreur réseau : ' + e.message;
|
|
}
|
|
this.testing = false;
|
|
this.tested = true;
|
|
}
|
|
}">
|
|
|
|
<h2 class="text-xl font-semibold text-slate-800 mb-1">Configuration de la base de données</h2>
|
|
<p class="text-slate-500 text-sm mb-6">Entrez les paramètres de connexion. Testez la connexion avant de continuer.</p>
|
|
|
|
@if($errors->any())
|
|
<div class="mb-5 p-4 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700 space-y-1">
|
|
@foreach($errors->all() as $e)<p>{{ $e }}</p>@endforeach
|
|
</div>
|
|
@endif
|
|
|
|
<form method="POST" action="{{ route('setup.database.save') }}">
|
|
@csrf
|
|
<input type="hidden" name="driver" :value="driver">
|
|
<input type="hidden" name="host" :value="host">
|
|
<input type="hidden" name="port" :value="port">
|
|
|
|
{{-- Choix du moteur --}}
|
|
<div class="mb-5">
|
|
<label class="block text-sm font-medium text-slate-700 mb-2">Moteur de base de données</label>
|
|
<div class="grid grid-cols-2 gap-3">
|
|
<button type="button" @click="setDriver('pgsql')"
|
|
:class="driver === 'pgsql'
|
|
? 'border-blue-500 bg-blue-50 text-blue-700'
|
|
: 'border-slate-200 bg-white text-slate-600 hover:border-slate-300'"
|
|
class="flex items-center gap-3 px-4 py-3 rounded-xl border-2 text-sm font-medium transition text-left">
|
|
<span class="text-2xl">🐘</span>
|
|
<div>
|
|
<div class="font-semibold">PostgreSQL</div>
|
|
<div class="text-xs opacity-60">Recommandé</div>
|
|
</div>
|
|
</button>
|
|
<button type="button" @click="setDriver('mysql')"
|
|
:class="driver === 'mysql'
|
|
? 'border-blue-500 bg-blue-50 text-blue-700'
|
|
: 'border-slate-200 bg-white text-slate-600 hover:border-slate-300'"
|
|
class="flex items-center gap-3 px-4 py-3 rounded-xl border-2 text-sm font-medium transition text-left">
|
|
<span class="text-2xl">🐬</span>
|
|
<div>
|
|
<div class="font-semibold">MySQL 8+</div>
|
|
<div class="text-xs opacity-60">Alternatif</div>
|
|
</div>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Hôte + Port --}}
|
|
<div class="grid grid-cols-3 gap-4 mb-4">
|
|
<div class="col-span-2">
|
|
<label class="block text-sm font-medium text-slate-700 mb-1">Hôte</label>
|
|
<input type="text" name="host" x-model="host"
|
|
class="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-700 mb-1">Port</label>
|
|
<input type="number" name="port" x-model="port"
|
|
class="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Nom de la base --}}
|
|
<div class="mb-4">
|
|
<label class="block text-sm font-medium text-slate-700 mb-1">Nom de la base de données</label>
|
|
<input type="text" name="database" x-model="database"
|
|
class="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
</div>
|
|
|
|
{{-- Utilisateur + Mot de passe --}}
|
|
<div class="grid grid-cols-2 gap-4 mb-6">
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-700 mb-1">Utilisateur</label>
|
|
<input type="text" name="username" x-model="username"
|
|
class="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-700 mb-1">Mot de passe</label>
|
|
<input type="password" name="password" x-model="password"
|
|
class="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Bouton test --}}
|
|
<div class="mb-6">
|
|
<button type="button" @click="testConnection()" :disabled="testing"
|
|
class="w-full flex items-center justify-center gap-2 px-4 py-2.5 rounded-lg border-2 border-slate-200
|
|
bg-slate-50 text-slate-700 text-sm font-medium hover:bg-slate-100 transition disabled:opacity-50">
|
|
<span x-show="!testing">⚡ Tester la connexion</span>
|
|
<span x-show="testing" x-cloak>Test en cours…</span>
|
|
</button>
|
|
<div x-show="tested" x-cloak class="mt-3 p-3 rounded-lg text-sm flex items-start gap-2"
|
|
:class="testOk
|
|
? 'bg-green-50 text-green-800 border border-green-200'
|
|
: 'bg-red-50 text-red-800 border border-red-200'">
|
|
<span x-text="testOk ? '✓' : '✗'" class="font-bold shrink-0"></span>
|
|
<span x-text="testMsg"></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-center justify-between">
|
|
<a href="{{ route('setup.index') }}" class="text-sm text-slate-500 hover:text-slate-700">← Retour</a>
|
|
<button type="submit"
|
|
class="px-6 py-2.5 rounded-lg bg-blue-600 text-white font-medium text-sm hover:bg-blue-700 transition">
|
|
Suivant →
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
@endsection
|