|
|
@@ -0,0 +1,395 @@
|
|
|
+# Navigation Context Plan
|
|
|
+
|
|
|
+## Goal
|
|
|
+
|
|
|
+Replace full `previous_url` query parameters with a short `nav` token and keep navigation history in session. This must:
|
|
|
+
|
|
|
+- eliminate URL growth;
|
|
|
+- preserve nested back navigation;
|
|
|
+- reduce conflicts between unrelated navigation chains;
|
|
|
+- avoid large `Location` headers on redirects.
|
|
|
+
|
|
|
+## Target Model
|
|
|
+
|
|
|
+In URL:
|
|
|
+
|
|
|
+```text
|
|
|
+?nav=abc123xyz
|
|
|
+```
|
|
|
+
|
|
|
+In session:
|
|
|
+
|
|
|
+```php
|
|
|
+[
|
|
|
+ 'navigation' => [
|
|
|
+ 'abc123xyz' => [
|
|
|
+ 'updated_at' => 1713945600,
|
|
|
+ 'stack' => [
|
|
|
+ '/reclamations?filters[comment]=...',
|
|
|
+ '/reclamations/show/848',
|
|
|
+ '/catalog/show/15',
|
|
|
+ ],
|
|
|
+ ],
|
|
|
+ ],
|
|
|
+]
|
|
|
+```
|
|
|
+
|
|
|
+## Core Principles
|
|
|
+
|
|
|
+1. Pass only `nav` in links and forms.
|
|
|
+2. Store history only in session.
|
|
|
+3. Push only GET pages into the stack.
|
|
|
+4. Never pass full `previous_url` in redirects again.
|
|
|
+5. Keep a fallback route per module if token/session context is missing.
|
|
|
+
|
|
|
+## Implementation Plan
|
|
|
+
|
|
|
+### 1. Add Navigation Service
|
|
|
+
|
|
|
+Create:
|
|
|
+
|
|
|
+- `app/Services/NavigationContextService.php`
|
|
|
+
|
|
|
+Responsibilities:
|
|
|
+
|
|
|
+- get existing token or create a new one;
|
|
|
+- store URL stack in session by token;
|
|
|
+- return correct back URL for current page;
|
|
|
+- cap stack size;
|
|
|
+- prune expired tokens.
|
|
|
+
|
|
|
+Planned public API:
|
|
|
+
|
|
|
+```php
|
|
|
+getOrCreateToken(Request $request): string
|
|
|
+rememberCurrentPage(Request $request, string $token): void
|
|
|
+backUrl(Request $request, string $token, ?string $fallback = null): ?string
|
|
|
+routeParams(array $params, string $token): array
|
|
|
+pruneExpired(): void
|
|
|
+forgetToken(string $token): void
|
|
|
+```
|
|
|
+
|
|
|
+Internal helpers:
|
|
|
+
|
|
|
+```php
|
|
|
+normalizeUrl(string $url): string
|
|
|
+isGetPage(Request $request): bool
|
|
|
+context(string $token): array
|
|
|
+saveContext(string $token, array $context): void
|
|
|
+```
|
|
|
+
|
|
|
+### 2. Define Stack Rules
|
|
|
+
|
|
|
+Rules for session history:
|
|
|
+
|
|
|
+1. Only GET pages are pushed to stack.
|
|
|
+2. POST/PUT/PATCH/DELETE requests are never pushed.
|
|
|
+3. Before storing URL:
|
|
|
+ - remove `nav`;
|
|
|
+ - remove service-only params if needed.
|
|
|
+4. If URL is already the last stack item, do not push it again.
|
|
|
+5. If URL already exists earlier in stack:
|
|
|
+ - remove old occurrence;
|
|
|
+ - append it to the end.
|
|
|
+6. Limit stack length to 20 items.
|
|
|
+7. Store `updated_at`.
|
|
|
+8. Remove expired contexts older than 24 hours.
|
|
|
+
|
|
|
+### 3. Token Format
|
|
|
+
|
|
|
+Requirements:
|
|
|
+
|
|
|
+- short;
|
|
|
+- URL-safe;
|
|
|
+- unique enough for one session.
|
|
|
+
|
|
|
+Recommended format:
|
|
|
+
|
|
|
+```php
|
|
|
+bin2hex(random_bytes(8))
|
|
|
+```
|
|
|
+
|
|
|
+### 4. Extend Base Controller
|
|
|
+
|
|
|
+Current base methods:
|
|
|
+
|
|
|
+- `resolvePreviousUrl()`
|
|
|
+- `previousUrlForRedirect()`
|
|
|
+
|
|
|
+Add wrappers over the new service:
|
|
|
+
|
|
|
+- `resolveNavToken(Request $request): string`
|
|
|
+- `rememberNavigation(Request $request, string $token): void`
|
|
|
+- `navigationBackUrl(Request $request, string $token, ?string $fallback = null): ?string`
|
|
|
+- `withNav(array $params, string $token): array`
|
|
|
+
|
|
|
+For transition period, do not remove old methods immediately.
|
|
|
+
|
|
|
+### 5. Define Fallback Routes per Module
|
|
|
+
|
|
|
+If token is missing or stack is empty, each module needs a stable fallback.
|
|
|
+
|
|
|
+Initial list:
|
|
|
+
|
|
|
+- reclamations -> `route('reclamations.index', session('gp_reclamations'))`
|
|
|
+- orders -> `route('order.index', session('gp_orders'))`
|
|
|
+- spare parts -> `route('spare_parts.index')`
|
|
|
+- spare part orders -> `route('spare_part_orders.index')`
|
|
|
+- import -> `route('import.index', session('gp_import'))`
|
|
|
+
|
|
|
+Need to confirm catalog/product fallback if dedicated index route exists.
|
|
|
+
|
|
|
+### 6. Implement Service Behavior
|
|
|
+
|
|
|
+`backUrl()` must return previous page relative to current page, not just latest entry.
|
|
|
+
|
|
|
+If stack is:
|
|
|
+
|
|
|
+```text
|
|
|
+/reclamations?filters=...
|
|
|
+/reclamations/show/848
|
|
|
+/catalog/show/15
|
|
|
+```
|
|
|
+
|
|
|
+Then on `/catalog/show/15` back URL must be `/reclamations/show/848`.
|
|
|
+
|
|
|
+Algorithm:
|
|
|
+
|
|
|
+1. Normalize current request URL.
|
|
|
+2. Read stack.
|
|
|
+3. If current URL equals last stack item, return previous one.
|
|
|
+4. Otherwise return last item.
|
|
|
+5. If nothing found, return fallback.
|
|
|
+
|
|
|
+### 7. Migrate GET Controllers First
|
|
|
+
|
|
|
+Start with pages that render show/edit screens and need back navigation.
|
|
|
+
|
|
|
+Priority controllers:
|
|
|
+
|
|
|
+1. `ReclamationController@show`
|
|
|
+2. `OrderController@show`, `@edit`
|
|
|
+3. `ProductController@show/edit`
|
|
|
+4. `ProductSKUController@show/edit`
|
|
|
+5. `SparePartController@show/edit`
|
|
|
+6. `SparePartOrderController@show/edit`
|
|
|
+7. `ImportController@show`
|
|
|
+
|
|
|
+Pattern for each GET action:
|
|
|
+
|
|
|
+```php
|
|
|
+$nav = $this->resolveNavToken($request);
|
|
|
+$this->rememberNavigation($request, $nav);
|
|
|
+
|
|
|
+$this->data['nav'] = $nav;
|
|
|
+$this->data['back_url'] = $this->navigationBackUrl(
|
|
|
+ $request,
|
|
|
+ $nav,
|
|
|
+ route('reclamations.index', session('gp_reclamations'))
|
|
|
+);
|
|
|
+```
|
|
|
+
|
|
|
+### 8. Replace `previous_url` in Blade Links
|
|
|
+
|
|
|
+Main rule:
|
|
|
+
|
|
|
+- stop passing `previous_url`;
|
|
|
+- pass only `nav`.
|
|
|
+
|
|
|
+Highest-risk files:
|
|
|
+
|
|
|
+1. `resources/views/partials/table.blade.php`
|
|
|
+2. `resources/views/orders/show.blade.php`
|
|
|
+3. `resources/views/reclamations/edit.blade.php`
|
|
|
+4. `resources/views/catalog/edit.blade.php`
|
|
|
+5. `resources/views/products_sku/edit.blade.php`
|
|
|
+6. `resources/views/spare_parts/edit.blade.php`
|
|
|
+7. `resources/views/spare_part_orders/edit.blade.php`
|
|
|
+
|
|
|
+Expected view variables:
|
|
|
+
|
|
|
+- `$nav`
|
|
|
+- `$back_url`
|
|
|
+
|
|
|
+Buttons/links:
|
|
|
+
|
|
|
+- use `$back_url` for "Назад";
|
|
|
+- use `route(..., ['nav' => $nav])` for nested transitions.
|
|
|
+
|
|
|
+### 9. Replace `previous_url` in Forms and AJAX
|
|
|
+
|
|
|
+Hidden fields:
|
|
|
+
|
|
|
+- replace hidden `previous_url` with hidden `nav`.
|
|
|
+
|
|
|
+AJAX updates:
|
|
|
+
|
|
|
+- send `nav`;
|
|
|
+- do not send `previous_url`;
|
|
|
+- return `204 No Content` for AJAX updates where page refresh is not needed.
|
|
|
+
|
|
|
+This is especially relevant for:
|
|
|
+
|
|
|
+- `resources/views/reclamations/edit.blade.php`
|
|
|
+
|
|
|
+### 10. Replace Redirect Logic in POST Controllers
|
|
|
+
|
|
|
+Main rule:
|
|
|
+
|
|
|
+- POST handlers keep `nav`;
|
|
|
+- redirects return either to current page with `nav`, or to resolved back URL;
|
|
|
+- full `previous_url` must not appear in redirect params.
|
|
|
+
|
|
|
+High-priority controllers:
|
|
|
+
|
|
|
+1. `ReclamationController`
|
|
|
+2. `ProductController`
|
|
|
+3. `OrderController`
|
|
|
+4. `ProductSKUController`
|
|
|
+5. `SparePartController`
|
|
|
+6. `SparePartOrderController`
|
|
|
+
|
|
|
+Pattern:
|
|
|
+
|
|
|
+```php
|
|
|
+$nav = $request->string('nav')->toString();
|
|
|
+
|
|
|
+return redirect()->route('reclamations.show', [
|
|
|
+ 'reclamation' => $reclamation,
|
|
|
+ 'nav' => $nav,
|
|
|
+]);
|
|
|
+```
|
|
|
+
|
|
|
+Use `redirect()->to($backUrl)` only when action should return to parent page instead of current card.
|
|
|
+
|
|
|
+### 11. Add Transitional Compatibility
|
|
|
+
|
|
|
+For rollout safety:
|
|
|
+
|
|
|
+1. If `nav` exists, use new logic.
|
|
|
+2. If `nav` is missing but `previous_url` exists:
|
|
|
+ - create a new token;
|
|
|
+ - import `previous_url` as first stack item;
|
|
|
+ - then append current page.
|
|
|
+3. If neither exists:
|
|
|
+ - use fallback route.
|
|
|
+
|
|
|
+This allows gradual migration without breaking old entry points.
|
|
|
+
|
|
|
+### 12. Standardize Shared Partials
|
|
|
+
|
|
|
+Review and update shared templates:
|
|
|
+
|
|
|
+- `resources/views/partials/submit.blade.php`
|
|
|
+- `resources/views/partials/table.blade.php`
|
|
|
+
|
|
|
+Target:
|
|
|
+
|
|
|
+- use `$back_url` instead of `$previous_url`;
|
|
|
+- use `$nav` instead of embedding source URL directly.
|
|
|
+
|
|
|
+`url()->previous()` should remain only as a last-resort fallback if needed.
|
|
|
+
|
|
|
+### 13. Session Cleanup Strategy
|
|
|
+
|
|
|
+To prevent session growth:
|
|
|
+
|
|
|
+- maximum contexts per session: 30
|
|
|
+- maximum stack length per context: 20
|
|
|
+- context TTL: 24 hours
|
|
|
+
|
|
|
+Cleanup should run inside service on:
|
|
|
+
|
|
|
+- `getOrCreateToken()`
|
|
|
+- `rememberCurrentPage()`
|
|
|
+
|
|
|
+### 14. Test Coverage
|
|
|
+
|
|
|
+Add feature tests for:
|
|
|
+
|
|
|
+1. token creation when `nav` absent;
|
|
|
+2. token reuse when `nav` present;
|
|
|
+3. GET page stored in stack;
|
|
|
+4. POST page not stored in stack;
|
|
|
+5. `nav` removed during URL normalization;
|
|
|
+6. duplicate URLs do not accumulate;
|
|
|
+7. `list -> show` back navigation;
|
|
|
+8. `list -> reclamation -> catalog` nested back navigation;
|
|
|
+9. separate `nav` tokens do not conflict;
|
|
|
+10. fallback works on empty/missing context;
|
|
|
+11. POST redirect does not include full `previous_url`;
|
|
|
+12. AJAX update returns `204` and no `Location`.
|
|
|
+
|
|
|
+### 15. Rollout Stages
|
|
|
+
|
|
|
+#### Stage 1. Infrastructure
|
|
|
+
|
|
|
+- create `NavigationContextService`;
|
|
|
+- add wrappers in base `Controller`;
|
|
|
+- add tests for service behavior.
|
|
|
+
|
|
|
+#### Stage 2. Reclamations
|
|
|
+
|
|
|
+- migrate `ReclamationController`;
|
|
|
+- migrate `resources/views/reclamations/edit.blade.php`;
|
|
|
+- migrate `resources/views/partials/table.blade.php`;
|
|
|
+- verify uploads, details, spare parts, and AJAX update flow.
|
|
|
+
|
|
|
+#### Stage 3. Orders and Linked Navigation
|
|
|
+
|
|
|
+- migrate `OrderController`;
|
|
|
+- migrate `resources/views/orders/show.blade.php`.
|
|
|
+
|
|
|
+#### Stage 4. Catalog and SKU
|
|
|
+
|
|
|
+- migrate `ProductController`;
|
|
|
+- migrate `ProductSKUController`;
|
|
|
+- migrate:
|
|
|
+ - `resources/views/catalog/edit.blade.php`
|
|
|
+ - `resources/views/products_sku/edit.blade.php`
|
|
|
+
|
|
|
+#### Stage 5. Spare Parts
|
|
|
+
|
|
|
+- migrate `SparePartController`;
|
|
|
+- migrate `SparePartOrderController`;
|
|
|
+- migrate related edit/show templates.
|
|
|
+
|
|
|
+#### Stage 6. Legacy Cleanup
|
|
|
+
|
|
|
+- remove `previous_url` from links and hidden inputs;
|
|
|
+- keep temporary compatibility branch only where needed;
|
|
|
+- later remove `resolvePreviousUrl()` and `previousUrlForRedirect()`.
|
|
|
+
|
|
|
+### 16. Decisions to Confirm Before Coding
|
|
|
+
|
|
|
+1. TTL for navigation context: recommended 24 hours
|
|
|
+2. Max contexts per session: recommended 30
|
|
|
+3. Max stack size per context: recommended 20
|
|
|
+4. Duplicate policy: move existing URL to end instead of duplicating
|
|
|
+5. Fallback route per module: must be explicitly defined
|
|
|
+
|
|
|
+### 17. Risks
|
|
|
+
|
|
|
+1. Mixed old/new navigation while migration is incomplete
|
|
|
+ Mitigation: transitional compatibility
|
|
|
+
|
|
|
+2. Reusing one token across unrelated flows
|
|
|
+ Mitigation: create token only at chain entry, then pass it through
|
|
|
+
|
|
|
+3. Cyclic or noisy back navigation
|
|
|
+ Mitigation: normalize URLs and deduplicate stack
|
|
|
+
|
|
|
+4. Wrong fallback target
|
|
|
+ Mitigation: define fallback per module instead of global fallback
|
|
|
+
|
|
|
+### 18. Definition of Done
|
|
|
+
|
|
|
+Task is done when:
|
|
|
+
|
|
|
+1. full `previous_url` is no longer passed in links and redirects;
|
|
|
+2. `nav` is used in key transitions;
|
|
|
+3. nested back flow works at least for:
|
|
|
+ - `reclamations list -> reclamation -> catalog -> sku`
|
|
|
+4. URLs no longer grow from filter chains;
|
|
|
+5. tests cover nested back navigation and separate tabs/contexts;
|
|
|
+6. redirect responses no longer produce oversized `Location` headers.
|