Étapes 6-9 + types de lieux + picker + filtres
- Étape 6 : formulaire de saisie dynamique des relevés (piloté par source_type_fields, calendriers grégorien/julien/républicain) - Étape 7 : workflow de statut des sources + notifications mail+DB (SourceAValider, SourceRejetee) - Étape 8 : recherche fulltext PostgreSQL avec filtres type/lieu/années et CTE récursive pour les subdivisions de lieux - Étape 9 : export GEDCOM 5.5.1 (GedcomExportService + DateConversionService) - Types de lieux : CRUD admin (LieuTypeController) avec champ ordre - Composant lieu-picker : modale Alpine.js avec recherche AJAX + debounce - Filtres sources : statut, type, lieu (CTE récursive), période annee_debut/annee_fin - Filtres lieux : type, texte, lieu parent avec descendants (CTE récursive) - Migration : lieu_id + annee_debut + annee_fin sur sources Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('lieu_types', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('nom')->unique();
|
||||
$table->unsignedSmallInteger('ordre')->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('lieu_types');
|
||||
}
|
||||
};
|
||||
@@ -10,10 +10,11 @@ return new class extends Migration
|
||||
{
|
||||
Schema::create('lieux', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('lieu_type_id')->nullable()->constrained('lieu_types')->nullOnDelete();
|
||||
$table->string('nom');
|
||||
$table->string('code')->nullable();
|
||||
$table->foreignId('lieu_parent_id')->nullable()->constrained('lieux')->nullOnDelete();
|
||||
$table->string('nom_long')->nullable(); // calculé : "Bordeaux, Gironde, France"
|
||||
$table->string('nom_long')->nullable();
|
||||
$table->decimal('latitude', 10, 7)->nullable();
|
||||
$table->decimal('longitude', 10, 7)->nullable();
|
||||
$table->text('note')->nullable();
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('notifications', function (Blueprint $table) {
|
||||
$table->uuid('id')->primary();
|
||||
$table->string('type');
|
||||
$table->morphs('notifiable');
|
||||
$table->text('data');
|
||||
$table->timestamp('read_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('notifications');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('sources', function (Blueprint $table) {
|
||||
$table->foreignId('lieu_id')->nullable()->after('depot_id')->constrained('lieux')->nullOnDelete();
|
||||
$table->unsignedSmallInteger('annee_debut')->nullable()->after('lieu_id');
|
||||
$table->unsignedSmallInteger('annee_fin')->nullable()->after('annee_debut');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('sources', function (Blueprint $table) {
|
||||
$table->dropForeign(['lieu_id']);
|
||||
$table->dropColumn(['lieu_id', 'annee_debut', 'annee_fin']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -2,24 +2,164 @@
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Enums\FieldType;
|
||||
use App\Enums\SourceStatus;
|
||||
use App\Enums\UserRole;
|
||||
use App\Models\Depot;
|
||||
use App\Models\Lieu;
|
||||
use App\Models\LieuType;
|
||||
use App\Models\Releve;
|
||||
use App\Models\Section;
|
||||
use App\Models\Source;
|
||||
use App\Models\SourceType;
|
||||
use App\Models\SourceTypeField;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class DatabaseSeeder extends Seeder
|
||||
{
|
||||
use WithoutModelEvents;
|
||||
|
||||
/**
|
||||
* Seed the application's database.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// User::factory(10)->create();
|
||||
// ── Utilisateurs ────────────────────────────────────────────────────
|
||||
$admin = User::create([
|
||||
'name' => 'Administrateur',
|
||||
'email' => 'admin@mesreleves.local',
|
||||
'password' => Hash::make('password'),
|
||||
'role' => UserRole::Admin,
|
||||
]);
|
||||
|
||||
User::factory()->create([
|
||||
'name' => 'Test User',
|
||||
'email' => 'test@example.com',
|
||||
$responsable = User::create([
|
||||
'name' => 'Responsable Bordeaux',
|
||||
'email' => 'responsable@mesreleves.local',
|
||||
'password' => Hash::make('password'),
|
||||
'role' => UserRole::SectionManager,
|
||||
]);
|
||||
|
||||
$membre = User::create([
|
||||
'name' => 'Marie Durand',
|
||||
'email' => 'membre@mesreleves.local',
|
||||
'password' => Hash::make('password'),
|
||||
'role' => UserRole::Member,
|
||||
]);
|
||||
|
||||
// ── Types de lieux ──────────────────────────────────────────────────
|
||||
$typePays = LieuType::create(['nom' => 'Pays', 'ordre' => 0]);
|
||||
$typeRegion = LieuType::create(['nom' => 'Région', 'ordre' => 10]);
|
||||
$typeDept = LieuType::create(['nom' => 'Département', 'ordre' => 20]);
|
||||
$typeVille = LieuType::create(['nom' => 'Ville', 'ordre' => 30]);
|
||||
$typeCommune = LieuType::create(['nom' => 'Commune', 'ordre' => 35]);
|
||||
$typeParoisse = LieuType::create(['nom' => 'Paroisse', 'ordre' => 40]);
|
||||
$typeLieuDit = LieuType::create(['nom' => 'Lieu-dit', 'ordre' => 50]);
|
||||
|
||||
// ── Lieux ───────────────────────────────────────────────────────────
|
||||
$france = Lieu::create(['nom' => 'France', 'lieu_type_id' => $typePays->id]);
|
||||
$gironde = Lieu::create(['nom' => 'Gironde', 'code' => '33', 'lieu_type_id' => $typeDept->id, 'lieu_parent_id' => $france->id]);
|
||||
$bordeaux = Lieu::create(['nom' => 'Bordeaux', 'code' => '33063', 'lieu_type_id' => $typeVille->id, 'lieu_parent_id' => $gironde->id,
|
||||
'latitude' => 44.8378, 'longitude' => -0.5792]);
|
||||
$begles = Lieu::create(['nom' => 'Bègles', 'code' => '33032', 'lieu_type_id' => $typeCommune->id, 'lieu_parent_id' => $gironde->id]);
|
||||
|
||||
// ── Section ─────────────────────────────────────────────────────────
|
||||
$section = Section::create([
|
||||
'nom' => 'Section Gironde',
|
||||
'lieu_id' => $bordeaux->id,
|
||||
'email_contact' => 'gironde@cgl.fr',
|
||||
]);
|
||||
$section->membres()->attach($responsable->id, ['role_in_section' => 'section_manager']);
|
||||
$section->membres()->attach($membre->id, ['role_in_section' => 'member']);
|
||||
|
||||
// ── Dépôt ───────────────────────────────────────────────────────────
|
||||
$depot = Depot::create([
|
||||
'nom' => 'Archives Départementales de la Gironde',
|
||||
'adresse_postale' => '72 cours Balguerie-Stuttenberg, 33000 Bordeaux',
|
||||
'url' => 'https://archives.gironde.fr',
|
||||
]);
|
||||
|
||||
// ── Type de source : État civil Naissance ────────────────────────────
|
||||
$stNaissance = SourceType::create([
|
||||
'nom' => 'État civil — Naissance',
|
||||
'description' => 'Actes de naissance (format post-révolutionnaire)',
|
||||
]);
|
||||
|
||||
$champsNaissance = [
|
||||
['name' => 'nom', 'label' => 'Nom', 'type' => FieldType::Text, 'required' => true, 'order' => 0],
|
||||
['name' => 'prenom', 'label' => 'Prénom(s)', 'type' => FieldType::Text, 'required' => true, 'order' => 1],
|
||||
['name' => 'date_evenement', 'label' => 'Date de naissance', 'type' => FieldType::Date, 'required' => true, 'order' => 2],
|
||||
['name' => 'lieu_naissance', 'label' => 'Lieu de naissance', 'type' => FieldType::Text, 'required' => false, 'order' => 3],
|
||||
['name' => 'numero_acte', 'label' => 'N° acte', 'type' => FieldType::Number, 'required' => false, 'order' => 4],
|
||||
['name' => 'nom_pere', 'label' => 'Nom du père', 'type' => FieldType::Text, 'required' => false, 'order' => 5],
|
||||
['name' => 'prenom_pere', 'label' => 'Prénom du père', 'type' => FieldType::Text, 'required' => false, 'order' => 6],
|
||||
['name' => 'nom_mere', 'label' => 'Nom de la mère', 'type' => FieldType::Text, 'required' => false, 'order' => 7],
|
||||
['name' => 'prenom_mere', 'label' => 'Prénom de la mère', 'type' => FieldType::Text, 'required' => false, 'order' => 8],
|
||||
['name' => 'note', 'label' => 'Note', 'type' => FieldType::Textarea, 'required' => false, 'order' => 9],
|
||||
];
|
||||
|
||||
foreach ($champsNaissance as $champ) {
|
||||
SourceTypeField::create(['source_type_id' => $stNaissance->id, ...$champ]);
|
||||
}
|
||||
|
||||
// ── Type de source : Mariage ─────────────────────────────────────────
|
||||
$stMariage = SourceType::create(['nom' => 'État civil — Mariage']);
|
||||
|
||||
$champsMariage = [
|
||||
['name' => 'nom_epoux', 'label' => 'Nom de l\'époux', 'type' => FieldType::Text, 'required' => true, 'order' => 0],
|
||||
['name' => 'prenom_epoux', 'label' => 'Prénom de l\'époux','type' => FieldType::Text, 'required' => true, 'order' => 1],
|
||||
['name' => 'nom_epouse', 'label' => 'Nom de l\'épouse', 'type' => FieldType::Text, 'required' => true, 'order' => 2],
|
||||
['name' => 'prenom_epouse', 'label' => 'Prénom de l\'épouse','type'=> FieldType::Text, 'required' => true, 'order' => 3],
|
||||
['name' => 'date_mariage', 'label' => 'Date du mariage', 'type' => FieldType::Date, 'required' => true, 'order' => 4],
|
||||
['name' => 'numero_acte', 'label' => 'N° acte', 'type' => FieldType::Number,'required'=> false, 'order' => 5],
|
||||
['name' => 'note', 'label' => 'Note', 'type' => FieldType::Textarea,'required'=> false,'order'=> 6],
|
||||
];
|
||||
|
||||
foreach ($champsMariage as $champ) {
|
||||
SourceTypeField::create(['source_type_id' => $stMariage->id, ...$champ]);
|
||||
}
|
||||
|
||||
// ── Source : naissances Bordeaux (en cours, membre assigné) ──────────
|
||||
$sourceNaissances = Source::create([
|
||||
'nom' => 'Naissances Bordeaux 1820–1830',
|
||||
'source_type_id' => $stNaissance->id,
|
||||
'depot_id' => $depot->id,
|
||||
'cote' => '4E 756',
|
||||
'auteur' => 'Marie Durand',
|
||||
'status' => SourceStatus::EnCours,
|
||||
]);
|
||||
$sourceNaissances->membres()->attach($membre->id);
|
||||
|
||||
// ── Relevés de la source naissances ──────────────────────────────────
|
||||
$releves = [
|
||||
['nom' => 'DUPONT', 'prenom' => 'Jean Marie', 'date_evenement' => ['valeur' => '1821-04-03', 'calendrier' => 'gregorien'], 'lieu_naissance' => 'Bordeaux', 'numero_acte' => 12, 'nom_pere' => 'DUPONT', 'prenom_pere' => 'Pierre', 'nom_mere' => 'MARTIN', 'prenom_mere' => 'Jeanne', 'note' => null],
|
||||
['nom' => 'MARTIN', 'prenom' => 'Marie Anne', 'date_evenement' => ['valeur' => '1822-07-15', 'calendrier' => 'gregorien'], 'lieu_naissance' => 'Bordeaux', 'numero_acte' => 34, 'nom_pere' => 'MARTIN', 'prenom_pere' => 'Louis', 'nom_mere' => 'BERNARD', 'prenom_mere' => 'Claire', 'note' => null],
|
||||
['nom' => 'BERNARD', 'prenom' => 'Pierre Louis', 'date_evenement' => ['valeur' => '1823-01-28', 'calendrier' => 'gregorien'], 'lieu_naissance' => 'Bègles', 'numero_acte' => 8, 'nom_pere' => 'BERNARD', 'prenom_pere' => 'Jacques', 'nom_mere' => 'LEBRUN', 'prenom_mere' => 'Marie', 'note' => 'Né prématurément'],
|
||||
['nom' => 'LEBRUN', 'prenom' => 'Céleste', 'date_evenement' => ['valeur' => '6 Vendémiaire An XI', 'calendrier' => 'republicain'], 'lieu_naissance' => 'Bordeaux', 'numero_acte' => 51, 'nom_pere' => null, 'prenom_pere' => null, 'nom_mere' => 'LEBRUN', 'prenom_mere' => 'Angélique', 'note' => 'Père inconnu'],
|
||||
['nom' => 'ROUX', 'prenom' => 'Henri Gustave', 'date_evenement' => ['valeur' => '1825-11-02', 'calendrier' => 'gregorien'], 'lieu_naissance' => 'Bordeaux', 'numero_acte' => 89, 'nom_pere' => 'ROUX', 'prenom_pere' => 'Étienne', 'nom_mere' => 'PETIT', 'prenom_mere' => 'Louise', 'note' => null],
|
||||
];
|
||||
|
||||
foreach ($releves as $data) {
|
||||
Releve::create([
|
||||
'source_id' => $sourceNaissances->id,
|
||||
'created_by' => $membre->id,
|
||||
'updated_by' => $membre->id,
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
// ── Source : mariages (à valider) ────────────────────────────────────
|
||||
Source::create([
|
||||
'nom' => 'Mariages Bordeaux 1815–1820',
|
||||
'source_type_id' => $stMariage->id,
|
||||
'depot_id' => $depot->id,
|
||||
'cote' => '4E 750',
|
||||
'status' => SourceStatus::AValider,
|
||||
]);
|
||||
|
||||
// ── Source : à faire ─────────────────────────────────────────────────
|
||||
Source::create([
|
||||
'nom' => 'Naissances Bègles 1830–1840',
|
||||
'source_type_id' => $stNaissance->id,
|
||||
'depot_id' => $depot->id,
|
||||
'cote' => '4E 820',
|
||||
'status' => SourceStatus::AFaire,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user