summary shared to implemented

This commit is contained in:
laxmanhalaki 2025-11-25 17:16:09 +05:30
parent f883bd34d8
commit 3dbe5e6900
4 changed files with 108 additions and 2 deletions

View File

@ -228,7 +228,7 @@ export function PageLayout({ children, currentPage = 'dashboard', onNavigate, on
alt="Royal Enfield Logo" alt="Royal Enfield Logo"
className="h-10 w-auto max-w-[168px] object-contain" className="h-10 w-auto max-w-[168px] object-contain"
/> />
<p className="text-xs text-gray-400 text-center mt-1 truncate">Approval Portal</p> <p className="text-xs text-gray-400 text-center mt-1 truncate">RE Flow</p>
</div> </div>
</div> </div>
<div className="p-3 flex-1 overflow-y-auto"> <div className="p-3 flex-1 overflow-y-auto">

View File

@ -104,6 +104,7 @@ function RequestDetailInner({ requestId: propRequestId, onBack, dynamicRequests
const [summaryId, setSummaryId] = useState<string | null>(null); const [summaryId, setSummaryId] = useState<string | null>(null);
const [summaryDetails, setSummaryDetails] = useState<SummaryDetails | null>(null); const [summaryDetails, setSummaryDetails] = useState<SummaryDetails | null>(null);
const [loadingSummary, setLoadingSummary] = useState(false); const [loadingSummary, setLoadingSummary] = useState(false);
const [sharedRecipientsRefreshTrigger, setSharedRecipientsRefreshTrigger] = useState(0);
const { user } = useAuth(); const { user } = useAuth();
// Custom hooks // Custom hooks
@ -468,6 +469,8 @@ function RequestDetailInner({ requestId: propRequestId, onBack, dynamicRequests
onAddSpectator={() => setShowAddSpectatorModal(true)} onAddSpectator={() => setShowAddSpectatorModal(true)}
onApprove={() => setShowApproveModal(true)} onApprove={() => setShowApproveModal(true)}
onReject={() => setShowRejectModal(true)} onReject={() => setShowRejectModal(true)}
summaryId={summaryId}
refreshTrigger={sharedRecipientsRefreshTrigger}
/> />
)} )}
</div> </div>
@ -484,6 +487,8 @@ function RequestDetailInner({ requestId: propRequestId, onBack, dynamicRequests
requestTitle={request?.title || 'N/A'} requestTitle={request?.title || 'N/A'}
onSuccess={() => { onSuccess={() => {
refreshDetails(); refreshDetails();
// Trigger refresh of shared recipients list
setSharedRecipientsRefreshTrigger(prev => prev + 1);
}} }}
/> />
)} )}

View File

@ -2,10 +2,12 @@
* Quick Actions Sidebar Component * Quick Actions Sidebar Component
*/ */
import { useEffect, useState } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Avatar, AvatarFallback } from '@/components/ui/avatar'; import { Avatar, AvatarFallback } from '@/components/ui/avatar';
import { UserPlus, Eye, CheckCircle, XCircle } from 'lucide-react'; import { UserPlus, Eye, CheckCircle, XCircle, Share2 } from 'lucide-react';
import { getSharedRecipients, type SharedRecipient } from '@/services/summaryApi';
interface QuickActionsSidebarProps { interface QuickActionsSidebarProps {
request: any; request: any;
@ -16,6 +18,8 @@ interface QuickActionsSidebarProps {
onAddSpectator: () => void; onAddSpectator: () => void;
onApprove: () => void; onApprove: () => void;
onReject: () => void; onReject: () => void;
summaryId?: string | null;
refreshTrigger?: number; // Trigger to refresh shared recipients list
} }
export function QuickActionsSidebar({ export function QuickActionsSidebar({
@ -27,7 +31,36 @@ export function QuickActionsSidebar({
onAddSpectator, onAddSpectator,
onApprove, onApprove,
onReject, onReject,
summaryId,
refreshTrigger,
}: QuickActionsSidebarProps) { }: QuickActionsSidebarProps) {
const [sharedRecipients, setSharedRecipients] = useState<SharedRecipient[]>([]);
const [loadingRecipients, setLoadingRecipients] = useState(false);
const isClosed = request?.status === 'closed';
// Fetch shared recipients when request is closed and summaryId is available
useEffect(() => {
const fetchSharedRecipients = async () => {
if (!isClosed || !summaryId || !isInitiator) {
setSharedRecipients([]);
return;
}
try {
setLoadingRecipients(true);
const recipients = await getSharedRecipients(summaryId);
setSharedRecipients(recipients);
} catch (error) {
console.error('Failed to fetch shared recipients:', error);
setSharedRecipients([]);
} finally {
setLoadingRecipients(false);
}
};
fetchSharedRecipients();
}, [isClosed, summaryId, isInitiator, refreshTrigger]);
return ( return (
<div className="space-y-4 sm:space-y-6"> <div className="space-y-4 sm:space-y-6">
{/* Quick Actions Card - Hide entire card for spectators and closed requests */} {/* Quick Actions Card - Hide entire card for spectators and closed requests */}
@ -118,6 +151,55 @@ export function QuickActionsSidebar({
)} )}
</CardContent> </CardContent>
</Card> </Card>
{/* Shared Recipients Card - Only for closed requests */}
{isClosed && isInitiator && (
<Card data-testid="shared-recipients-card">
<CardHeader className="pb-2">
<CardTitle className="text-sm sm:text-base flex items-center gap-2">
<Share2 className="w-4 h-4" />
Summary Shared With
</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
{loadingRecipients ? (
<div className="py-4 text-center">
<p className="text-sm text-gray-500">Loading...</p>
</div>
) : sharedRecipients.length > 0 ? (
sharedRecipients.map((recipient, index) => {
const avatar = (recipient.displayName || 'NA')
.split(' ')
.map((s: string) => s[0])
.join('')
.slice(0, 2)
.toUpperCase();
return (
<div key={recipient.userId || index} className="flex items-center gap-3" data-testid={`shared-recipient-${index}`}>
<Avatar className="h-8 w-8">
<AvatarFallback className="bg-green-100 text-green-800 text-xs font-semibold">
{avatar}
</AvatarFallback>
</Avatar>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-gray-900">{recipient.displayName}</p>
<p className="text-xs text-gray-500 truncate">{recipient.email}</p>
{recipient.isRead && (
<p className="text-xs text-green-600 mt-0.5">Viewed</p>
)}
</div>
</div>
);
})
) : (
<div className="py-4 text-center">
<p className="text-sm text-gray-500">Summary not shared yet</p>
</div>
)}
</CardContent>
</Card>
)}
</div> </div>
); );
} }

View File

@ -143,3 +143,22 @@ export async function getSummaryByRequestId(requestId: string): Promise<RequestS
} }
} }
export interface SharedRecipient {
userId: string;
email: string;
displayName: string;
designation: string | null;
department: string | null;
sharedAt: string;
viewedAt: string | null;
isRead: boolean;
}
/**
* Get list of users who received shared summary for a specific summary
*/
export async function getSharedRecipients(summaryId: string): Promise<SharedRecipient[]> {
const res = await apiClient.get(`/summaries/${summaryId}/recipients`);
return res.data.data || [];
}