Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
46.67% covered (danger)
46.67%
14 / 30
75.00% covered (warning)
75.00%
9 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
SetupWizard
46.67% covered (danger)
46.67%
14 / 30
75.00% covered (warning)
75.00%
9 / 12
49.13
0.00% covered (danger)
0.00%
0 / 1
 steps
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 realSteps
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isStep
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 finished
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 doneSteps
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isDone
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 markDone
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 nextIncomplete
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 nextStep
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 finish
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 reopen
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 progress
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace App\Services\Setup;
4
5/**
6 * The first-run setup guide. State lives in the `setup` settings group:
7 *   setup.done_steps  → array of completed step keys
8 *   setup.finished    → bool, true once the guide is finished or skipped
9 *
10 * Everything here is optional — the store already works with sensible
11 * defaults. The guide just walks a new operator through the essentials.
12 */
13class SetupWizard
14{
15    /** Ordered steps. 'done' is the closing screen, not a real step. */
16    public const STEPS = ['business', 'branding', 'money', 'delivery', 'product', 'done'];
17
18    public function steps(): array
19    {
20        return [
21            'business' => ['title' => 'Business details', 'icon' => 'fas fa-store', 'blurb' => 'Your shop name and how customers reach you.'],
22            'branding' => ['title' => 'Logo', 'icon' => 'fas fa-image', 'blurb' => 'Upload your logo — or do it later.'],
23            'money' => ['title' => 'Currency & tax', 'icon' => 'fas fa-coins', 'blurb' => 'The currency symbol shown across the store.'],
24            'delivery' => ['title' => 'Delivery', 'icon' => 'fas fa-truck', 'blurb' => 'Your delivery areas and charges.'],
25            'product' => ['title' => 'First product', 'icon' => 'fas fa-box', 'blurb' => 'Add one product to get going.'],
26            'done' => ['title' => 'All set', 'icon' => 'fas fa-circle-check', 'blurb' => ''],
27        ];
28    }
29
30    /** Real steps only (no 'done'). */
31    public function realSteps(): array
32    {
33        return array_values(array_filter(self::STEPS, fn ($s) => $s !== 'done'));
34    }
35
36    public function isStep(string $step): bool
37    {
38        return in_array($step, self::STEPS, true);
39    }
40
41    public function finished(): bool
42    {
43        return (bool) settings('setup.finished', false);
44    }
45
46    /** @return string[] */
47    public function doneSteps(): array
48    {
49        return (array) settings('setup.done_steps', []);
50    }
51
52    public function isDone(string $step): bool
53    {
54        return in_array($step, $this->doneSteps(), true);
55    }
56
57    public function markDone(string $step): void
58    {
59        $done = array_values(array_unique([...$this->doneSteps(), $step]));
60        settings()->set('setup.done_steps', $done);
61    }
62
63    /** First real step not yet completed, or 'done' when all are. */
64    public function nextIncomplete(): string
65    {
66        foreach ($this->realSteps() as $step) {
67            if (! $this->isDone($step)) {
68                return $step;
69            }
70        }
71
72        return 'done';
73    }
74
75    public function nextStep(string $current): string
76    {
77        $i = array_search($current, self::STEPS, true);
78
79        return self::STEPS[$i + 1] ?? 'done';
80    }
81
82    public function finish(): void
83    {
84        settings()->set('setup.finished', true);
85    }
86
87    public function reopen(): void
88    {
89        settings()->set('setup.finished', false);
90    }
91
92    /** @return array{done:int,total:int,percent:int} */
93    public function progress(): array
94    {
95        $total = count($this->realSteps());
96        $done = count(array_intersect($this->doneSteps(), $this->realSteps()));
97
98        return [
99            'done' => $done,
100            'total' => $total,
101            'percent' => $total ? (int) round($done / $total * 100) : 0,
102        ];
103    }
104}