Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
57.14% covered (warning)
57.14%
32 / 56
54.55% covered (warning)
54.55%
6 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
SetupController
57.14% covered (warning)
57.14%
32 / 56
54.55% covered (warning)
54.55%
6 / 11
43.50
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 index
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 step
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 save
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
3.07
 skip
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 finish
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 saveBusiness
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 saveBranding
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 saveMoney
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 saveDelivery
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 saveProduct
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace App\Http\Controllers\admin;
4
5use App\Http\Controllers\Controller;
6use App\Models\admin\Product;
7use App\Services\Setup\SetupWizard;
8use Illuminate\Http\RedirectResponse;
9use Illuminate\Http\Request;
10use Illuminate\Support\Str;
11use Illuminate\View\View;
12
13/**
14 * First-run setup guide (People/Settings → Setup guide). Resumable and
15 * skippable — every step can be skipped and the whole thing dismissed.
16 */
17class SetupController extends Controller
18{
19    public function __construct(private SetupWizard $wizard) {}
20
21    public function index(): RedirectResponse
22    {
23        return redirect()->route('admin.setup.step', $this->wizard->nextIncomplete());
24    }
25
26    public function step(string $step): View|RedirectResponse
27    {
28        if (! $this->wizard->isStep($step)) {
29            return redirect()->route('admin.setup.index');
30        }
31
32        return view('admin.setup.step', [
33            'wizard' => $this->wizard,
34            'step' => $step,
35            'meta' => $this->wizard->steps()[$step],
36        ]);
37    }
38
39    public function save(Request $request, string $step): RedirectResponse
40    {
41        if (! $this->wizard->isStep($step) || $step === 'done') {
42            return redirect()->route('admin.setup.index');
43        }
44
45        $this->{'save' . Str::studly($step)}($request);
46
47        $this->wizard->markDone($step);
48
49        return redirect()->route('admin.setup.step', $this->wizard->nextStep($step));
50    }
51
52    public function skip(string $step): RedirectResponse
53    {
54        if ($this->wizard->isStep($step) && $step !== 'done') {
55            $this->wizard->markDone($step);
56        }
57
58        return redirect()->route('admin.setup.step', $this->wizard->nextStep($step));
59    }
60
61    public function finish(): RedirectResponse
62    {
63        $this->wizard->finish();
64
65        return redirect()->route('admin.dashboard')->with('success', 'Setup complete — your store is ready.');
66    }
67
68    /* ---------------------------------------------------------------- steps */
69
70    private function saveBusiness(Request $request): void
71    {
72        $data = $request->validate([
73            'site_name' => ['required', 'string', 'max:120'],
74            'phone_primary' => ['nullable', 'string', 'max:40'],
75            'email' => ['nullable', 'email', 'max:120'],
76            'address' => ['nullable', 'string', 'max:500'],
77        ]);
78
79        settings()->set('general.site_name', $data['site_name']);
80        settings()->set('contact.phone_primary', $data['phone_primary'] ?? '');
81        settings()->set('contact.email', $data['email'] ?? '');
82        settings()->set('contact.address', $data['address'] ?? '');
83    }
84
85    private function saveBranding(Request $request): void
86    {
87        $data = $request->validate(['logo' => ['nullable', 'string', 'max:255']]);
88
89        if (! empty($data['logo'])) {
90            settings()->set('general.logo', $data['logo']);
91        }
92    }
93
94    private function saveMoney(Request $request): void
95    {
96        $data = $request->validate([
97            'currency_symbol' => ['required', 'string', 'max:8'],
98            'currency_position' => ['required', 'in:prefix,suffix'],
99        ]);
100
101        settings()->set('general.currency_symbol', $data['currency_symbol']);
102        settings()->set('general.currency_position', $data['currency_position']);
103    }
104
105    private function saveDelivery(Request $request): void
106    {
107        $rows = collect($request->input('shipping_methods', []))
108            ->filter(fn ($r) => filled($r['label'] ?? null))
109            ->map(fn ($r) => ['label' => $r['label'], 'charge' => (float) ($r['charge'] ?? 0)])
110            ->values()
111            ->all();
112
113        if ($rows) {
114            settings()->set('checkout.shipping_methods', $rows);
115        }
116    }
117
118    private function saveProduct(Request $request): void
119    {
120        $data = $request->validate([
121            'name' => ['required', 'string', 'max:180'],
122            'selling_price' => ['required', 'numeric', 'min:0'],
123        ]);
124
125        Product::create([
126            'name' => $data['name'],
127            'slug' => Str::slug($data['name']) . '-' . Str::lower(Str::random(4)),
128            'selling_price' => $data['selling_price'],
129            'price' => $data['selling_price'],
130            'stock_status' => 1,
131        ]);
132    }
133}