Compare commits

...

3 Commits

Author SHA1 Message Date
ca992019e5 feat: input component
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
2025-11-24 09:51:31 -05:00
7a6fde5581 feat: Additional specimen functionality 2025-11-24 09:51:22 -05:00
1c26a7801c feat: add basic specimen functionality 2025-11-24 09:51:00 -05:00
11 changed files with 288 additions and 2 deletions

View File

@ -0,0 +1,105 @@
<?php
namespace App\Livewire\Forms;
use App\Models\Specimen;
use Livewire\Attributes\Validate;
use Livewire\Form;
class SpecimenForm extends Form
{
#[Validate('required')]
public string $date = '';
#[Validate('required')]
public string $family = '';
#[Validate('required')]
public string $genus = '';
#[Validate('required')]
public string $specificEpithet = '';
#[Validate('required')]
public string $collector = '';
#[Validate('required')]
public string $collectionNumber = '';
public array $associateCollectors = [];
#[Validate('required')]
public string $plantHabit = '';
#[Validate('required')]
public string $populationSize = '';
#[Validate('required')]
public string $substrate = '';
#[Validate('required')]
public string $phenologyFlowering = '';
#[Validate('required')]
public string $phenologyFruiting = '';
#[Validate('required')]
public string $phenologyFruitingFlowering = '';
#[Validate('required')]
public string $phenologyVegetative = '';
public string $phenologyNotes = '';
#[Validate('required')]
public string $vegetationCommunity = '';
public array $plantAssociates = [];
#[Validate('required')]
public string $aspect = '';
#[Validate('required')]
public string $exposure = '';
#[Validate('required')]
public string $elevation = '';
#[Validate('required')]
public string $location = '';
public string $latitude = '';
public string $longitude = '';
public function store()
{
$this->validate();
Specimen::create($this->only([
'date',
'family',
'genus',
'specificEpithet',
'collector',
'collectionNumber',
'associateCollectors',
'plantHabit',
'populationSize',
'substrate',
'phenologyFlowering',
'phenologyFruiting',
'phenologyFruitingFlowering',
'phenologyVegetative',
'phenologyNotes',
'vegetationCommunity',
'plantAssociates',
'aspect',
'exposure',
'elevation',
'location',
'latitude',
'longitude',
]));
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace App\Livewire\Herbarium;
use App\Livewire\Forms\SpecimenForm;
use Livewire\Component;
class CreateSpecimen extends Component
{
public SpecimenForm $form;
public function render()
{
return view('livewire.herbarium.create-specimen');
}
public function save()
{
$this->form->store();
return $this->redirect('/herbarium/list');
}
}

View File

@ -5,6 +5,7 @@
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOne;
class Specimen extends Model
{
@ -13,6 +14,43 @@ class Specimen extends Model
protected $table = 'specimens';
protected $fillable = [
''
]
'date',
'family',
'genus',
'specific_epithet',
'collector',
'collection_number',
'associate_collectors',
'plant_habit', // herb, vine, shrub, liana, shrub, subshrub, tree
'population_size',
'substrate',
'phenology_flowering',
'phenology_fruiting',
'phenology_fruiting_flowering',
'phenology_notes',
'phenology_vegetative',
'vegetation_community',
'plant_associates',
'aspect',
'exposure',
'elevation',
'location',
'latitude',
'longitude',
'user_id',
];
public function user(): HasOne
{
return $this->hasOne(User::class,'id','user_id');
}
protected function casts(): array
{
return [
'date' => 'date',
'associate_collectors' => 'array',
'plant_associates' => 'array',
];
}
}

View File

@ -4,6 +4,7 @@
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Str;
@ -37,6 +38,11 @@ class User extends Authenticatable
'remember_token',
];
public function specimens(): HasMany
{
return $this->hasMany(Specimen::class);
}
/**
* Get the attributes that should be cast.
*

View File

@ -0,0 +1,26 @@
<?php
namespace App\View\Components\Forms;
use Closure;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;
class textInput extends Component
{
/**
* Create a new component instance.
*/
public function __construct()
{
//
}
/**
* Get the view / contents that represent the component.
*/
public function render(): View|Closure|string
{
return view('components.forms.text-input');
}
}

View File

@ -0,0 +1,50 @@
<?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('specimen', function (Blueprint $table) {
$table->id()->primary();
$table->date('date');
$table->string('family');
$table->string('genus');
$table->string('specific_epithet');
$table->string('collector');
$table->string('collection_number');
$table->text('associate_collectors')->nullable();
$table->string('plant_habit');
$table->text('population_size');
$table->text('substrate');
$table->string('phenology_flowering');
$table->string('phenology_fruiting');
$table->string('phenology_fruiting_flowering');
$table->string('phenology_vegetative');
$table->text('phenology_notes')->nullable();
$table->text('vegetation_community');
$table->text('plant_associates')->nullable();
$table->string('aspect');
$table->string('exposure');
$table->string('elevation');
$table->text('location');
$table->string('latitude')->nullable();
$table->string('longitude')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('specimen');
}
};

View File

@ -0,0 +1,15 @@
@props([
'name',
'description',
])
<div>
<div>
@error($name) <span class="error">{{ $message }}</span> @enderror
</div>
<flux:field type="text" name="{{ $name }}" {{ $attributes }}>
<flux:label>{{ $name }}</flux:label>
<flux:description>{{ $description }}</flux:description>
<flux:input />
</flux:field>
</div>

View File

@ -15,6 +15,10 @@
<flux:navlist.group :heading="__('Platform')" class="grid">
<flux:navlist.item icon="home" :href="route('dashboard')" :current="request()->routeIs('dashboard')" wire:navigate>{{ __('Dashboard') }}</flux:navlist.item>
</flux:navlist.group>
<flux:navlist.group :heading="__('Specimens')" class="grid">
<flux:navlist.item :href="route('specimen.create')" :current="request()->routeIs('specimen.create')" wire:navigate>{{ __('Add New') }}</flux:navlist.item>
</flux:navlist.group>
</flux:navlist>
<flux:spacer />

View File

@ -0,0 +1,5 @@
<div>
<h1>Specimens - {{ $header }}</h1>
<flux:separator class="my-8" />
{{ $slot }}
</div>

View File

@ -0,0 +1,9 @@
<div>
<form wire:submit="save">
@csrf
<x-forms.text-input name="test" description="testing" wire:model="form.genus" />
<button type="submit">Save</button>
</form>
</div>

View File

@ -1,5 +1,6 @@
<?php
use App\Livewire\Herbarium\CreateSpecimen;
use App\Livewire\Settings\Appearance;
use App\Livewire\Settings\Password;
use App\Livewire\Settings\Profile;
@ -33,3 +34,7 @@
)
->name('two-factor.show');
});
Route::middleware(['auth', 'verified'])->group(function () {
Route::get('specimens/create', CreateSpecimen::class)->name('specimen.create');
});