541 lines
14 KiB
TypeScript
541 lines
14 KiB
TypeScript
/**
|
|
* Integration Examples
|
|
*
|
|
* This file shows how to integrate the new template-driven RequestDetail
|
|
* component into your existing application.
|
|
*/
|
|
|
|
import { useEffect, useState } from 'react';
|
|
import { Route, Routes, useNavigate, useParams, Link, BrowserRouter, NavigateFunction } from 'react-router-dom';
|
|
import { useAuth } from '@/contexts/AuthContext';
|
|
import { RequestDetailTemplated } from './RequestDetailTemplated';
|
|
import { RequestDetail } from './RequestDetail';
|
|
import { registerTemplate, selectTemplate, getTemplate } from './templates';
|
|
|
|
// Mock components for examples
|
|
const Home = () => <div>Home</div>;
|
|
const Login = () => <div>Login</div>;
|
|
const Dashboard = () => <div>Dashboard</div>;
|
|
const ProtectedRoute = () => <div>Protected</div>;
|
|
const Modal = ({ children, onClose }: any) => <div>{children}</div>;
|
|
const HomePage = () => <div>Home</div>;
|
|
const LoginPage = () => <div>Login</div>;
|
|
const DashboardPage = () => <div>Dashboard</div>;
|
|
const NotFoundPage = () => <div>Not Found</div>;
|
|
|
|
// Mock hooks
|
|
const useRequests = () => [] as any[];
|
|
|
|
// Mock data
|
|
const requests = [] as any[];
|
|
const notifications = [] as any[];
|
|
|
|
/**
|
|
* Example 1: Simple Route Integration
|
|
*
|
|
* Replace your existing route with the new template-driven component
|
|
*/
|
|
export function SimpleRouteIntegration() {
|
|
return (
|
|
<Routes>
|
|
{/* Old way - still works for backward compatibility */}
|
|
<Route
|
|
path="/request/:requestId"
|
|
element={<RequestDetail />}
|
|
/>
|
|
|
|
{/* New way - template-driven (recommended) */}
|
|
<Route
|
|
path="/request-v2/:requestId"
|
|
element={<RequestDetailTemplated />}
|
|
/>
|
|
</Routes>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Example 2: Gradual Migration Strategy
|
|
*
|
|
* Run both versions side-by-side during migration
|
|
*/
|
|
export function GradualMigration() {
|
|
return (
|
|
<Routes>
|
|
{/* Legacy route for existing links */}
|
|
<Route
|
|
path="/request/:requestId"
|
|
element={<RequestDetail />}
|
|
/>
|
|
|
|
{/* New template-driven route */}
|
|
<Route
|
|
path="/request/v2/:requestId"
|
|
element={<RequestDetailTemplated />}
|
|
/>
|
|
|
|
{/* Dealer-specific route */}
|
|
<Route
|
|
path="/dealer/claim/:requestId"
|
|
element={<RequestDetailTemplated template="dealerClaim" />}
|
|
/>
|
|
|
|
{/* Vendor-specific route */}
|
|
<Route
|
|
path="/vendor/request/:requestId"
|
|
element={<RequestDetailTemplated template="vendor" />}
|
|
/>
|
|
</Routes>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Example 3: Conditional Rendering Based on User Role
|
|
*/
|
|
export function ConditionalRenderingExample() {
|
|
return function RequestDetailRoute() {
|
|
const { user } = useAuth();
|
|
const { requestId } = useParams();
|
|
|
|
// Use new template system for dealers and vendors
|
|
if (user?.role === 'dealer' || user?.role === 'vendor') {
|
|
return <RequestDetailTemplated requestId={requestId} />;
|
|
}
|
|
|
|
// Use old component for other users (during migration)
|
|
return <RequestDetail requestId={requestId} />;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Example 4: Dashboard Integration
|
|
*/
|
|
export function DashboardIntegration() {
|
|
const navigate = useNavigate();
|
|
|
|
const handleViewRequest = (request: any) => {
|
|
// Automatic template selection based on request type
|
|
navigate(`/request/${request.requestId}`);
|
|
|
|
// Or explicit template
|
|
if (request.category === 'claim-management') {
|
|
navigate(`/dealer/claim/${request.requestId}`);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="dashboard">
|
|
{requests.map(request => (
|
|
<div key={request.requestId}>
|
|
<h3>{request.title}</h3>
|
|
<button onClick={() => handleViewRequest(request)}>
|
|
View Details
|
|
</button>
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Example 5: Modal Integration
|
|
*/
|
|
export function ModalIntegration() {
|
|
const [selectedRequestId, setSelectedRequestId] = useState<string | null>(null);
|
|
|
|
return (
|
|
<>
|
|
<button onClick={() => setSelectedRequestId('REQ-123')}>
|
|
View Request
|
|
</button>
|
|
|
|
{selectedRequestId && (
|
|
<Modal onClose={() => setSelectedRequestId(null)}>
|
|
<RequestDetailTemplated
|
|
requestId={selectedRequestId}
|
|
onBack={() => setSelectedRequestId(null)}
|
|
/>
|
|
</Modal>
|
|
)}
|
|
</>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Example 6: App.tsx Integration
|
|
*/
|
|
export function AppIntegrationExample() {
|
|
return (
|
|
<BrowserRouter>
|
|
<Routes>
|
|
{/* Public routes */}
|
|
<Route path="/" element={<Home />} />
|
|
<Route path="/login" element={<Login />} />
|
|
|
|
{/* Protected routes */}
|
|
<Route element={<ProtectedRoute />}>
|
|
<Route path="/dashboard" element={<Dashboard />} />
|
|
|
|
{/* Request Detail Routes */}
|
|
<Route
|
|
path="/request/:requestId"
|
|
element={<RequestDetailTemplated />}
|
|
/>
|
|
|
|
{/* Dealer-specific routes */}
|
|
<Route
|
|
path="/dealer/claim/:requestId"
|
|
element={<RequestDetailTemplated template="dealerClaim" />}
|
|
/>
|
|
|
|
{/* Vendor-specific routes */}
|
|
<Route
|
|
path="/vendor/request/:requestId"
|
|
element={<RequestDetailTemplated template="vendor" />}
|
|
/>
|
|
</Route>
|
|
</Routes>
|
|
</BrowserRouter>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Example 7: Custom Template Registration at App Startup
|
|
*/
|
|
export function AppInitialization() {
|
|
useEffect(() => {
|
|
// Register custom templates at app startup
|
|
import('./templates/customTemplates').then(({ customTemplates }) => {
|
|
customTemplates.forEach(template => {
|
|
registerTemplate(template);
|
|
});
|
|
});
|
|
}, []);
|
|
|
|
return <App />;
|
|
}
|
|
|
|
/**
|
|
* Example 8: Dynamic Template Selection Hook
|
|
*/
|
|
export function useDynamicTemplate(requestId: string) {
|
|
const [template, setTemplate] = useState<string>('standard');
|
|
const { user } = useAuth();
|
|
|
|
useEffect(() => {
|
|
// Fetch request details
|
|
fetch(`/api/requests/${requestId}`)
|
|
.then(res => res.json())
|
|
.then(request => {
|
|
// Select template based on request data
|
|
const templateId = selectTemplate(user, request);
|
|
setTemplate(templateId);
|
|
});
|
|
}, [requestId, user]);
|
|
|
|
return template;
|
|
}
|
|
|
|
/**
|
|
* Example 9: Using with Dynamic Template
|
|
*/
|
|
export function DynamicTemplateExample() {
|
|
const { requestId } = useParams();
|
|
const templateId = useDynamicTemplate(requestId || '');
|
|
|
|
return (
|
|
<RequestDetailTemplated
|
|
requestId={requestId}
|
|
template={templateId}
|
|
/>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Example 10: Table Row Click Handler
|
|
*/
|
|
export function TableIntegration() {
|
|
const navigate = useNavigate();
|
|
|
|
const handleRowClick = (request: any) => {
|
|
// Navigate to appropriate template based on request type
|
|
if (request.category === 'claim-management') {
|
|
navigate(`/dealer/claim/${request.requestId}`);
|
|
} else if (request.category === 'vendor') {
|
|
navigate(`/vendor/request/${request.requestId}`);
|
|
} else {
|
|
navigate(`/request/${request.requestId}`);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<table>
|
|
<tbody>
|
|
{requests.map(request => (
|
|
<tr
|
|
key={request.requestId}
|
|
onClick={() => handleRowClick(request)}
|
|
className="cursor-pointer hover:bg-gray-50"
|
|
>
|
|
<td>{request.requestId}</td>
|
|
<td>{request.title}</td>
|
|
<td>{request.status}</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Example 11: Notification Click Handler
|
|
*/
|
|
export function NotificationIntegration() {
|
|
const navigate = useNavigate();
|
|
|
|
const handleNotificationClick = (notification: any) => {
|
|
const { requestId, requestType } = notification;
|
|
|
|
// Navigate with appropriate template
|
|
switch (requestType) {
|
|
case 'dealer-claim':
|
|
navigate(`/request/${requestId}?template=dealerClaim`);
|
|
break;
|
|
case 'vendor':
|
|
navigate(`/request/${requestId}?template=vendor`);
|
|
break;
|
|
default:
|
|
navigate(`/request/${requestId}`);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="notifications">
|
|
{notifications.map(notification => (
|
|
<div
|
|
key={notification.id}
|
|
onClick={() => handleNotificationClick(notification)}
|
|
>
|
|
{notification.message}
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Example 12: Search Results Integration
|
|
*/
|
|
export function SearchResultsIntegration() {
|
|
const [searchResults, setSearchResults] = useState([]);
|
|
const navigate = useNavigate();
|
|
|
|
const handleSearchResultClick = (result: any) => {
|
|
// Use explicit template if known, otherwise let auto-selection work
|
|
if (result.template) {
|
|
navigate(`/request/${result.requestId}?template=${result.template}`);
|
|
} else {
|
|
navigate(`/request/${result.requestId}`);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="search-results">
|
|
{searchResults.map(result => (
|
|
<div
|
|
key={result.requestId}
|
|
onClick={() => handleSearchResultClick(result)}
|
|
className="search-result-item"
|
|
>
|
|
<h4>{result.title}</h4>
|
|
<p>{result.description}</p>
|
|
<span className="badge">{result.category}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Complete Integration Example
|
|
*
|
|
* This shows a complete integration with:
|
|
* - Route configuration
|
|
* - Protected routes
|
|
* - Custom template registration
|
|
* - Navigation helpers
|
|
*/
|
|
|
|
// main.tsx or App.tsx
|
|
import { BrowserRouter } from 'react-router-dom';
|
|
import { AuthProvider } from '@/contexts/AuthContext';
|
|
import { registerTemplate } from '@/pages/RequestDetail/templates';
|
|
import { customTemplates } from './customTemplates';
|
|
|
|
function App() {
|
|
// Register custom templates on app load
|
|
useEffect(() => {
|
|
customTemplates.forEach(registerTemplate);
|
|
}, []);
|
|
|
|
return (
|
|
<BrowserRouter>
|
|
<AuthProvider>
|
|
<Routes>
|
|
{/* Public Routes */}
|
|
<Route path="/" element={<HomePage />} />
|
|
<Route path="/login" element={<LoginPage />} />
|
|
|
|
{/* Protected Routes */}
|
|
<Route element={<ProtectedRoute />}>
|
|
<Route path="/dashboard" element={<DashboardPage />} />
|
|
|
|
{/* Universal Request Detail Route */}
|
|
<Route
|
|
path="/request/:requestId"
|
|
element={<RequestDetailTemplated />}
|
|
/>
|
|
|
|
{/* Type-specific routes (optional) */}
|
|
<Route
|
|
path="/dealer/claim/:requestId"
|
|
element={<RequestDetailTemplated template="dealerClaim" />}
|
|
/>
|
|
|
|
<Route
|
|
path="/vendor/order/:requestId"
|
|
element={<RequestDetailTemplated template="vendor" />}
|
|
/>
|
|
|
|
{/* Catch all */}
|
|
<Route path="*" element={<NotFoundPage />} />
|
|
</Route>
|
|
</Routes>
|
|
</AuthProvider>
|
|
</BrowserRouter>
|
|
);
|
|
}
|
|
|
|
export default App;
|
|
|
|
/**
|
|
* Helper Functions
|
|
*/
|
|
|
|
// Navigate to request with appropriate template
|
|
export function navigateToRequest(
|
|
navigate: NavigateFunction,
|
|
requestId: string,
|
|
request?: any
|
|
) {
|
|
if (request?.category === 'claim-management') {
|
|
navigate(`/dealer/claim/${requestId}`);
|
|
} else if (request?.category === 'vendor') {
|
|
navigate(`/vendor/order/${requestId}`);
|
|
} else {
|
|
navigate(`/request/${requestId}`);
|
|
}
|
|
}
|
|
|
|
// Get request URL with appropriate template
|
|
export function getRequestUrl(requestId: string, request?: any): string {
|
|
if (request?.category === 'claim-management') {
|
|
return `/dealer/claim/${requestId}`;
|
|
} else if (request?.category === 'vendor') {
|
|
return `/vendor/order/${requestId}`;
|
|
} else {
|
|
return `/request/${requestId}`;
|
|
}
|
|
}
|
|
|
|
// Check if user can access template
|
|
export function canAccessTemplate(
|
|
templateId: string,
|
|
user: any,
|
|
request: any
|
|
): boolean {
|
|
const template = getTemplate(templateId);
|
|
return template?.canAccess?.(user, request) ?? true;
|
|
}
|
|
|
|
/**
|
|
* Usage in Components
|
|
*/
|
|
|
|
// Example: Dashboard request list
|
|
function RequestList() {
|
|
const navigate = useNavigate();
|
|
const requests = useRequests();
|
|
|
|
return (
|
|
<div className="request-list">
|
|
{requests.map(request => (
|
|
<div
|
|
key={request.requestId}
|
|
onClick={() => navigateToRequest(navigate, request.requestId, request)}
|
|
className="request-item"
|
|
>
|
|
<h3>{request.title}</h3>
|
|
<p>{request.description}</p>
|
|
<span className="badge">{request.category}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Example: Link generation
|
|
function RequestLink({ request }: { request: any }) {
|
|
const url = getRequestUrl(request.requestId, request);
|
|
|
|
return (
|
|
<Link to={url} className="request-link">
|
|
View Request {request.requestId}
|
|
</Link>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* TypeScript Types for Integration
|
|
*/
|
|
|
|
interface NavigationOptions {
|
|
requestId: string;
|
|
template?: string;
|
|
request?: any;
|
|
queryParams?: Record<string, string>;
|
|
}
|
|
|
|
function navigateToRequestAdvanced(
|
|
navigate: NavigateFunction,
|
|
options: NavigationOptions
|
|
) {
|
|
const { requestId, template, request, queryParams } = options;
|
|
|
|
let url: string;
|
|
|
|
if (template) {
|
|
url = `/request/${requestId}?template=${template}`;
|
|
} else if (request) {
|
|
url = getRequestUrl(requestId, request);
|
|
} else {
|
|
url = `/request/${requestId}`;
|
|
}
|
|
|
|
if (queryParams) {
|
|
const params = new URLSearchParams(queryParams);
|
|
url += `${url.includes('?') ? '&' : '?'}${params.toString()}`;
|
|
}
|
|
|
|
navigate(url);
|
|
}
|
|
|
|
/**
|
|
* Notes:
|
|
*
|
|
* 1. Always prefer automatic template selection over explicit template prop
|
|
* 2. Use explicit template only when necessary (e.g., deep links)
|
|
* 3. Register custom templates at app startup
|
|
* 4. Use navigation helpers for consistent routing
|
|
* 5. Handle access control at route level
|
|
*/
|