Skip to content

Commit 598a5f6

Browse files
Multi Step Form Wizard
1 parent dff7203 commit 598a5f6

17 files changed

+402
-0
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
<?php
2+
3+
namespace App\Http\Livewire;
4+
5+
use App\Models\Customer;
6+
use App\Models\Invoice;
7+
use Livewire\Component;
8+
use Illuminate\Support\Facades\Log;
9+
10+
class CreateInvoice extends Component
11+
{
12+
public $totalSteps = 3;
13+
public $step = 1;
14+
15+
//Fields to store
16+
public $customer_id = '';
17+
public $isFixed = true;
18+
public $amount = 0;
19+
public $hours = 0;
20+
public $rate = 0;
21+
public $calculatedAmount = 0;
22+
public $message = '';
23+
24+
protected $rules = [
25+
'customer_id' => 'required',
26+
'isFixed' => 'required|boolean',
27+
'amount' => 'required|numeric|gt:0',
28+
'hours' => 'numeric|gt:0',
29+
'rate' => 'numeric|gt:0',
30+
'message' => 'required|min:20',
31+
];
32+
33+
protected $messages = [
34+
'customer_id.required' => 'Please select Customer.',
35+
'amount.required' => 'Please enter Amount.',
36+
'rate.gt' => 'Hourly Rate must be greater than 0.',
37+
'message.required' => 'Please add Message for Customer.',
38+
];
39+
40+
public function render()
41+
{
42+
43+
$customers = Customer::orderBy('full_name')->pluck('full_name', 'id');
44+
return view('livewire.invoices.index', [
45+
'customers' => $customers
46+
]);
47+
}
48+
49+
public function moveAhead()
50+
{
51+
if($this->step == 1) {
52+
//Validate Step 1 Data
53+
$this->validateOnly('customer_id');
54+
//Wont reach here if the Validation Fails.
55+
//Reset Error Bag to clear any errors on Step 2.
56+
$this->resetErrorBag();
57+
//Recalculate Amount for Step 2.
58+
$this->_calculateAmount();
59+
}
60+
61+
if($this->step == 2) {
62+
if($this->isFixed) {
63+
//Fixed Invoice Validation
64+
$this->validateOnly('amount');
65+
} else {
66+
//Hourly Invoice Validation
67+
$this->validateOnly('hours');
68+
$this->validateOnly('rate');
69+
}
70+
//Reset Error Bag to clear any errors on Step 3.
71+
$this->resetErrorBag();
72+
}
73+
74+
if($this->step == 3) {
75+
$this->validateOnly('message');
76+
//Save to the Invoice Model
77+
$invoice = new Invoice;
78+
$invoice->customer_id = $this->customer_id;
79+
$invoice->is_fixed = $this->isFixed;
80+
if($invoice->is_fixed ) {
81+
$invoice->amount = $this->amount;
82+
} else {
83+
$invoice->hours = $this->hours;
84+
$invoice->rate = $this->rate;
85+
$invoice->amount = $this->calculatedAmount;
86+
}
87+
$invoice->message = $this->message;
88+
$invoice->save();
89+
90+
//redirect
91+
redirect()->route('dashboard');
92+
}
93+
94+
//Increase Step
95+
$this->step += 1;
96+
$this->_validateStep();
97+
}
98+
99+
public function moveBack()
100+
{
101+
$this->step -= 1;
102+
$this->_validateStep();
103+
}
104+
105+
private function _validateStep()
106+
{
107+
if ($this->step < 1) {
108+
$this->step = 1;
109+
}
110+
111+
if ($this->step > $this->totalSteps) {
112+
$this->step = $this->totalSteps;
113+
}
114+
}
115+
116+
public function updatedHours($value)
117+
{
118+
//
119+
$this->calculatedAmount = 0;
120+
$this->validateOnly('hours');
121+
$this->_calculateAmount();
122+
}
123+
124+
public function updatedRate($value)
125+
{
126+
//
127+
$this->calculatedAmount = 0;
128+
$this->validateOnly('rate');
129+
$this->_calculateAmount();
130+
131+
}
132+
133+
private function _calculateAmount()
134+
{
135+
if (is_numeric($this->hours) && is_numeric($this->rate)) {
136+
$this->calculatedAmount = $this->hours * $this->rate;
137+
} else {
138+
$this->calculatedAmount = 0;
139+
}
140+
}
141+
}

