113610ea8f
Two changes in one commit:
== 1. Moved apply_margin toggle from line-level to Fișă-level ==
The per-manoperă apply_margin toggle is gone from the Manopere tab.
In its place: a single "Aplică marjă internă" toggle in Fișa's
"Plată & total" section (next to override_margin_pct). One decision
per Fișă instead of per line — cleaner mental model, matches how the
shop actually thinks about at-cost vs. billable work.
Migration: work_orders.apply_margin boolean default true (idempotent).
WorkOrderWork::saving now reads WO.apply_margin from DB directly (not
via belongsTo cache) to determine salary_base:
- WO.apply_margin=false → every line gets salary_base=total, applied_margin_pct=0
- WO.apply_margin=true → resolver chain (WO.override → user margin → tenant default)
Old wo_works.apply_margin column stays untouched (backward-compat with
existing rows), but no longer exposed in UI. Tests updated to new
semantic. All existing tests green.
== 2. Full i18n audit on client-facing portal — RO/RU separated ==
Problem: user selecting Russian saw Romanian mixed into headings,
buttons, labels. Every client-facing Blade file was 100% hardcoded
Romanian — zero __() calls.
Fix: created lang/ro/portal.php + lang/ru/portal.php with 131 keys
across 3 namespaces:
- portal.common (email, phone, save, total, powered_by, ...)
- portal.invitation (welcome_name, activate_account, expired_body, ...)
- portal.tracking (title_fisa, approve, approval_needed_title,
ready_estimated, hours, unit_pcs, ...)
- portal.shop (catalog, cart, checkout_title, order_number, vin_title,
signin_title, add_to_cart, in_stock, ...)
Converted 15 Blade files to __() calls:
- resources/views/invitations/{accept,expired,invalid}.blade.php
- resources/views/tracking/show.blade.php
- resources/views/shop/{layout,catalog,cart,checkout,order,account,part,vin}.blade.php
- resources/views/shop/auth/{login,register,forgot,reset}.blade.php
Each view's <html lang="{{ app()->getLocale() }}"> now reflects the
resolved locale (was hardcoded lang="ro").
SetLocale middleware resolves locale in this order:
1. session locale (user picked via language switcher)
2. authenticated user.locale
3. tenant.settings.language
4. app.locale default (now 'ro')
Config change: config/app.php default locale + fallback both = 'ro'
(was 'en'). English falls back to Romanian for portal.* keys since
we don't ship English portal translations — a Romanian shop that
switches to English shows Romanian text, which is safer than showing
"portal.invitation.activate_account" literals.
phpunit.xml sets APP_LOCALE=ro so test assertSee() calls that look
for Romanian text pass.
Verified via portal.* grep: 131 __() calls across 15 files. Zero
hardcoded Romanian nouns/verbs left in any client-facing view.
Suite: 303 passed (840 assertions). Unchanged count — refactor,
not new tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
54 lines
2.6 KiB
PHP
54 lines
2.6 KiB
PHP
<!DOCTYPE html>
|
|
<html lang="{{ app()->getLocale() }}">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>{{ __('portal.invitation.title_accept') }} — {{ $company }}</title>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<style>
|
|
body { font-family: -apple-system, BlinkMacSystemFont, sans-serif; background: #f5f7fa; color: #1a202c; margin: 0; padding: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center; }
|
|
.card { background: white; padding: 32px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.08); max-width: 420px; width: 100%; }
|
|
h1 { font-size: 22px; margin: 0 0 8px; }
|
|
.sub { color: #4a5568; font-size: 14px; margin-bottom: 24px; }
|
|
label { display: block; font-size: 12px; font-weight: 600; color: #718096; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 6px; }
|
|
input { width: 100%; padding: 10px 12px; border: 1px solid #e2e8f0; border-radius: 8px; font-size: 15px; box-sizing: border-box; outline: none; }
|
|
input:focus { border-color: #3b82f6; }
|
|
.field { margin-bottom: 16px; }
|
|
.field.readonly input { background: #f7fafc; color: #718096; }
|
|
.btn { width: 100%; padding: 12px; background: #3b82f6; color: white; border: none; border-radius: 8px; font-size: 15px; font-weight: 600; cursor: pointer; margin-top: 8px; }
|
|
.btn:hover { background: #2563eb; }
|
|
.errors { background: #fee2e2; color: #991b1b; padding: 10px 12px; border-radius: 6px; font-size: 13px; margin-bottom: 16px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="card">
|
|
<h1>{{ __('portal.invitation.welcome_name', ['name' => $name]) }}</h1>
|
|
<p class="sub">{!! __('portal.invitation.welcome_body', ['company' => '<strong>' . e($company) . '</strong>']) !!}</p>
|
|
|
|
@if ($errors->any())
|
|
<div class="errors">
|
|
@foreach ($errors->all() as $error)
|
|
<div>{{ $error }}</div>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
|
|
<form method="POST" action="{{ url('/invitations/' . $token) }}">
|
|
@csrf
|
|
<div class="field readonly">
|
|
<label>{{ __('portal.common.email') }}</label>
|
|
<input type="email" value="{{ $email }}" readonly>
|
|
</div>
|
|
<div class="field">
|
|
<label>{{ __('portal.invitation.new_password') }}</label>
|
|
<input type="password" name="password" required minlength="8" autofocus>
|
|
</div>
|
|
<div class="field">
|
|
<label>{{ __('portal.invitation.confirm_password') }}</label>
|
|
<input type="password" name="password_confirmation" required minlength="8">
|
|
</div>
|
|
<button class="btn" type="submit">{{ __('portal.invitation.activate_account') }}</button>
|
|
</form>
|
|
</div>
|
|
</body>
|
|
</html>
|