410 lines
10 KiB
TypeScript
410 lines
10 KiB
TypeScript
/**
|
|
* Custom Template Example
|
|
*
|
|
* This file demonstrates how to create and use custom templates
|
|
* for specialized workflows and user types.
|
|
*/
|
|
|
|
import {
|
|
Star,
|
|
Workflow,
|
|
ClipboardList,
|
|
FileText,
|
|
Activity,
|
|
} from 'lucide-react';
|
|
import { RequestDetailTemplate } from '../types/template.types';
|
|
import { OverviewTab } from '../components/tabs/OverviewTab';
|
|
import { WorkflowTab } from '../components/tabs/WorkflowTab';
|
|
import { DocumentsTab } from '../components/tabs/DocumentsTab';
|
|
import { ActivityTab } from '../components/tabs/ActivityTab';
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { Button } from '@/components/ui/button';
|
|
import { toast } from 'sonner';
|
|
|
|
/**
|
|
* Example: Custom Tab Component
|
|
*/
|
|
export function CustomFeatureTab({ request, user, refreshDetails }: any) {
|
|
const handleCustomAction = async () => {
|
|
toast.info('Custom action triggered!');
|
|
await refreshDetails?.();
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<Star className="w-5 h-5 text-yellow-500" />
|
|
Custom Feature
|
|
</CardTitle>
|
|
<CardDescription>
|
|
This is a custom tab component for specialized workflows
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div className="p-4 bg-blue-50 rounded-lg">
|
|
<p className="text-sm text-blue-600 font-medium">Request ID</p>
|
|
<p className="text-lg font-bold text-blue-900">
|
|
{request?.requestId || 'N/A'}
|
|
</p>
|
|
</div>
|
|
|
|
<div className="p-4 bg-green-50 rounded-lg">
|
|
<p className="text-sm text-green-600 font-medium">User Role</p>
|
|
<p className="text-lg font-bold text-green-900">
|
|
{user?.role || 'N/A'}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border-t pt-4">
|
|
<h3 className="font-semibold mb-2">Custom Actions</h3>
|
|
<div className="flex gap-2">
|
|
<Button onClick={handleCustomAction}>
|
|
Execute Custom Action
|
|
</Button>
|
|
<Button variant="outline" onClick={refreshDetails}>
|
|
Refresh Data
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-gray-50 p-4 rounded-lg">
|
|
<p className="text-sm text-gray-600">
|
|
💡 <strong>Tip:</strong> You can add any custom logic, API calls, or UI components here.
|
|
This tab has access to all request data and user context.
|
|
</p>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Example: Marketing Campaign Template
|
|
*/
|
|
export const marketingCampaignTemplate: RequestDetailTemplate = {
|
|
id: 'marketingCampaign',
|
|
name: 'Marketing Campaign Request',
|
|
description: 'Template for marketing campaign approval workflows',
|
|
|
|
tabs: [
|
|
{
|
|
id: 'overview',
|
|
label: 'Overview',
|
|
icon: ClipboardList,
|
|
component: OverviewTab,
|
|
order: 1,
|
|
},
|
|
{
|
|
id: 'campaign',
|
|
label: 'Campaign Details',
|
|
icon: Star,
|
|
component: CustomFeatureTab,
|
|
order: 2,
|
|
},
|
|
{
|
|
id: 'workflow',
|
|
label: 'Approval Flow',
|
|
icon: Workflow,
|
|
component: WorkflowTab,
|
|
order: 3,
|
|
},
|
|
{
|
|
id: 'documents',
|
|
label: 'Creative Assets',
|
|
icon: FileText,
|
|
component: DocumentsTab,
|
|
order: 4,
|
|
},
|
|
{
|
|
id: 'activity',
|
|
label: 'Activity',
|
|
icon: Activity,
|
|
component: ActivityTab,
|
|
order: 5,
|
|
},
|
|
],
|
|
|
|
defaultTab: 'overview',
|
|
|
|
header: {
|
|
showBackButton: true,
|
|
showRefreshButton: true,
|
|
showShareSummaryButton: false,
|
|
},
|
|
|
|
quickActions: {
|
|
enabled: true,
|
|
customActions: [
|
|
{
|
|
id: 'schedule-campaign',
|
|
label: 'Schedule Campaign',
|
|
icon: Star,
|
|
action: async (context) => {
|
|
const { toast } = await import('sonner');
|
|
|
|
if (context.request?.status !== 'approved') {
|
|
toast.error('Campaign must be approved first');
|
|
return;
|
|
}
|
|
|
|
// TODO: Implement campaign scheduling
|
|
toast.success('Campaign scheduled successfully!');
|
|
},
|
|
visible: (context) => {
|
|
return (
|
|
context.request?.status === 'approved' &&
|
|
(context.user?.role === 'marketing' || context.isInitiator)
|
|
);
|
|
},
|
|
variant: 'default',
|
|
},
|
|
],
|
|
},
|
|
|
|
layout: {
|
|
showQuickActionsSidebar: true,
|
|
fullWidthTabs: [],
|
|
},
|
|
|
|
canAccess: (user, request) => {
|
|
const allowedRoles = ['marketing', 'admin'];
|
|
return (
|
|
allowedRoles.includes(user?.role) ||
|
|
user?.userId === request?.initiator?.userId
|
|
);
|
|
},
|
|
|
|
onInit: (context) => {
|
|
console.log('Marketing Campaign Template initialized');
|
|
|
|
// Example: Track analytics
|
|
// analytics.track('campaign_request_viewed', {
|
|
// requestId: context.request?.requestId,
|
|
// userId: context.user?.userId,
|
|
// });
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Example: Finance Approval Template
|
|
*/
|
|
export const financeApprovalTemplate: RequestDetailTemplate = {
|
|
id: 'financeApproval',
|
|
name: 'Finance Approval Request',
|
|
description: 'Template for financial approval workflows with budget tracking',
|
|
|
|
tabs: [
|
|
{
|
|
id: 'overview',
|
|
label: 'Overview',
|
|
icon: ClipboardList,
|
|
component: OverviewTab,
|
|
order: 1,
|
|
},
|
|
{
|
|
id: 'workflow',
|
|
label: 'Workflow',
|
|
icon: Workflow,
|
|
component: WorkflowTab,
|
|
order: 2,
|
|
},
|
|
{
|
|
id: 'documents',
|
|
label: 'Financial Docs',
|
|
icon: FileText,
|
|
component: DocumentsTab,
|
|
order: 3,
|
|
},
|
|
{
|
|
id: 'activity',
|
|
label: 'Activity',
|
|
icon: Activity,
|
|
component: ActivityTab,
|
|
order: 4,
|
|
},
|
|
],
|
|
|
|
defaultTab: 'overview',
|
|
|
|
header: {
|
|
showBackButton: true,
|
|
showRefreshButton: true,
|
|
showShareSummaryButton: true,
|
|
},
|
|
|
|
quickActions: {
|
|
enabled: true,
|
|
},
|
|
|
|
layout: {
|
|
showQuickActionsSidebar: true,
|
|
fullWidthTabs: [],
|
|
},
|
|
|
|
canAccess: (user, request) => {
|
|
const allowedRoles = ['finance', 'cfo', 'admin'];
|
|
return (
|
|
allowedRoles.includes(user?.role) ||
|
|
user?.userId === request?.initiator?.userId ||
|
|
user?.department === 'finance'
|
|
);
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Example: How to use custom templates
|
|
*/
|
|
export function CustomTemplateUsageExample() {
|
|
return (
|
|
<>
|
|
{/* Example 1: Explicit template selection */}
|
|
{/*
|
|
<RequestDetailTemplated
|
|
requestId="REQ-123"
|
|
template="marketingCampaign"
|
|
/>
|
|
*/}
|
|
|
|
{/* Example 2: Register custom template and let auto-selection work */}
|
|
{/*
|
|
import { registerTemplate } from '@/pages/RequestDetail/templates';
|
|
|
|
// Register at app startup
|
|
registerTemplate(marketingCampaignTemplate);
|
|
registerTemplate(financeApprovalTemplate);
|
|
|
|
// Then use normally - template will be auto-selected
|
|
<RequestDetailTemplated requestId="REQ-123" />
|
|
*/}
|
|
|
|
{/* Example 3: Update template selector for custom logic */}
|
|
{/*
|
|
// In templates/index.ts
|
|
export const selectTemplate: TemplateSelector = (user, request, routeParams) => {
|
|
// Marketing campaigns
|
|
if (request?.category === 'marketing-campaign') {
|
|
return 'marketingCampaign';
|
|
}
|
|
|
|
// Finance approvals
|
|
if (request?.type === 'financial-approval' || request?.amount > 100000) {
|
|
return 'financeApproval';
|
|
}
|
|
|
|
// Dealer claims
|
|
if (request?.category === 'claim-management') {
|
|
return 'dealerClaim';
|
|
}
|
|
|
|
// Default
|
|
return 'standard';
|
|
};
|
|
*/}
|
|
</>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Example: Conditional Tab Visibility
|
|
*/
|
|
export const conditionalTabExample: RequestDetailTemplate = {
|
|
id: 'conditionalExample',
|
|
name: 'Conditional Tab Example',
|
|
description: 'Shows how to conditionally show/hide tabs',
|
|
|
|
tabs: [
|
|
{
|
|
id: 'overview',
|
|
label: 'Overview',
|
|
icon: ClipboardList,
|
|
component: OverviewTab,
|
|
order: 1,
|
|
// Always visible
|
|
},
|
|
{
|
|
id: 'admin-only',
|
|
label: 'Admin Panel',
|
|
icon: Star,
|
|
component: CustomFeatureTab,
|
|
order: 2,
|
|
// Only visible to admins
|
|
visible: (context) => context.user?.role === 'admin',
|
|
},
|
|
{
|
|
id: 'closed-requests',
|
|
label: 'Closure Details',
|
|
icon: Star,
|
|
component: CustomFeatureTab,
|
|
order: 3,
|
|
// Only visible for closed requests
|
|
visible: (context) => context.isClosed,
|
|
},
|
|
{
|
|
id: 'initiator-only',
|
|
label: 'Initiator Actions',
|
|
icon: Star,
|
|
component: CustomFeatureTab,
|
|
order: 4,
|
|
// Only visible to request initiator
|
|
visible: (context) => context.isInitiator,
|
|
},
|
|
{
|
|
id: 'high-value',
|
|
label: 'Additional Approvals',
|
|
icon: Star,
|
|
component: CustomFeatureTab,
|
|
order: 5,
|
|
// Only visible for high-value requests
|
|
visible: (context) => {
|
|
const amount = context.request?.amount || 0;
|
|
return amount > 100000;
|
|
},
|
|
},
|
|
],
|
|
|
|
defaultTab: 'overview',
|
|
|
|
header: {
|
|
showBackButton: true,
|
|
showRefreshButton: true,
|
|
},
|
|
|
|
quickActions: {
|
|
enabled: true,
|
|
},
|
|
|
|
layout: {
|
|
showQuickActionsSidebar: true,
|
|
fullWidthTabs: [],
|
|
},
|
|
|
|
canAccess: () => true,
|
|
};
|
|
|
|
/**
|
|
* How to register these templates:
|
|
*
|
|
* 1. Import in templates/index.ts:
|
|
* import { marketingCampaignTemplate } from './examples/CustomTemplateExample';
|
|
*
|
|
* 2. Add to registry:
|
|
* export const templateRegistry = {
|
|
* standard: standardTemplate,
|
|
* dealerClaim: dealerClaimTemplate,
|
|
* marketingCampaign: marketingCampaignTemplate,
|
|
* // ...
|
|
* };
|
|
*
|
|
* 3. Update selector:
|
|
* export const selectTemplate = (user, request) => {
|
|
* if (request?.category === 'marketing') return 'marketingCampaign';
|
|
* // ...
|
|
* };
|
|
*/
|
|
|