From 4ddd2e8267de3a732b91b20c5a9c3dd28deeed92 Mon Sep 17 00:00:00 2001
From: jakub-przepiora
Date: Sun, 30 Aug 2026 01:22:20 +0200
Subject: [PATCH 1/2] feat(onboarding): make the module-preset choice a screen
before the wizard
The Lightweight/Advanced/Custom preset selection was rendered as step 1 of
the wizard (stepper 'Modules -> Line -> ...', 5 steps). It's now an independent
screen that PRECEDES the wizard:
- OnboardingLayout: 4-step stepper (Line/Product/Process/Work Order); the
stepper is hidden on the preset screen (step 0).
- Controller: renumbered the step props (modules=0, step1..4=1..4, complete=5).
- Modules screen reframed ('Before the setup wizard' / 'Choose your setup')
and the per-step 'X/5' labels renumbered to 'X/4'.
Test asserts the preset screen renders at step 0 and the wizard starts at 1.
en/pl strings. Full suite 2507 passed.
---
CHANGELOG.md | 3 +++
.../Controllers/Web/OnboardingController.php | 14 ++++++++------
backend/lang/en.json | 5 ++++-
backend/lang/pl.json | 5 ++++-
.../resources/js/Pages/onboarding/Modules.jsx | 6 +++---
backend/resources/js/Pages/onboarding/Step1.jsx | 2 +-
backend/resources/js/Pages/onboarding/Step2.jsx | 2 +-
backend/resources/js/Pages/onboarding/Step3.jsx | 2 +-
backend/resources/js/Pages/onboarding/Step4.jsx | 2 +-
.../resources/js/layouts/OnboardingLayout.jsx | 15 ++++++++++-----
backend/tests/Feature/OnboardingWizardTest.php | 16 ++++++++++++++++
11 files changed, 52 insertions(+), 20 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1d25dc940..84b7a71d0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,9 @@ Format based on [Keep a Changelog](https://keepachangelog.com/).
## [Unreleased]
+### Changed
+- **Onboarding: the module-preset choice now precedes the setup wizard** *(admin)* — picking Lightweight / Advanced / Custom is an **independent screen shown before** the wizard, not its first step. The wizard's stepper is hidden on that screen and now counts **4 steps** (Line → Product → Process → Work Order) instead of five. You choose the feature set first, then the wizard walks you through the rest. No change to what each preset enables.
+
### Added
- **Plant timezone is changeable after installation** *(admin)* — Settings → System → General now carries a timezone picker (region + zone), writing the same `system_settings` row the installer's step does; the wizard already promised this was possible. The chosen zone is re-applied per request and before each queued job, so on Octane a change reaches every worker immediately instead of waiting for a container restart. Saving reloads the page so every displayed time switches over at once.
- **Product types as Bill-of-Materials components** *(admin)* — a BOM line can now be a manufactured **product type** (a sub-assembly), not only a material. In the BOM editor a Material / Product type switch picks the component kind; product-type lines carry the same quantity-per-unit, step, scrap %, consumption timing and notes as materials. A product type can't be a component of itself, and each appears once per template. Lines are captured in the work-order snapshot as sub-assembly references; they're a simple component reference (they don't explode into their own BOM) and are skipped by the material stock/consumption engine. Additive — existing material BOMs are unaffected.
diff --git a/backend/app/Http/Controllers/Web/OnboardingController.php b/backend/app/Http/Controllers/Web/OnboardingController.php
index b09eda775..3fcaad36c 100644
--- a/backend/app/Http/Controllers/Web/OnboardingController.php
+++ b/backend/app/Http/Controllers/Web/OnboardingController.php
@@ -34,7 +34,9 @@ public function index()
public function modules()
{
return Inertia::render('onboarding/Modules', [
- 'step' => 1,
+ // step 0 = the preset screen precedes the wizard (no stepper); the
+ // wizard's own steps start at 1 (Line) after a preset is chosen.
+ 'step' => 0,
'modules' => ModuleRegistry::forForm(),
'presets' => ModuleRegistry::PRESETS,
]);
@@ -58,7 +60,7 @@ public function storeModules(Request $request)
public function step1()
{
- return Inertia::render('onboarding/Step1', ['step' => 2]);
+ return Inertia::render('onboarding/Step1', ['step' => 1]);
}
public function storeStep1(Request $request)
@@ -83,7 +85,7 @@ public function step2(Request $request)
return redirect()->route('onboarding.step1');
}
- return Inertia::render('onboarding/Step2', ['step' => 3]);
+ return Inertia::render('onboarding/Step2', ['step' => 2]);
}
public function storeStep2(Request $request)
@@ -115,7 +117,7 @@ public function step3(Request $request)
return redirect()->route('onboarding.step1');
}
- return Inertia::render('onboarding/Step3', ['step' => 4]);
+ return Inertia::render('onboarding/Step3', ['step' => 3]);
}
public function storeStep3(Request $request)
@@ -183,7 +185,7 @@ public function step4(Request $request)
return redirect()->route('onboarding.step1');
}
- return Inertia::render('onboarding/Step4', ['step' => 5]);
+ return Inertia::render('onboarding/Step4', ['step' => 4]);
}
public function storeStep4(Request $request, WorkOrderService $workOrderService)
@@ -210,7 +212,7 @@ public function complete(Request $request)
$this->markCompleted();
$request->session()->forget('onboarding');
- return Inertia::render('onboarding/Complete', ['step' => 6]);
+ return Inertia::render('onboarding/Complete', ['step' => 5]);
}
public function skip(Request $request)
diff --git a/backend/lang/en.json b/backend/lang/en.json
index f7f6173c1..e500ec536 100644
--- a/backend/lang/en.json
+++ b/backend/lang/en.json
@@ -5669,5 +5669,8 @@
"Region": "Region",
"Select timezone": "Select timezone",
"Plant timezone. Every timestamp, report boundary and shift edge in the app is expressed in it.": "Plant timezone. Every timestamp, report boundary and shift edge in the app is expressed in it.",
- "Changing the timezone reloads the page so every displayed time switches over at once.": "Changing the timezone reloads the page so every displayed time switches over at once."
+ "Changing the timezone reloads the page so every displayed time switches over at once.": "Changing the timezone reloads the page so every displayed time switches over at once.",
+ "Choose your setup": "Choose your setup",
+ "Before the setup wizard": "Before the setup wizard",
+ "First pick the feature set you want. Core areas (Dashboard, Orders, Production, Admin) are always on. You can change this anytime in Settings → System → Modules. Next, the setup wizard will walk you through your first line, product and work order.": "First pick the feature set you want. Core areas (Dashboard, Orders, Production, Admin) are always on. You can change this anytime in Settings → System → Modules. Next, the setup wizard will walk you through your first line, product and work order."
}
diff --git a/backend/lang/pl.json b/backend/lang/pl.json
index e6ad59beb..f7fe05928 100644
--- a/backend/lang/pl.json
+++ b/backend/lang/pl.json
@@ -5669,5 +5669,8 @@
"Region": "Region",
"Select timezone": "Wybierz strefę czasową",
"Plant timezone. Every timestamp, report boundary and shift edge in the app is expressed in it.": "Strefa czasowa zakładu. Wszystkie znaczniki czasu, granice raportów i zmian w aplikacji są w niej wyrażone.",
- "Changing the timezone reloads the page so every displayed time switches over at once.": "Zmiana strefy czasowej przeładowuje stronę, aby wszystkie wyświetlane godziny zmieniły się naraz."
+ "Changing the timezone reloads the page so every displayed time switches over at once.": "Zmiana strefy czasowej przeładowuje stronę, aby wszystkie wyświetlane godziny zmieniły się naraz.",
+ "Choose your setup": "Wybierz konfigurację",
+ "Before the setup wizard": "Przed kreatorem konfiguracji",
+ "First pick the feature set you want. Core areas (Dashboard, Orders, Production, Admin) are always on. You can change this anytime in Settings → System → Modules. Next, the setup wizard will walk you through your first line, product and work order.": "Najpierw wybierz zestaw funkcji. Obszary rdzeniowe (Panel, Zlecenia, Produkcja, Administracja) są zawsze włączone. Możesz to zmienić w każdej chwili w Ustawienia → System → Moduły. Następnie kreator konfiguracji przeprowadzi Cię przez pierwszą linię, produkt i zlecenie."
}
diff --git a/backend/resources/js/Pages/onboarding/Modules.jsx b/backend/resources/js/Pages/onboarding/Modules.jsx
index a34da28c4..c6a183d10 100644
--- a/backend/resources/js/Pages/onboarding/Modules.jsx
+++ b/backend/resources/js/Pages/onboarding/Modules.jsx
@@ -65,11 +65,11 @@ export default function OnboardingModules() {
return (
<>
-
-
Step 1/5
+
+
{__('Before the setup wizard')}
{__('Choose your modules')}
- {__('Turn on only the feature areas your team needs. Core areas (Dashboard, Orders, Production, Admin) are always on. You can change this anytime in Settings → System → Modules.')}
+ {__('First pick the feature set you want. Core areas (Dashboard, Orders, Production, Admin) are always on. You can change this anytime in Settings → System → Modules. Next, the setup wizard will walk you through your first line, product and work order.')}
-
Step 2/5
+
Step 1/4
{__('Create a Production Line')}
{__('A production line is a physical area where manufacturing happens. Start by creating your first one.')}
diff --git a/backend/resources/js/Pages/onboarding/Step2.jsx b/backend/resources/js/Pages/onboarding/Step2.jsx
index 913df15b7..46e56b9f4 100644
--- a/backend/resources/js/Pages/onboarding/Step2.jsx
+++ b/backend/resources/js/Pages/onboarding/Step2.jsx
@@ -21,7 +21,7 @@ export default function Step2() {
return (
<>
-
Step 3/5
+
Step 2/4
{__('Add a Product Type')}
{__('What product does this line produce? Define the product type.')}
diff --git a/backend/resources/js/Pages/onboarding/Step3.jsx b/backend/resources/js/Pages/onboarding/Step3.jsx
index d92f172c5..499beedfa 100644
--- a/backend/resources/js/Pages/onboarding/Step3.jsx
+++ b/backend/resources/js/Pages/onboarding/Step3.jsx
@@ -78,7 +78,7 @@ export default function Step3() {
return (
<>
-
Step 4/5
+
Step 3/4
{__('Define Process Template')}
A process template defines the production steps (recipe) for your product. Add each step in the order
diff --git a/backend/resources/js/Pages/onboarding/Step4.jsx b/backend/resources/js/Pages/onboarding/Step4.jsx
index 9880340b7..f1b4e7afb 100644
--- a/backend/resources/js/Pages/onboarding/Step4.jsx
+++ b/backend/resources/js/Pages/onboarding/Step4.jsx
@@ -25,7 +25,7 @@ export default function Step4() {
return (
<>
-
Step 5/5
+
Step 4/4
{__('Create First Work Order')}
{__('A work order represents a production batch to manufacture. Create your first one.')}
diff --git a/backend/resources/js/layouts/OnboardingLayout.jsx b/backend/resources/js/layouts/OnboardingLayout.jsx
index 131bbf001..8dc660c98 100644
--- a/backend/resources/js/layouts/OnboardingLayout.jsx
+++ b/backend/resources/js/layouts/OnboardingLayout.jsx
@@ -3,17 +3,20 @@ import { __ } from '../lib/i18n';
/**
* Wizard chrome for the onboarding flow.
- * Reproduces onboarding/layout.blade.php: centred card, logo, 5-step stepper
- * (Modules → Line → Product → Process → Work Order), skip link at bottom.
+ * Reproduces onboarding/layout.blade.php: centred card, logo, 4-step stepper
+ * (Line → Product → Process → Work Order), skip link at bottom.
*
- * Reads `step` (1–6) from page props; 6 = Complete (all steps shown as done).
+ * Reads `step` from page props: 0 = the module-preset screen that PRECEDES the
+ * wizard (stepper hidden — it's an independent choice, not a wizard step); 1–4 =
+ * the wizard steps; 5 = Complete (all steps shown as done).
*
* Geist White restyle: light-only v1 — om-* tokens, hairline card, mono labels.
*/
export default function OnboardingLayout({ children }) {
const { step = 1, csrf_token } = usePage().props;
- const steps = ['Modules', 'Line', 'Product', 'Process', 'Work Order'];
+ const steps = ['Line', 'Product', 'Process', 'Work Order'];
+ const showStepper = step >= 1;
const skipWizard = (e) => {
e.preventDefault();
@@ -30,7 +33,8 @@ export default function OnboardingLayout({ children }) {
{__('Setup Wizard')}
- {/* Stepper */}
+ {/* Stepper — hidden on the preset screen, which precedes the wizard */}
+ {showStepper && (
{steps.map((label, i) => {
const stepNum = i + 1;
@@ -74,6 +78,7 @@ export default function OnboardingLayout({ children }) {
);
})}
+ )}
{/* Content card */}
{children}
diff --git a/backend/tests/Feature/OnboardingWizardTest.php b/backend/tests/Feature/OnboardingWizardTest.php
index 14f49a48e..ae33dc9fd 100644
--- a/backend/tests/Feature/OnboardingWizardTest.php
+++ b/backend/tests/Feature/OnboardingWizardTest.php
@@ -11,6 +11,7 @@
use App\Support\ModuleRegistry;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
+use Inertia\Testing\AssertableInertia;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
use Tests\TestCase;
@@ -63,6 +64,21 @@ public function test_wizard_skipped_when_marked_completed(): void
$this->assertFalse(OnboardingController::shouldShowWizard());
}
+ public function test_preset_screen_precedes_the_wizard(): void
+ {
+ // The module-preset screen is an independent screen (step 0, no wizard
+ // stepper) shown before the wizard, whose own steps start at 1 (Line).
+ $this->actingAs($this->admin)->get(route('onboarding.modules'))
+ ->assertInertia(fn (AssertableInertia $p) => $p
+ ->component('onboarding/Modules')
+ ->where('step', 0));
+
+ $this->actingAs($this->admin)->get(route('onboarding.step1'))
+ ->assertInertia(fn (AssertableInertia $p) => $p
+ ->component('onboarding/Step1')
+ ->where('step', 1));
+ }
+
public function test_step1_creates_line(): void
{
$response = $this->actingAs($this->admin)->post(route('onboarding.step1'), [
From 80ad0564e9e7b88bfd348990ad1786a4c77224ed Mon Sep 17 00:00:00 2001
From: jakub-przepiora
Date: Sun, 30 Aug 2026 23:36:32 +0200
Subject: [PATCH 2/2] fix(onboarding): address CodeRabbit on the module-preset
screen
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Mention the process template in the preset intro so it lists all four wizard
steps (line, product, process template, work order).
- Use the established Polish terms on the preset screen — 'Obszary podstawowe'
and 'Pulpit' (matching the existing onboarding copy), not 'Obszary rdzeniowe'
and 'Panel'.
---
backend/lang/en.json | 2 +-
backend/lang/pl.json | 2 +-
backend/resources/js/Pages/onboarding/Modules.jsx | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/backend/lang/en.json b/backend/lang/en.json
index 0da19e08b..71986bad9 100644
--- a/backend/lang/en.json
+++ b/backend/lang/en.json
@@ -5672,7 +5672,7 @@
"Changing the timezone reloads the page so every displayed time switches over at once.": "Changing the timezone reloads the page so every displayed time switches over at once.",
"Choose your setup": "Choose your setup",
"Before the setup wizard": "Before the setup wizard",
- "First pick the feature set you want. Core areas (Dashboard, Orders, Production, Admin) are always on. You can change this anytime in Settings → System → Modules. Next, the setup wizard will walk you through your first line, product and work order.": "First pick the feature set you want. Core areas (Dashboard, Orders, Production, Admin) are always on. You can change this anytime in Settings → System → Modules. Next, the setup wizard will walk you through your first line, product and work order.",
+ "First pick the feature set you want. Core areas (Dashboard, Orders, Production, Admin) are always on. You can change this anytime in Settings → System → Modules. Next, the setup wizard will walk you through your first line, product, process template and work order.": "First pick the feature set you want. Core areas (Dashboard, Orders, Production, Admin) are always on. You can change this anytime in Settings → System → Modules. Next, the setup wizard will walk you through your first line, product, process template and work order.",
"Count at Station / Step": "Count at Station / Step",
"Station (workstation)": "Station (workstation)",
"…or step number": "…or step number",
diff --git a/backend/lang/pl.json b/backend/lang/pl.json
index ae70ba5b8..77c5dce1e 100644
--- a/backend/lang/pl.json
+++ b/backend/lang/pl.json
@@ -5672,7 +5672,7 @@
"Changing the timezone reloads the page so every displayed time switches over at once.": "Zmiana strefy czasowej przeładowuje stronę, aby wszystkie wyświetlane godziny zmieniły się naraz.",
"Choose your setup": "Wybierz konfigurację",
"Before the setup wizard": "Przed kreatorem konfiguracji",
- "First pick the feature set you want. Core areas (Dashboard, Orders, Production, Admin) are always on. You can change this anytime in Settings → System → Modules. Next, the setup wizard will walk you through your first line, product and work order.": "Najpierw wybierz zestaw funkcji. Obszary rdzeniowe (Panel, Zlecenia, Produkcja, Administracja) są zawsze włączone. Możesz to zmienić w każdej chwili w Ustawienia → System → Moduły. Następnie kreator konfiguracji przeprowadzi Cię przez pierwszą linię, produkt i zlecenie.",
+ "First pick the feature set you want. Core areas (Dashboard, Orders, Production, Admin) are always on. You can change this anytime in Settings → System → Modules. Next, the setup wizard will walk you through your first line, product, process template and work order.": "Najpierw wybierz zestaw funkcji. Obszary podstawowe (Pulpit, Zlecenia, Produkcja, Admin) są zawsze włączone. Możesz to zmienić w każdej chwili w Ustawienia → System → Moduły. Następnie kreator konfiguracji przeprowadzi Cię przez pierwszą linię, produkt, szablon procesu i zlecenie.",
"Count at Station / Step": "Licz na stanowisku / kroku",
"Station (workstation)": "Stanowisko (workstation)",
"…or step number": "…lub numer kroku",
diff --git a/backend/resources/js/Pages/onboarding/Modules.jsx b/backend/resources/js/Pages/onboarding/Modules.jsx
index c6a183d10..083d2f28a 100644
--- a/backend/resources/js/Pages/onboarding/Modules.jsx
+++ b/backend/resources/js/Pages/onboarding/Modules.jsx
@@ -69,7 +69,7 @@ export default function OnboardingModules() {
{__('Before the setup wizard')}
{__('Choose your modules')}
- {__('First pick the feature set you want. Core areas (Dashboard, Orders, Production, Admin) are always on. You can change this anytime in Settings → System → Modules. Next, the setup wizard will walk you through your first line, product and work order.')}
+ {__('First pick the feature set you want. Core areas (Dashboard, Orders, Production, Admin) are always on. You can change this anytime in Settings → System → Modules. Next, the setup wizard will walk you through your first line, product, process template and work order.')}