Add Filaments module with Seeder, update Calculator UI for filament selection, and upgrade dependencies for Livewire Flux

This commit is contained in:
2026-01-25 11:49:17 +01:00
parent ec37f6b421
commit b1e2ab8978
6 changed files with 249 additions and 17 deletions
+56 -1
View File
@@ -8,16 +8,71 @@ use App\Models\Calculationpreset as Model;
use Livewire\WithPagination;
use Livewire\Attributes\title;
use App\Models\Filament;
use App\Models\FilamentType;
use App\Models\Manufacturer;
use App\Models\Color;
#[title('Calculator')]
class Calculator extends Component
{
use WithPagination;
public Model $calculationpreset ;
public string $selection = '1';
public float $time = 0;
public string $timeunit = 'h';
public float $additionalcost = 0;
public string $additionalcostunit = 'per Piece';
public int $countonplate = 0;
public int $selcountonplate = 0;
public string $search_type = '';
public string $search_manufacturer = '';
public string $search_name = '';
public string $search_color = '';
public array $selectedFilaments = []; // [filament_id => amount_g]
public function toggleFilament($filamentId)
{
if (isset($this->selectedFilaments[$filamentId])) {
unset($this->selectedFilaments[$filamentId]);
} else {
$this->selectedFilaments[$filamentId] = 0;
}
}
public function render()
{
$filaments = Filament::query()
->where(function ($query) {
$query->when($this->search_type, function ($query) {
$query->whereHas('type', function ($q) {
$q->where('name', $this->search_type);
});
})
->when($this->search_manufacturer, function ($query) {
$query->whereHas('manufacturer', function ($q) {
$q->where('name', 'like', '%' . $this->search_manufacturer . '%');
});
})
->when($this->search_name, function ($query) {
$query->where('name', 'like', '%' . $this->search_name . '%');
})
->when($this->search_color, function ($query) {
$query->whereHas('color', function ($q) {
$q->where('name', 'like', '%' . $this->search_color . '%');
});
});
})
->orWhereIn('id', array_keys($this->selectedFilaments))
->with(['type', 'manufacturer', 'color'])
->get();
return view('livewire.calculator', [
'presets' => \App\Models\Calculationpreset::all(),
'calculationpreset' => \App\Models\Calculationpreset::find($this->selection),
'filaments' => $filaments,
'filamenttypes' => FilamentType::all(),
]);
}
}
+4 -4
View File
@@ -13,7 +13,7 @@ class Filament extends Model
protected $fillable = [
'name',
'description',
'type_id',
'filament_type_id',
'manufacturer_id',
'color_id',
'weight',
@@ -31,19 +31,19 @@ class Filament extends Model
// Filament belongs to a type
public function type()
{
return $this->hasOne(FilamentType::class);
return $this->belongsTo(FilamentType::class, 'filament_type_id');
}
// Filament belongs to a manufacturer
public function manufacturer()
{
return $this->hasOne(Manufacturer::class);
return $this->belongsTo(Manufacturer::class);
}
// Filament belongs to a color
public function color()
{
return $this->hasOne(Color::class);
return $this->belongsTo(Color::class);
}
}
Generated
+7 -7
View File
@@ -2318,16 +2318,16 @@
},
{
"name": "livewire/flux",
"version": "v2.10.2",
"version": "v2.11.1",
"source": {
"type": "git",
"url": "https://github.com/livewire/flux.git",
"reference": "e7a93989788429bb6c0a908a056d22ea3a6c7975"
"reference": "3ada3b2644215fd1ccb7003ce8e6bc185c22e70a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/livewire/flux/zipball/e7a93989788429bb6c0a908a056d22ea3a6c7975",
"reference": "e7a93989788429bb6c0a908a056d22ea3a6c7975",
"url": "https://api.github.com/repos/livewire/flux/zipball/3ada3b2644215fd1ccb7003ce8e6bc185c22e70a",
"reference": "3ada3b2644215fd1ccb7003ce8e6bc185c22e70a",
"shasum": ""
},
"require": {
@@ -2335,7 +2335,7 @@
"illuminate/support": "^10.0|^11.0|^12.0",
"illuminate/view": "^10.0|^11.0|^12.0",
"laravel/prompts": "^0.1|^0.2|^0.3",
"livewire/livewire": "^3.7.3|^4.0",
"livewire/livewire": "^3.7.4|^4.0",
"php": "^8.1",
"symfony/console": "^6.0|^7.0"
},
@@ -2378,9 +2378,9 @@
],
"support": {
"issues": "https://github.com/livewire/flux/issues",
"source": "https://github.com/livewire/flux/tree/v2.10.2"
"source": "https://github.com/livewire/flux/tree/v2.11.1"
},
"time": "2025-12-19T02:11:45+00:00"
"time": "2026-01-21T17:09:56+00:00"
},
{
"name": "livewire/livewire",
+1
View File
@@ -32,6 +32,7 @@ class DatabaseSeeder extends Seeder
BuildplateSeeder::class,
CalculationpresetSeeder::class,
PrintertypeSeeder::class,
FilamentSeeder::class,
]);
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class FilamentSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// Seed predefined Filaments
$filaments = [
['name' => 'Bambu PLA Basic', 'manufacturer_id' => 1, 'filament_type_id' => 1, 'color_id' => 10, 'temp_max' => 230, 'temp_min' => 180, 'price' => 19.99, 'weight' => 1000, 'diameter' => '1.75'],
['name' => 'Bambu PLA Basic', 'manufacturer_id' => 1, 'filament_type_id' => 1, 'color_id' => 11, 'temp_max' => 230, 'temp_min' => 180, 'price' => 19.99, 'weight' => 1000, 'diameter' => '1.75'],
];
foreach ($filaments as $filament) {
\App\Models\Filament::create($filament);
}
}
}
+154 -4
View File
@@ -3,15 +3,165 @@
{{ __('Calculator') }}
<x-slot name="headerleft">
<flux:select wire:model.live="calculationpreset" placeholder="Select a preset">
@foreach ( \App\Models\Calculationpreset::all() as $preset)
<flux:select wire:model.live="selection" placeholder="Select a preset">
@foreach ( $presets as $preset)
<flux:select.option value="{{ $preset->id }}">{{ $preset->name }}</flux:select.option>
@endforeach
</flux:select>
</x-slot>
</x-header>
<div class="container m-auto my-8 bg-elephant border border-elephant-900 rounded-lg shadow p-6 text-gray-50">
{{ $preset->name }} : {{ $preset->price_kwh }}
@if($calculationpreset)
<div class="container m-auto">
<div class="grid grid-cols-3 gap-4">
<div class="container m-auto my-8 bg-elephant border border-elephant-900 rounded-lg shadow p-6 text-gray-50 col-span-2">
<div class="pr-4 flex flex-col gap-4">
<div class="border-b border-elephant-900 pb-4">
<flux:heading>Inputs</flux:heading>
<flux:text class="mt-2">Enter your calculation data.</flux:text>
</div>
<div class="flex gap-2 pb-4 border-b border-elephant-900">
<flux:field>
<flux:label>Time</flux:label>
<flux:input.group>
<flux:input wire:model.live.debounce="time" type="number"/>
<flux:select class="max-w-fit" wire:model.lazy="timeunit">
<flux:select.option selected>h</flux:select.option>
<flux:select.option>min</flux:select.option>
</flux:select>
</flux:input.group>
</flux:field>
</div>
<div class="flex gap-2 pb-4 border-b border-elephant-900 flex-col">
<div>
<flux:heading>Count on Plate</flux:heading>
<flux:text class="mt-2">More Objects per Plate?</flux:text>
</div>
<div>
<flux:input.group class="flex gap-2">
<flux:label>Enable</flux:label>
<flux:checkbox wire:model.lazy="selcountonplate" />
</flux:input.group>
</div>
@if($selcountonplate)
<div class="flex gap-2 flex-col px-8">
<flux:field>
<flux:label>Count on Plate</flux:label>
<flux:input wire:model.lazy="countonplate" type="number"/>
</flux:field>
<flux:field>
<flux:label>Additional Cost</flux:label>
<flux:input.group>
<flux:input wire:model.live.debounce="additionalcost" type="number"/>
<flux:select class="max-w-fit" wire:model.lazy="additionalcostunit">
<flux:select.option selected>per Piece</flux:select.option>
<flux:select.option>per Plate</flux:select.option>
</flux:select>
</flux:input.group>
</flux:field>
</div>
@endif
</div>
<div>
<flux:heading>Filament Selection</flux:heading>
<flux:text class="my-2">Filter by manufacturer, filament type and color.</flux:text>
<div class="my-2">
<flux:table>
<flux:table.columns>
<flux:table.column> Select </flux:table.column>
<flux:table.column> Type </flux:table.column>
<flux:table.column> Manufacturer </flux:table.column>
<flux:table.column> Name </flux:table.column>
<flux:table.column> Color </flux:table.column>
<flux:table.column> Amount (g) </flux:table.column>
</flux:table.columns>
<flux:table.rows class="bg-black/50">
<flux:table.row>
<flux:table.cell class="w-5"></flux:table.cell>
<flux:table.cell>
<flux:input wire:model.live="search_type" placeholder="Type" />
</flux:table.cell>
<flux:table.cell>
<flux:input wire:model.live.debounce="search_manufacturer" placeholder="Manufacturer" />
</flux:table.cell>
<flux:table.cell>
<flux:input wire:model.live.debounce="search_name" placeholder="Name" />
</flux:table.cell>
<flux:table.cell>
<flux:input wire:model.live.debounce="search_color" placeholder="Color" />
</flux:table.cell>
<flux:table.cell></flux:table.cell>
</flux:table.row>
@foreach ($filaments as $filament)
<flux:table.row :key="$filament->id" :class="isset($selectedFilaments[$filament->id]) ? 'bg-elephant-900' : ''">
<flux:table.cell class="w-5">
<flux:checkbox wire:click="toggleFilament({{ $filament->id }})" :checked="isset($selectedFilaments[$filament->id])" />
</flux:table.cell>
<flux:table.cell>{{ $filament->type?->name ?? 'N/A' }}</flux:table.cell>
<flux:table.cell>{{ $filament->manufacturer?->name ?? 'N/A' }}</flux:table.cell>
<flux:table.cell>{{ $filament->name }}</flux:table.cell>
<flux:table.cell>
<div class="flex items-center gap-2">
<div class="w-4 h-4 rounded-full border border-gray-500" style="background-color: {{ '#' . $filament->color->hex ?? 'transparent' }}"></div>
{{ $filament->color?->name ?? 'N/A' }}
</div>
</flux:table.cell>
<flux:table.cell class="w-7">
@if(isset($selectedFilaments[$filament->id]))
<flux:input type="number" wire:model.live="selectedFilaments.{{ $filament->id }}"/>
@endif
</flux:table.cell>
</flux:table.row>
@endforeach
</flux:table.rows>
</flux:table>
</div>
</div>
</div>
</div>
<div class="container m-auto my-8 bg-gold-drop-800 border border-gold-drop-600 rounded-lg shadow p-6 text-gray-50">
<div>
<div class="border-b border-gold-drop-600 pb-4">
<flux:heading>Outputs</flux:heading>
<flux:text class="mt-2">Get your ready for use Prices.</flux:text>
</div>
@php
$totalFilamentCost = 0;
foreach ($selectedFilaments as $id => $amount) {
$f = $filaments->firstWhere('id', $id);
if ($f && $f->price && $f->weight) {
$totalFilamentCost += ($f->price / $f->weight) * $amount;
}
}
$energyCost = ($calculationpreset->price_kwh ?? 0) * 0.25 * ($timeunit == 'h' ? $time : $time / 60);
$printerCost = ($calculationpreset->factor_printer ?? 0) * ($timeunit == 'h' ? $time : $time / 60);
$maintenanceCost = ($calculationpreset->factor_maintenance ?? 0) * ($timeunit == 'h' ? $time : $time / 60);
$totalCost = $totalFilamentCost + $energyCost + $printerCost + $maintenanceCost + ($additionalcostunit == 'per Piece' ? $additionalcost : $additionalcost / max(1, $countonplate ?? 1));
@endphp
<div class="mt-4 space-y-2">
<p>Time: {{ $time }}{{ $timeunit }}</p>
<p>Filament Cost: {{ number_format($totalFilamentCost, 2) }}</p>
<p>Energy Cost: {{ number_format($energyCost, 2) }}</p>
<p>Printer Cost: {{ number_format($printerCost, 2) }}</p>
<p>Maintenance Cost: {{ number_format($maintenanceCost, 2) }}</p>
@if($countonplate > 0 )
<div class="border-t border-gold-drop-600 flex flex-col gap-2 py-4">
<p>Additional Cost: {{ $additionalcost }} ({{ $additionalcostunit }})</p>
<flux:heading size="lg">Per Item Cost: {{ number_format($totalCost / $countonplate, 2) }} | {{ number_format(($totalCost * ($calculationpreset->factor_price / 100)/ $countonplate), 2) }}</flux:heading>
</div>
@endif
<div class="border-t border-gold-drop-600 flex flex-col gap-2 py-4">
<flux:heading size="lg" >Total Cost: {{ number_format($totalCost, 2) }} | {{ number_format($totalCost * ($calculationpreset->factor_price / 100), 2) }}</flux:heading>
</div>
</div>
</div>
</div>
</div>
</div>
@endif
</div>