app/Models/Customer.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace App\Models;
4+
5+
use Illuminate\Database\Eloquent\Factories\HasFactory;
6+
use Illuminate\Database\Eloquent\Model;
7+
8+
class Customer extends Model
9+
{
10+
use HasFactory;
11+
12+
protected $fillable = ['full_name', 'email'];
13+
14+
}

app/Models/Invoice.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace App\Models;
4+
5+
use Illuminate\Database\Eloquent\Factories\HasFactory;
6+
use Illuminate\Database\Eloquent\Model;
7+
8+
class Invoice extends Model
9+
{
10+
use HasFactory;
11+
12+
protected $guarded = [];
13+
14+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Database\Factories;
4+
5+
use App\Models\Customer;
6+
use Illuminate\Database\Eloquent\Factories\Factory;
7+
8+
class CustomerFactory extends Factory
9+
{
10+
/**
11+
* The name of the factory's corresponding model.
12+
*
13+
* @var string
14+
*/
15+
protected $model = Customer::class;
16+
17+
/**
18+
* Define the model's default state.
19+
*
20+
* @return array
21+
*/
22+
public function definition()
23+
{
24+
return [
25+
//
26+
'full_name' => $this->faker->name(),
27+
'email' => $this->faker->unique()->safeEmail(),
28+
];
29+
}
30+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
class CreateCustomersTable extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*
12+
* @return void
13+
*/
14+
public function up()
15+
{
16+
Schema::create('customers', function (Blueprint $table) {
17+
$table->id();
18+
$table->string('full_name');
19+
$table->string('email')->unique();
20+
$table->timestamps();
21+
});
22+
}
23+
24+
/**
25+
* Reverse the migrations.
26+
*
27+
* @return void
28+
*/
29+
public function down()
30+
{
31+
Schema::dropIfExists('customers');
32+
}
33+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
class CreateInvoicesTable extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*
12+
* @return void
13+
*/
14+
public function up()
15+
{
16+
Schema::create('invoices', function (Blueprint $table) {
17+
$table->id();
18+
$table->integer('customer_id')->index();
19+
$table->boolean('is_fixed');
20+
$table->float('amount');
21+
$table->float('hours')->nullable();
22+
$table->float('rate')->nullable();
23+
$table->text('message');
24+
$table->timestamps();
25+
});
26+
}
27+
28+
/**
29+
* Reverse the migrations.
30+
*
31+
* @return void
32+
*/
33+
public function down()
34+
{
35+
Schema::dropIfExists('invoices');
36+
}
37+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<button wire:loading.attr="disabled" {{ $attributes->merge(['class' => 'px-4 py-2 bg-blue-600 font-semibold text-xs text-white uppercase tracking-widest hover:bg-blue-700 active:bg-blue-700 disabled:opacity-25']) }}>
2+
{{ $slot }}
3+
</button>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<h2 class="font-semibold text-3xl mx-2 text-gray-800 leading-tight border-b-2 border-gray-300">
2+
{{$slot}}
3+
</h2>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@props(['active' => false])
2+
3+
<div class="@if($active) bg-blue-500 text-black @else bg-gray-300 text-white @endif flex items-center justify-center text-2xl font-extrabold">
4+
{{ $slot }}
5+
</div>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@props(['for'])
2+
3+
@error($for)
4+
<span class="mt-3 text-sm text-red-600"">
5+
{{ $message }}
6+
</span>
7+
@enderror

0 commit comments

Comments
 (0)