Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
14 changes: 8 additions & 6 deletions backend/app/Http/Controllers/Web/OnboardingController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
]);
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions backend/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
3 changes: 3 additions & 0 deletions backend/lang/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions backend/resources/js/Pages/onboarding/Modules.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@ export default function OnboardingModules() {

return (
<>
<Head title={__('Step 1 — Modules')} />
<div className="font-mono text-[9.5px] uppercase tracking-[0.08em] text-om-faint mb-2">Step 1/5</div>
<Head title={__('Choose your setup')} />
<div className="font-mono text-[9.5px] uppercase tracking-[0.08em] text-om-faint mb-2">{__('Before the setup wizard')}</div>
<h2 className="text-xl font-semibold tracking-[-0.02em] text-om-ink mb-2">{__('Choose your modules')}</h2>
<p className="text-sm text-om-muted mb-6">
{__('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.')}
</p>

<form onSubmit={submit}>
Expand Down
2 changes: 1 addition & 1 deletion backend/resources/js/Pages/onboarding/Step1.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default function Step1() {
return (
<>
<Head title={__('Step 1 — Production Line')} />
<div className="font-mono text-[9.5px] uppercase tracking-[0.08em] text-om-faint mb-2">Step 2/5</div>
<div className="font-mono text-[9.5px] uppercase tracking-[0.08em] text-om-faint mb-2">Step 1/4</div>
<h2 className="text-xl font-semibold tracking-[-0.02em] text-om-ink mb-2">{__('Create a Production Line')}</h2>
<p className="text-sm text-om-muted mb-6">
{__('A production line is a physical area where manufacturing happens. Start by creating your first one.')}
Expand Down
2 changes: 1 addition & 1 deletion backend/resources/js/Pages/onboarding/Step2.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default function Step2() {
return (
<>
<Head title={__('Step 2 — Product Type')} />
<div className="font-mono text-[9.5px] uppercase tracking-[0.08em] text-om-faint mb-2">Step 3/5</div>
<div className="font-mono text-[9.5px] uppercase tracking-[0.08em] text-om-faint mb-2">Step 2/4</div>
<h2 className="text-xl font-semibold tracking-[-0.02em] text-om-ink mb-2">{__('Add a Product Type')}</h2>
<p className="text-sm text-om-muted mb-6">{__('What product does this line produce? Define the product type.')}</p>

Expand Down
2 changes: 1 addition & 1 deletion backend/resources/js/Pages/onboarding/Step3.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export default function Step3() {
return (
<>
<Head title={__('Step 3 — Process Template')} />
<div className="font-mono text-[9.5px] uppercase tracking-[0.08em] text-om-faint mb-2">Step 4/5</div>
<div className="font-mono text-[9.5px] uppercase tracking-[0.08em] text-om-faint mb-2">Step 3/4</div>
<h2 className="text-xl font-semibold tracking-[-0.02em] text-om-ink mb-2">{__('Define Process Template')}</h2>
<p className="text-sm text-om-muted mb-6">
A process template defines the production steps (recipe) for your product. Add each step in the order
Expand Down
2 changes: 1 addition & 1 deletion backend/resources/js/Pages/onboarding/Step4.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default function Step4() {
return (
<>
<Head title={__('Step 4 — Work Order')} />
<div className="font-mono text-[9.5px] uppercase tracking-[0.08em] text-om-faint mb-2">Step 5/5</div>
<div className="font-mono text-[9.5px] uppercase tracking-[0.08em] text-om-faint mb-2">Step 4/4</div>
<h2 className="text-xl font-semibold tracking-[-0.02em] text-om-ink mb-2">{__('Create First Work Order')}</h2>
<p className="text-sm text-om-muted mb-6">
{__('A work order represents a production batch to manufacture. Create your first one.')}
Expand Down
15 changes: 10 additions & 5 deletions backend/resources/js/layouts/OnboardingLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -30,7 +33,8 @@ export default function OnboardingLayout({ children }) {
<p className="font-mono text-[10px] uppercase tracking-[0.12em] text-om-faint">{__('Setup Wizard')}</p>
</div>

{/* Stepper */}
{/* Stepper — hidden on the preset screen, which precedes the wizard */}
{showStepper && (
<div className="flex items-center justify-center mb-8">
{steps.map((label, i) => {
const stepNum = i + 1;
Expand Down Expand Up @@ -74,6 +78,7 @@ export default function OnboardingLayout({ children }) {
);
})}
</div>
)}

{/* Content card */}
<div className="bg-om-card border border-om-line rounded-om shadow-[0_20px_50px_-20px_rgba(0,0,0,.35)] p-8">{children}</div>
Expand Down
16 changes: 16 additions & 0 deletions backend/tests/Feature/OnboardingWizardTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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'), [
Expand Down
Loading