Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
46.67% |
14 / 30 |
|
75.00% |
9 / 12 |
CRAP | |
0.00% |
0 / 1 |
| SetupWizard | |
46.67% |
14 / 30 |
|
75.00% |
9 / 12 |
49.13 | |
0.00% |
0 / 1 |
| steps | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
| realSteps | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| isStep | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| finished | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| doneSteps | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| isDone | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| markDone | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
| nextIncomplete | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
| nextStep | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
| finish | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| reopen | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| progress | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
| 1 | <?php |
| 2 | |
| 3 | namespace 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 | */ |
| 13 | class 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 | } |