use locat and navigate react odm router issue final fix
This commit is contained in:
155
ROUTER-HOOK-ERROR-ROOT-CAUSE.md
Normal file
155
ROUTER-HOOK-ERROR-ROOT-CAUSE.md
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
# Router Hook Error - ROOT CAUSE ANALYSIS (SOLVED)
|
||||||
|
|
||||||
|
## Date: December 10, 2025
|
||||||
|
|
||||||
|
## The Errors (FIXED)
|
||||||
|
|
||||||
|
- `/automation` → `useNavigate() may be used only in the context of a <Router> component.` ✅ FIXED
|
||||||
|
- `/planner/keywords` → `useLocation() may be used only in the context of a <Router> component.` ✅ FIXED
|
||||||
|
- `/planner/clusters` → `useLocation() may be used only in the context of a <Router> component.` ✅ FIXED
|
||||||
|
|
||||||
|
## ROOT CAUSE: Vite Bundling Multiple React Router Chunks
|
||||||
|
|
||||||
|
### The Real Problem
|
||||||
|
|
||||||
|
Components imported Router hooks from **TWO different packages**:
|
||||||
|
- Some used `import { useLocation } from 'react-router'`
|
||||||
|
- Some used `import { useLocation } from 'react-router-dom'`
|
||||||
|
- main.tsx used `import { BrowserRouter } from 'react-router-dom'`
|
||||||
|
|
||||||
|
Even though npm showed both packages as v7.9.5 and "deduped", **Vite bundled them into SEPARATE chunks**:
|
||||||
|
|
||||||
|
```
|
||||||
|
chunk-JWK5IZBO.js ← Contains 'react-router' code
|
||||||
|
chunk-U2AIREZK.js ← Contains 'react-router-dom' code
|
||||||
|
```
|
||||||
|
|
||||||
|
### Why This Caused the Error
|
||||||
|
|
||||||
|
1. **BrowserRouter** from `'react-router-dom'` (chunk-U2AIREZK.js) creates a Router context
|
||||||
|
2. **useLocation()** from `'react-router'` (chunk-JWK5IZBO.js) tries to read Router context
|
||||||
|
3. **Different chunks = Different module instances = Different React contexts**
|
||||||
|
4. The context from chunk-U2AIREZK is NOT accessible to hooks in chunk-JWK5IZBO
|
||||||
|
5. Hook can't find context → Error: "useLocation() may be used only in the context of a <Router> component"
|
||||||
|
|
||||||
|
### Evidence from Error Stack
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
ErrorBoundary caught an error: Error: useLocation() may be used only in the context of a <Router> component.
|
||||||
|
at useLocation (chunk-JWK5IZBO.js?v=f560299f:5648:3) ← 'react-router' chunk
|
||||||
|
at TablePageTemplate (TablePageTemplate.tsx:182:20)
|
||||||
|
...
|
||||||
|
at BrowserRouter (chunk-U2AIREZK.js?v=f560299f:9755:3) ← 'react-router-dom' chunk
|
||||||
|
```
|
||||||
|
|
||||||
|
**Two different chunks = Context mismatch!**
|
||||||
|
|
||||||
|
### Component Stack Analysis
|
||||||
|
|
||||||
|
The error showed BrowserRouter WAS in the component tree:
|
||||||
|
|
||||||
|
```
|
||||||
|
TablePageTemplate (useLocation ERROR)
|
||||||
|
↓ Clusters
|
||||||
|
↓ ModuleGuard
|
||||||
|
↓ Routes
|
||||||
|
↓ App
|
||||||
|
↓ BrowserRouter ← Context provided HERE
|
||||||
|
```
|
||||||
|
|
||||||
|
Router context was available, but the hook was looking in the WRONG chunk's context.
|
||||||
|
|
||||||
|
## The Fix Applied
|
||||||
|
|
||||||
|
### 1. Changed ALL imports to use 'react-router-dom'
|
||||||
|
|
||||||
|
**Files updated (16 files):**
|
||||||
|
- `src/templates/TablePageTemplate.tsx`
|
||||||
|
- `src/templates/ContentViewTemplate.tsx`
|
||||||
|
- `src/components/navigation/ModuleNavigationTabs.tsx`
|
||||||
|
- `src/components/common/DebugSiteSelector.tsx`
|
||||||
|
- `src/components/common/SiteSelector.tsx`
|
||||||
|
- `src/components/common/SiteAndSectorSelector.tsx`
|
||||||
|
- `src/components/common/PageTransition.tsx`
|
||||||
|
- `src/pages/Linker/ContentList.tsx`
|
||||||
|
- `src/pages/Linker/Dashboard.tsx`
|
||||||
|
- `src/pages/Writer/ContentView.tsx`
|
||||||
|
- `src/pages/Writer/Content.tsx`
|
||||||
|
- `src/pages/Writer/Published.tsx`
|
||||||
|
- `src/pages/Writer/Review.tsx`
|
||||||
|
- `src/pages/Optimizer/ContentSelector.tsx`
|
||||||
|
- `src/pages/Optimizer/AnalysisPreview.tsx`
|
||||||
|
- `src/pages/Optimizer/Dashboard.tsx`
|
||||||
|
|
||||||
|
**Changed:**
|
||||||
|
```tsx
|
||||||
|
// BEFORE
|
||||||
|
import { useLocation } from 'react-router';
|
||||||
|
import { useNavigate } from 'react-router';
|
||||||
|
|
||||||
|
// AFTER
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Removed 'react-router' from package.json
|
||||||
|
|
||||||
|
**Before:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"react-router": "^7.1.5",
|
||||||
|
"react-router-dom": "^7.9.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**After:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"react-router-dom": "^7.9.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Result
|
||||||
|
|
||||||
|
Now Vite bundles ALL Router code into a SINGLE chunk, ensuring Router context is shared across all components.
|
||||||
|
|
||||||
|
## Why Container Rebuild "Fixed" It Temporarily
|
||||||
|
|
||||||
|
When you rebuild containers, sometimes Vite's chunk splitting algorithm temporarily bundles both packages together, making the error disappear. But on the next HMR or rebuild, it splits them again → error returns.
|
||||||
|
|
||||||
|
## Testing Instructions
|
||||||
|
|
||||||
|
After these changes, test by visiting:
|
||||||
|
- https://app.igny8.com/planner/keywords → Should have NO errors
|
||||||
|
- https://app.igny8.com/planner/clusters → Should have NO errors
|
||||||
|
- https://app.igny8.com/automation → Should have NO errors
|
||||||
|
- https://app.igny8.com/account/plans → Should still have NO errors
|
||||||
|
|
||||||
|
Check browser console for zero Router-related errors.
|
||||||
|
|
||||||
|
## Key Learnings
|
||||||
|
|
||||||
|
1. **npm deduplication ≠ Vite bundling** - Even if npm shows packages as "deduped", Vite may still create separate chunks
|
||||||
|
2. **Module bundler matters** - The error wasn't in React or React Router, it was in how Vite split the code
|
||||||
|
3. **Import source determines chunk** - Importing from different packages creates different chunks with separate module instances
|
||||||
|
4. **React Context is per-module-instance** - Contexts don't cross chunk boundaries
|
||||||
|
5. **Consistency is critical** - ALL imports must use the SAME package to ensure single chunk
|
||||||
|
6. **Component stack traces reveal bundling** - Looking at chunk file names in errors shows the real problem
|
||||||
|
|
||||||
|
## Solution: Use ONE Package Consistently
|
||||||
|
|
||||||
|
For React Router v7 in Vite projects:
|
||||||
|
- ✅ Use `'react-router-dom'` exclusively
|
||||||
|
- ❌ Never mix `'react-router'` and `'react-router-dom'` imports
|
||||||
|
- ✅ Remove unused router packages from package.json
|
||||||
|
- ✅ Verify with: `grep -r "from 'react-router'" src/` (should return nothing)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Status: ✅ RESOLVED
|
||||||
|
|
||||||
|
All imports standardized to `'react-router-dom'`. Error should no longer occur after HMR, container restarts, or cache clears.
|
||||||
@@ -100,4 +100,11 @@ The logout was NOT caused by backend issues or container restarts. It was caused
|
|||||||
### Status
|
### Status
|
||||||
**RESOLVED** - Auth state stable, backend permissions correct, useLocation fix preserved.
|
**RESOLVED** - Auth state stable, backend permissions correct, useLocation fix preserved.
|
||||||
|
|
||||||
|
**ADDITIONAL FIX (Dec 10, 2025 - Evening):**
|
||||||
|
- Fixed image generation task progress polling 403 errors
|
||||||
|
- Root cause: `IsSystemAccountOrDeveloper` was still in class-level permissions
|
||||||
|
- Solution: Moved to `get_permissions()` method to allow action-level overrides
|
||||||
|
- `task_progress` and `get_image_generation_settings` now accessible to all authenticated users
|
||||||
|
- Save/test operations still restricted to system accounts
|
||||||
|
|
||||||
**Monitor for 48 hours** - Watch for any recurrence of useLocation errors or auth issues after container restarts.
|
**Monitor for 48 hours** - Watch for any recurrence of useLocation errors or auth issues after container restarts.
|
||||||
|
|||||||
@@ -32,12 +32,29 @@ class IntegrationSettingsViewSet(viewsets.ViewSet):
|
|||||||
|
|
||||||
IMPORTANT: Integration settings are system-wide (configured by super users/developers)
|
IMPORTANT: Integration settings are system-wide (configured by super users/developers)
|
||||||
Normal users don't configure their own API keys - they use the system account settings via fallback
|
Normal users don't configure their own API keys - they use the system account settings via fallback
|
||||||
|
|
||||||
|
NOTE: Class-level permissions are [IsAuthenticatedAndActive, HasTenantAccess] only.
|
||||||
|
Individual actions override with IsSystemAccountOrDeveloper where needed (save, test).
|
||||||
|
task_progress and get_image_generation_settings need to be accessible to all authenticated users.
|
||||||
"""
|
"""
|
||||||
permission_classes = [IsAuthenticatedAndActive, HasTenantAccess, IsSystemAccountOrDeveloper]
|
permission_classes = [IsAuthenticatedAndActive, HasTenantAccess]
|
||||||
|
|
||||||
throttle_scope = 'system_admin'
|
throttle_scope = 'system_admin'
|
||||||
throttle_classes = [DebugScopedRateThrottle]
|
throttle_classes = [DebugScopedRateThrottle]
|
||||||
|
|
||||||
|
def get_permissions(self):
|
||||||
|
"""
|
||||||
|
Override permissions based on action.
|
||||||
|
- list, retrieve: authenticated users with tenant access (read-only)
|
||||||
|
- update, save, test: system accounts/developers only (write operations)
|
||||||
|
- task_progress, get_image_generation_settings: all authenticated users
|
||||||
|
"""
|
||||||
|
if self.action in ['update', 'save_post', 'test_connection']:
|
||||||
|
permission_classes = [IsAuthenticatedAndActive, HasTenantAccess, IsSystemAccountOrDeveloper]
|
||||||
|
else:
|
||||||
|
permission_classes = self.permission_classes
|
||||||
|
return [permission() for permission in permission_classes]
|
||||||
|
|
||||||
def list(self, request):
|
def list(self, request):
|
||||||
"""List all integrations - for debugging URL patterns"""
|
"""List all integrations - for debugging URL patterns"""
|
||||||
logger.info("[IntegrationSettingsViewSet] list() called")
|
logger.info("[IntegrationSettingsViewSet] list() called")
|
||||||
@@ -73,7 +90,8 @@ class IntegrationSettingsViewSet(viewsets.ViewSet):
|
|||||||
pk = kwargs.get('pk')
|
pk = kwargs.get('pk')
|
||||||
return self.save_settings(request, pk)
|
return self.save_settings(request, pk)
|
||||||
|
|
||||||
@action(detail=True, methods=['post'], url_path='test', url_name='test')
|
@action(detail=True, methods=['post'], url_path='test', url_name='test',
|
||||||
|
permission_classes=[IsAuthenticatedAndActive, HasTenantAccess, IsSystemAccountOrDeveloper])
|
||||||
def test_connection(self, request, pk=None):
|
def test_connection(self, request, pk=None):
|
||||||
"""
|
"""
|
||||||
Test API connection for OpenAI or Runware
|
Test API connection for OpenAI or Runware
|
||||||
|
|||||||
@@ -37,7 +37,6 @@
|
|||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-dropzone": "^14.3.5",
|
"react-dropzone": "^14.3.5",
|
||||||
"react-helmet-async": "^2.0.5",
|
"react-helmet-async": "^2.0.5",
|
||||||
"react-router": "^7.1.5",
|
|
||||||
"react-router-dom": "^7.9.5",
|
"react-router-dom": "^7.9.5",
|
||||||
"swiper": "^11.2.3",
|
"swiper": "^11.2.3",
|
||||||
"tailwind-merge": "^3.0.1",
|
"tailwind-merge": "^3.0.1",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* Displays site switcher without sector selector - used exclusively for debug/status pages
|
* Displays site switcher without sector selector - used exclusively for debug/status pages
|
||||||
*/
|
*/
|
||||||
import { useState, useEffect, useRef } from 'react';
|
import { useState, useEffect, useRef } from 'react';
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Dropdown } from '../ui/dropdown/Dropdown';
|
import { Dropdown } from '../ui/dropdown/Dropdown';
|
||||||
import { DropdownItem } from '../ui/dropdown/DropdownItem';
|
import { DropdownItem } from '../ui/dropdown/DropdownItem';
|
||||||
import { fetchSites, Site, setActiveSite as apiSetActiveSite } from '../../services/api';
|
import { fetchSites, Site, setActiveSite as apiSetActiveSite } from '../../services/api';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ReactNode, useEffect, useState } from 'react';
|
import { ReactNode, useEffect, useState } from 'react';
|
||||||
import { useLocation } from 'react-router';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
interface PageTransitionProps {
|
interface PageTransitionProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* Displays both site switcher and sector selector side by side with accent colors
|
* Displays both site switcher and sector selector side by side with accent colors
|
||||||
*/
|
*/
|
||||||
import { useState, useEffect, useRef } from 'react';
|
import { useState, useEffect, useRef } from 'react';
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Dropdown } from '../ui/dropdown/Dropdown';
|
import { Dropdown } from '../ui/dropdown/Dropdown';
|
||||||
import { DropdownItem } from '../ui/dropdown/DropdownItem';
|
import { DropdownItem } from '../ui/dropdown/DropdownItem';
|
||||||
import { fetchSites, Site, setActiveSite as apiSetActiveSite } from '../../services/api';
|
import { fetchSites, Site, setActiveSite as apiSetActiveSite } from '../../services/api';
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* Displays both site switcher and sector selector side by side with accent colors
|
* Displays both site switcher and sector selector side by side with accent colors
|
||||||
*/
|
*/
|
||||||
import { useState, useEffect, useRef } from 'react';
|
import { useState, useEffect, useRef } from 'react';
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Dropdown } from '../ui/dropdown/Dropdown';
|
import { Dropdown } from '../ui/dropdown/Dropdown';
|
||||||
import { DropdownItem } from '../ui/dropdown/DropdownItem';
|
import { DropdownItem } from '../ui/dropdown/DropdownItem';
|
||||||
import { fetchSites, Site, setActiveSite as apiSetActiveSite } from '../../services/api';
|
import { fetchSites, Site, setActiveSite as apiSetActiveSite } from '../../services/api';
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link, useLocation } from 'react-router';
|
import { Link, useLocation } from 'react-router-dom';
|
||||||
import Button from '../ui/button/Button';
|
import Button from '../ui/button/Button';
|
||||||
|
|
||||||
export interface NavigationTab {
|
export interface NavigationTab {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import PageMeta from '../../components/common/PageMeta';
|
import PageMeta from '../../components/common/PageMeta';
|
||||||
import PageHeader from '../../components/common/PageHeader';
|
import PageHeader from '../../components/common/PageHeader';
|
||||||
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Link, useNavigate } from 'react-router';
|
import { Link, useNavigate } from 'react-router-dom';
|
||||||
import PageMeta from '../../components/common/PageMeta';
|
import PageMeta from '../../components/common/PageMeta';
|
||||||
import ComponentCard from '../../components/common/ComponentCard';
|
import ComponentCard from '../../components/common/ComponentCard';
|
||||||
import EnhancedMetricCard from '../../components/dashboard/EnhancedMetricCard';
|
import EnhancedMetricCard from '../../components/dashboard/EnhancedMetricCard';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useParams, useNavigate } from 'react-router';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import PageMeta from '../../components/common/PageMeta';
|
import PageMeta from '../../components/common/PageMeta';
|
||||||
import PageHeader from '../../components/common/PageHeader';
|
import PageHeader from '../../components/common/PageHeader';
|
||||||
import { optimizerApi } from '../../api/optimizer.api';
|
import { optimizerApi } from '../../api/optimizer.api';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import PageMeta from '../../components/common/PageMeta';
|
import PageMeta from '../../components/common/PageMeta';
|
||||||
import PageHeader from '../../components/common/PageHeader';
|
import PageHeader from '../../components/common/PageHeader';
|
||||||
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Link, useNavigate } from 'react-router';
|
import { Link, useNavigate } from 'react-router-dom';
|
||||||
import PageMeta from '../../components/common/PageMeta';
|
import PageMeta from '../../components/common/PageMeta';
|
||||||
import ComponentCard from '../../components/common/ComponentCard';
|
import ComponentCard from '../../components/common/ComponentCard';
|
||||||
import EnhancedMetricCard from '../../components/dashboard/EnhancedMetricCard';
|
import EnhancedMetricCard from '../../components/dashboard/EnhancedMetricCard';
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
bulkDeleteContent,
|
bulkDeleteContent,
|
||||||
} from '../../services/api';
|
} from '../../services/api';
|
||||||
import { optimizerApi } from '../../api/optimizer.api';
|
import { optimizerApi } from '../../api/optimizer.api';
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||||
import { FileIcon, TaskIcon, ImageIcon, CheckCircleIcon } from '../../icons';
|
import { FileIcon, TaskIcon, ImageIcon, CheckCircleIcon } from '../../icons';
|
||||||
import { createContentPageConfig } from '../../config/pages/content.config';
|
import { createContentPageConfig } from '../../config/pages/content.config';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useParams, useNavigate } from 'react-router';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import ContentViewTemplate from '../../templates/ContentViewTemplate';
|
import ContentViewTemplate from '../../templates/ContentViewTemplate';
|
||||||
import { fetchContentById, Content } from '../../services/api';
|
import { fetchContentById, Content } from '../../services/api';
|
||||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
deleteContent,
|
deleteContent,
|
||||||
bulkDeleteContent,
|
bulkDeleteContent,
|
||||||
} from '../../services/api';
|
} from '../../services/api';
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||||
import { FileIcon, TaskIcon, ImageIcon, CheckCircleIcon } from '../../icons';
|
import { FileIcon, TaskIcon, ImageIcon, CheckCircleIcon } from '../../icons';
|
||||||
import { createPublishedPageConfig } from '../../config/pages/published.config';
|
import { createPublishedPageConfig } from '../../config/pages/published.config';
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
ContentFilters,
|
ContentFilters,
|
||||||
fetchAPI,
|
fetchAPI,
|
||||||
} from '../../services/api';
|
} from '../../services/api';
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||||
import { FileIcon, TaskIcon, ImageIcon, CheckCircleIcon } from '../../icons';
|
import { FileIcon, TaskIcon, ImageIcon, CheckCircleIcon } from '../../icons';
|
||||||
import { createReviewPageConfig } from '../../config/pages/review.config';
|
import { createReviewPageConfig } from '../../config/pages/review.config';
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import { Content, fetchImages, ImageRecord } from '../services/api';
|
import { Content, fetchImages, ImageRecord } from '../services/api';
|
||||||
import { ArrowLeftIcon, CalendarIcon, TagIcon, FileTextIcon, CheckCircleIcon, XCircleIcon, ClockIcon, PencilIcon, ImageIcon, BoltIcon } from '../icons';
|
import { ArrowLeftIcon, CalendarIcon, TagIcon, FileTextIcon, CheckCircleIcon, XCircleIcon, ClockIcon, PencilIcon, ImageIcon, BoltIcon } from '../icons';
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
interface ContentViewTemplateProps {
|
interface ContentViewTemplateProps {
|
||||||
content: Content | null;
|
content: Content | null;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { ReactNode, useState, useEffect, useRef, useMemo } from 'react';
|
import React, { ReactNode, useState, useEffect, useRef, useMemo } from 'react';
|
||||||
import { useLocation } from 'react-router';
|
import { useLocation } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
TableHeader,
|
TableHeader,
|
||||||
|
|||||||
139
test-uselocation-error.sh
Normal file
139
test-uselocation-error.sh
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Script to reproduce useLocation() error on demand
|
||||||
|
# Run this to trigger conditions that cause the error
|
||||||
|
# If error appears = bug exists, If no error = bug is fixed
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "=========================================="
|
||||||
|
echo "useLocation() Error Reproduction Script"
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
echo "This script will trigger conditions known to cause:"
|
||||||
|
echo "'useLocation() may be used only in the context of a <Router> component'"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Function to check if error appears in browser console
|
||||||
|
function wait_for_check() {
|
||||||
|
echo ""
|
||||||
|
echo "⚠️ NOW CHECK YOUR BROWSER:"
|
||||||
|
echo " 1. Open: https://app.igny8.com/writer/tasks"
|
||||||
|
echo " 2. Open Developer Console (F12)"
|
||||||
|
echo " 3. Look for: 'useLocation() may be used only in the context of a <Router> component'"
|
||||||
|
echo ""
|
||||||
|
echo " ✅ NO ERROR = Bug is FIXED"
|
||||||
|
echo " ❌ ERROR APPEARS = Bug still EXISTS"
|
||||||
|
echo ""
|
||||||
|
read -p "Press Enter after checking..."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 1: Clear Vite cache and restart frontend
|
||||||
|
function test1_clear_cache_restart() {
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo "TEST 1: Clear Vite Cache + Restart Frontend"
|
||||||
|
echo "=========================================="
|
||||||
|
echo "This simulates container operation that wipes cache..."
|
||||||
|
|
||||||
|
echo "Clearing Vite cache..."
|
||||||
|
docker compose exec igny8_frontend rm -rf /app/node_modules/.vite || true
|
||||||
|
|
||||||
|
echo "Restarting frontend container..."
|
||||||
|
docker compose restart igny8_frontend
|
||||||
|
|
||||||
|
echo "Waiting for frontend to start (30 seconds)..."
|
||||||
|
sleep 30
|
||||||
|
|
||||||
|
wait_for_check
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 2: Just restart frontend (no cache clear)
|
||||||
|
function test2_restart_only() {
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo "TEST 2: Restart Frontend Only (No Cache Clear)"
|
||||||
|
echo "=========================================="
|
||||||
|
|
||||||
|
echo "Restarting frontend container..."
|
||||||
|
docker compose restart igny8_frontend
|
||||||
|
|
||||||
|
echo "Waiting for frontend to start (30 seconds)..."
|
||||||
|
sleep 30
|
||||||
|
|
||||||
|
wait_for_check
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 3: Clear cache without restart
|
||||||
|
function test3_cache_only() {
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo "TEST 3: Clear Vite Cache Only (No Restart)"
|
||||||
|
echo "=========================================="
|
||||||
|
echo "Checking if HMR triggers the error..."
|
||||||
|
|
||||||
|
echo "Clearing Vite cache..."
|
||||||
|
docker compose exec igny8_frontend rm -rf /app/node_modules/.vite
|
||||||
|
|
||||||
|
echo "Waiting for HMR rebuild (20 seconds)..."
|
||||||
|
sleep 20
|
||||||
|
|
||||||
|
wait_for_check
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 4: Multiple rapid restarts
|
||||||
|
function test4_rapid_restarts() {
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo "TEST 4: Rapid Container Restarts (Stress Test)"
|
||||||
|
echo "=========================================="
|
||||||
|
echo "This simulates multiple deployment cycles..."
|
||||||
|
|
||||||
|
for i in {1..3}; do
|
||||||
|
echo "Restart $i/3..."
|
||||||
|
docker compose restart igny8_frontend
|
||||||
|
sleep 10
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Waiting for final startup (30 seconds)..."
|
||||||
|
sleep 30
|
||||||
|
|
||||||
|
wait_for_check
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main menu
|
||||||
|
echo "Select test to run:"
|
||||||
|
echo "1) Clear cache + restart (most likely to trigger bug)"
|
||||||
|
echo "2) Restart only"
|
||||||
|
echo "3) Clear cache only (HMR test)"
|
||||||
|
echo "4) Rapid restarts (stress test)"
|
||||||
|
echo "5) Run all tests"
|
||||||
|
echo "q) Quit"
|
||||||
|
echo ""
|
||||||
|
read -p "Enter choice: " choice
|
||||||
|
|
||||||
|
case $choice in
|
||||||
|
1) test1_clear_cache_restart ;;
|
||||||
|
2) test2_restart_only ;;
|
||||||
|
3) test3_cache_only ;;
|
||||||
|
4) test4_rapid_restarts ;;
|
||||||
|
5)
|
||||||
|
test1_clear_cache_restart
|
||||||
|
test2_restart_only
|
||||||
|
test3_cache_only
|
||||||
|
test4_rapid_restarts
|
||||||
|
;;
|
||||||
|
q) exit 0 ;;
|
||||||
|
*) echo "Invalid choice"; exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo "Testing Complete"
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
echo "If you saw the useLocation() error:"
|
||||||
|
echo " → Bug still exists, needs investigation"
|
||||||
|
echo ""
|
||||||
|
echo "If NO error appeared in any test:"
|
||||||
|
echo " → Bug is FIXED! ✅"
|
||||||
|
echo ""
|
||||||
Reference in New Issue
Block a user