diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ad453d0..7c5a500e 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 - **Count production from MQTT machines onto a line/step** *(admin / connectivity)* — a machine/MQTT device can be **assigned to a production line**, and a new **"Count at Station / Step"** topic-mapping action turns each sensor pulse (e.g. a break-beam sensor: one unit leaving a station) into `+1` on the addressed step of the line's **currently running** work order — no per-order configuration. The per-step throughput is tracked in a new `passed_qty` counter; a mapping flagged as the finished-goods counting point also feeds the work order's `produced_qty` (through the shared machine-count path, so `counting_source` and auto start/complete are honoured — no double counting). `update_work_order_qty` can now also target a line directly (the running order) instead of a fixed order number. The device form gains an **Assigned line** picker and the topic-mapping editor a guided **Line + Station/Step** form for the count action (no more hand-written JSON). - **Traceability when editing an in-use process template** *(admin)* — editing a template's steps (add / rename / delete / reorder) still mutates the current template in place, and running work orders correctly keep their frozen snapshot — but that used to happen silently. Now the template page shows a **warning banner** when the template backs active (non-finished) work orders, destructive step edits ask for confirmation while it's in use, and **every step change is written to the immutable audit log** (before/after shape) so the previous version is never lost. No change to how orders resolve their steps. diff --git a/backend/app/Http/Controllers/Web/OnboardingController.php b/backend/app/Http/Controllers/Web/OnboardingController.php index b09eda77..3fcaad36 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 163186c7..71986bad 100644 --- a/backend/lang/en.json +++ b/backend/lang/en.json @@ -5670,6 +5670,9 @@ "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.", + "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, 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 402bc68b..77c5dce1 100644 --- a/backend/lang/pl.json +++ b/backend/lang/pl.json @@ -5670,6 +5670,9 @@ "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.", + "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, 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 a34da28c..083d2f28 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 ( <> -
-
+
+
- {__('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, process template and work order.')}
-
+
{__('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 913df15b..46e56b9f 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 ( <>
-
+
{__('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 d92f172c..499beedf 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 ( <>
-
+
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 9880340b..f1b4e7af 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 ( <>
-
+
{__('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 131bbf00..8dc660c9 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 && (
+ )} {/* Content card */}
diff --git a/backend/tests/Feature/OnboardingWizardTest.php b/backend/tests/Feature/OnboardingWizardTest.php index 14f49a48..ae33dc9f 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'), [