mobile responsive changes

This commit is contained in:
laxmanhalaki 2025-11-06 10:13:57 +05:30
parent 75cfd57514
commit 6b7837540b
7 changed files with 373 additions and 359 deletions

View File

@ -250,7 +250,7 @@ export function PageLayout({ children, currentPage = 'dashboard', onNavigate, on
</header> </header>
{/* Main Content */} {/* Main Content */}
<main className="flex-1 p-6 overflow-auto min-w-0"> <main className="flex-1 p-2 sm:p-4 lg:p-6 overflow-auto min-w-0">
{children} {children}
</main> </main>
</div> </div>

View File

@ -390,7 +390,11 @@ export function WorkNoteChat({ requestId, onBack, messages: externalMessages, on
} }
} catch {} } catch {}
try { try {
const base = (import.meta as any).env.VITE_BASE_URL || window.location.origin; // Get backend URL from environment (same as API calls)
// Strip /api/v1 suffix if present to get base WebSocket URL
const apiBaseUrl = (import.meta as any).env.VITE_API_BASE_URL || 'http://localhost:5000/api/v1';
const base = apiBaseUrl.replace(/\/api\/v1$/, '');
console.log('[WorkNoteChat] Connecting socket to:', base);
const s = getSocket(base); const s = getSocket(base);
// Only join room if not skipped (standalone mode) // Only join room if not skipped (standalone mode)

View File

@ -337,8 +337,8 @@ function BackendAuthProvider({ children }: { children: ReactNode }) {
try { try {
setError(null); setError(null);
// Redirect to Okta login // Redirect to Okta login
const oktaDomain = 'https://dev-830839.oktapreview.com'; const oktaDomain = import.meta.env.VITE_OKTA_DOMAIN || 'https://dev-830839.oktapreview.com';
const clientId = '0oa2j8slwj5S4bG5k0h8'; const clientId = import.meta.env.VITE_OKTA_CLIENT_ID || '0oa2jgzvrpdwx2iqd0h8';
const redirectUri = `${window.location.origin}/login/callback`; const redirectUri = `${window.location.origin}/login/callback`;
const responseType = 'code'; const responseType = 'code';
const scope = 'openid profile email'; const scope = 'openid profile email';

View File

@ -180,43 +180,44 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
].filter(Boolean).length; ].filter(Boolean).length;
return ( return (
<div className="space-y-6 p-6 max-w-7xl mx-auto"> <div className="space-y-4 sm:space-y-6 max-w-7xl mx-auto">
{/* Enhanced Header */} {/* Enhanced Header */}
<div className="flex flex-col lg:flex-row lg:items-center justify-between gap-6"> <div className="flex flex-col lg:flex-row lg:items-center justify-between gap-3 sm:gap-4 md:gap-6">
<div className="space-y-2"> <div className="space-y-1 sm:space-y-2">
<div className="flex items-center gap-3"> <div className="flex items-center gap-2 sm:gap-3">
<div className="w-12 h-12 bg-gradient-to-br from-slate-800 to-slate-900 rounded-xl flex items-center justify-center shadow-lg"> <div className="w-10 h-10 sm:w-12 sm:h-12 bg-gradient-to-br from-slate-800 to-slate-900 rounded-xl flex items-center justify-center shadow-lg">
<FileText className="w-6 h-6 text-white" /> <FileText className="w-5 h-5 sm:w-6 sm:h-6 text-white" />
</div> </div>
<div> <div>
<h1 className="text-3xl font-bold text-gray-900">Closed Requests</h1> <h1 className="text-xl sm:text-2xl md:text-3xl font-bold text-gray-900">Closed Requests</h1>
<p className="text-gray-600">Review completed and archived requests</p> <p className="text-sm sm:text-base text-gray-600">Review completed and archived requests</p>
</div> </div>
</div> </div>
</div> </div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-2 sm:gap-3">
<Badge variant="secondary" className="text-lg px-4 py-2 bg-slate-100 text-slate-800 font-semibold"> <Badge variant="secondary" className="text-xs sm:text-sm md:text-base px-2 sm:px-3 md:px-4 py-1 sm:py-1.5 md:py-2 bg-slate-100 text-slate-800 font-semibold">
{loading ? 'Loading…' : `${filteredAndSortedRequests.length} closed requests`} {loading ? 'Loading…' : `${filteredAndSortedRequests.length} closed`}
<span className="hidden sm:inline ml-1">requests</span>
</Badge> </Badge>
<Button variant="outline" size="sm" className="gap-2"> <Button variant="outline" size="sm" className="gap-1 sm:gap-2 h-8 sm:h-9">
<RefreshCw className="w-4 h-4" /> <RefreshCw className="w-3.5 h-3.5 sm:w-4 sm:h-4" />
Refresh <span className="hidden sm:inline">Refresh</span>
</Button> </Button>
</div> </div>
</div> </div>
{/* Enhanced Filters Section */} {/* Enhanced Filters Section */}
<Card className="shadow-lg border-0"> <Card className="shadow-lg border-0">
<CardHeader className="pb-4"> <CardHeader className="pb-3 sm:pb-4 px-3 sm:px-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-3"> <div className="flex items-center gap-2 sm:gap-3">
<div className="p-2 bg-blue-100 rounded-lg"> <div className="p-1.5 sm:p-2 bg-blue-100 rounded-lg">
<Filter className="h-5 w-5 text-blue-600" /> <Filter className="h-4 w-4 sm:h-5 sm:w-5 text-blue-600" />
</div> </div>
<div> <div>
<CardTitle className="text-lg">Filters & Search</CardTitle> <CardTitle className="text-base sm:text-lg">Filters & Search</CardTitle>
<CardDescription> <CardDescription className="text-xs sm:text-sm">
{activeFiltersCount > 0 && ( {activeFiltersCount > 0 && (
<span className="text-blue-600 font-medium"> <span className="text-blue-600 font-medium">
{activeFiltersCount} filter{activeFiltersCount > 1 ? 's' : ''} active {activeFiltersCount} filter{activeFiltersCount > 1 ? 's' : ''} active
@ -225,45 +226,45 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
</CardDescription> </CardDescription>
</div> </div>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-1 sm:gap-2">
{activeFiltersCount > 0 && ( {activeFiltersCount > 0 && (
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={clearFilters} onClick={clearFilters}
className="text-red-600 hover:bg-red-50 gap-1" className="text-red-600 hover:bg-red-50 gap-1 h-8 sm:h-9 px-2 sm:px-3"
> >
<X className="w-3 h-3" /> <X className="w-3 h-3 sm:w-3.5 sm:h-3.5" />
Clear <span className="text-xs sm:text-sm">Clear</span>
</Button> </Button>
)} )}
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={() => setShowAdvancedFilters(!showAdvancedFilters)} onClick={() => setShowAdvancedFilters(!showAdvancedFilters)}
className="gap-2" className="gap-1 sm:gap-2 h-8 sm:h-9 px-2 sm:px-3"
> >
<Settings2 className="w-4 h-4" /> <Settings2 className="w-3.5 h-3.5 sm:w-4 sm:h-4" />
{showAdvancedFilters ? 'Basic' : 'Advanced'} <span className="text-xs sm:text-sm hidden md:inline">{showAdvancedFilters ? 'Basic' : 'Advanced'}</span>
</Button> </Button>
</div> </div>
</div> </div>
</CardHeader> </CardHeader>
<CardContent className="space-y-4"> <CardContent className="space-y-3 sm:space-y-4 px-3 sm:px-6">
{/* Primary filters */} {/* Primary filters */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3 sm:gap-4">
<div className="relative"> <div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" /> <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-3.5 h-3.5 sm:w-4 sm:h-4" />
<Input <Input
placeholder="Search requests, IDs, or initiators..." placeholder="Search requests, IDs..."
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
className="pl-10 h-11 bg-gray-50 border-gray-200 focus:bg-white transition-colors" className="pl-9 sm:pl-10 h-9 sm:h-10 md:h-11 text-sm sm:text-base bg-gray-50 border-gray-200 focus:bg-white transition-colors"
/> />
</div> </div>
<Select value={priorityFilter} onValueChange={setPriorityFilter}> <Select value={priorityFilter} onValueChange={setPriorityFilter}>
<SelectTrigger className="h-11 bg-gray-50 border-gray-200 focus:bg-white"> <SelectTrigger className="h-9 sm:h-10 md:h-11 text-sm sm:text-base bg-gray-50 border-gray-200 focus:bg-white">
<SelectValue placeholder="All Priorities" /> <SelectValue placeholder="All Priorities" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -284,7 +285,7 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
</Select> </Select>
<Select value={statusFilter} onValueChange={setStatusFilter}> <Select value={statusFilter} onValueChange={setStatusFilter}>
<SelectTrigger className="h-11 bg-gray-50 border-gray-200 focus:bg-white"> <SelectTrigger className="h-9 sm:h-10 md:h-11 text-sm sm:text-base bg-gray-50 border-gray-200 focus:bg-white">
<SelectValue placeholder="All Statuses" /> <SelectValue placeholder="All Statuses" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -306,7 +307,7 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
<div className="flex gap-2"> <div className="flex gap-2">
<Select value={sortBy} onValueChange={(value: any) => setSortBy(value)}> <Select value={sortBy} onValueChange={(value: any) => setSortBy(value)}>
<SelectTrigger className="h-11 bg-gray-50 border-gray-200 focus:bg-white"> <SelectTrigger className="h-9 sm:h-10 md:h-11 text-sm sm:text-base bg-gray-50 border-gray-200 focus:bg-white">
<SelectValue placeholder="Sort by" /> <SelectValue placeholder="Sort by" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -320,9 +321,9 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
variant="outline" variant="outline"
size="sm" size="sm"
onClick={() => setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc')} onClick={() => setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc')}
className="px-3 h-11" className="px-2 sm:px-3 h-9 sm:h-10 md:h-11"
> >
{sortOrder === 'asc' ? <SortAsc className="w-4 h-4" /> : <SortDesc className="w-4 h-4" />} {sortOrder === 'asc' ? <SortAsc className="w-3.5 h-3.5 sm:w-4 sm:h-4" /> : <SortDesc className="w-3.5 h-3.5 sm:w-4 sm:h-4" />}
</Button> </Button>
</div> </div>
</div> </div>
@ -341,89 +342,87 @@ export function ClosedRequests({ onViewRequest }: ClosedRequestsProps) {
className="group hover:shadow-xl transition-all duration-300 cursor-pointer border-0 shadow-md hover:scale-[1.01]" className="group hover:shadow-xl transition-all duration-300 cursor-pointer border-0 shadow-md hover:scale-[1.01]"
onClick={() => onViewRequest?.(request.id, request.title)} onClick={() => onViewRequest?.(request.id, request.title)}
> >
<CardContent className="p-6"> <CardContent className="p-3 sm:p-6">
<div className="flex items-start gap-6"> <div className="flex flex-col sm:flex-row items-start gap-3 sm:gap-6">
{/* Priority Indicator */} {/* Priority Indicator */}
<div className="flex flex-col items-center gap-2 pt-1"> <div className="flex sm:flex-col items-center gap-2 pt-1 w-full sm:w-auto">
<div className={`p-3 rounded-xl ${priorityConfig.color} border`}> <div className={`p-2 sm:p-3 rounded-xl ${priorityConfig.color} border flex-shrink-0`}>
<priorityConfig.icon className={`w-5 h-5 ${priorityConfig.iconColor}`} /> <priorityConfig.icon className={`w-4 h-4 sm:w-5 sm:h-5 ${priorityConfig.iconColor}`} />
</div> </div>
<Badge <Badge
variant="outline" variant="outline"
className={`text-xs font-medium ${priorityConfig.color} capitalize`} className={`text-xs font-medium ${priorityConfig.color} capitalize flex-shrink-0`}
> >
{request.priority} {request.priority}
</Badge> </Badge>
</div> </div>
{/* Main Content */} {/* Main Content */}
<div className="flex-1 min-w-0 space-y-4"> <div className="flex-1 min-w-0 space-y-3 sm:space-y-4 w-full">
{/* Header */} {/* Header */}
<div className="flex items-start justify-between gap-4"> <div className="flex items-start justify-between gap-2 sm:gap-4">
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="flex items-center gap-3 mb-2"> <div className="flex flex-wrap items-center gap-1.5 sm:gap-3 mb-2">
<h3 className="text-lg font-semibold text-gray-900 group-hover:text-blue-600 transition-colors"> <h3 className="text-sm sm:text-lg font-semibold text-gray-900 group-hover:text-blue-600 transition-colors">
{(request as any).displayId || request.id} {(request as any).displayId || request.id}
</h3> </h3>
<Badge <Badge
variant="outline" variant="outline"
className={`${statusConfig.color} border font-medium`} className={`${statusConfig.color} border font-medium text-xs shrink-0`}
> >
<statusConfig.icon className="w-3 h-3 mr-1" /> <statusConfig.icon className="w-3 h-3 mr-1" />
{request.status} <span className="capitalize">{request.status}</span>
</Badge> </Badge>
{request.department && ( {request.department && (
<Badge variant="secondary" className="bg-gray-100 text-gray-700"> <Badge variant="secondary" className="bg-gray-100 text-gray-700 text-xs hidden sm:inline-flex shrink-0">
{request.department} {request.department}
</Badge> </Badge>
)} )}
</div> </div>
<h4 className="text-xl font-bold text-gray-900 mb-2 line-clamp-1"> <h4 className="text-base sm:text-xl font-bold text-gray-900 mb-2 line-clamp-2">
{request.title} {request.title}
</h4> </h4>
<p className="text-gray-600 line-clamp-2 leading-relaxed"> <p className="text-xs sm:text-sm text-gray-600 line-clamp-2 leading-relaxed">
{request.description} {request.description}
</p> </p>
</div> </div>
<div className="flex flex-col items-end gap-2"> <div className="flex flex-col items-end gap-2 flex-shrink-0">
<ArrowRight className="w-5 h-5 text-gray-400 group-hover:text-blue-600 transition-colors" /> <ArrowRight className="w-4 h-4 sm:w-5 sm:h-5 text-gray-400 group-hover:text-blue-600 transition-colors" />
</div> </div>
</div> </div>
{/* Status Info */} {/* Status Info */}
<div className="flex items-center gap-4 p-4 bg-gray-50 rounded-lg"> <div className="flex items-center gap-2 sm:gap-4 p-3 sm:p-4 bg-gray-50 rounded-lg">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2 min-w-0">
<CheckCircle className="w-4 h-4 text-green-500" /> <CheckCircle className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-green-500 flex-shrink-0" />
<span className="text-sm text-gray-700 font-medium"> <span className="text-xs sm:text-sm text-gray-700 font-medium truncate">
{request.reason} {request.reason}
</span> </span>
</div> </div>
</div> </div>
{/* Participants & Metadata */} {/* Participants & Metadata */}
<div className="flex items-center justify-between"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 sm:gap-6">
<div className="flex items-center gap-6"> <div className="flex items-center gap-2 min-w-0">
<div className="flex items-center gap-2"> <Avatar className="h-7 w-7 sm:h-8 sm:w-8 ring-2 ring-white shadow-sm flex-shrink-0">
<Avatar className="h-8 w-8 ring-2 ring-white shadow-sm"> <AvatarFallback className="bg-slate-700 text-white text-xs sm:text-sm font-semibold">
<AvatarFallback className="bg-slate-700 text-white text-sm font-semibold"> {request.initiator.avatar}
{request.initiator.avatar} </AvatarFallback>
</AvatarFallback> </Avatar>
</Avatar> <div className="min-w-0">
<div> <p className="text-xs sm:text-sm font-medium text-gray-900 truncate">{request.initiator.name}</p>
<p className="text-sm font-medium text-gray-900">{request.initiator.name}</p> <p className="text-xs text-gray-500">Initiator</p>
<p className="text-xs text-gray-500">Initiator</p>
</div>
</div> </div>
</div> </div>
<div className="text-right"> <div className="text-left sm:text-right">
<div className="flex items-center gap-4 text-xs text-gray-500"> <div className="flex flex-col gap-1 text-xs text-gray-500">
<span className="flex items-center gap-1"> <span className="flex items-center gap-1">
<Calendar className="w-3 h-3" /> <Calendar className="w-3 h-3 flex-shrink-0" />
Created {request.createdAt} <span className="truncate">Created {request.createdAt}</span>
</span> </span>
<span>Closed {request.dueDate}</span> <span className="truncate">Closed {request.dueDate}</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -210,78 +210,78 @@ export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsPr
}; };
return ( return (
<div className="space-y-6"> <div className="space-y-4 sm:space-y-6">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<h1 className="text-2xl font-semibold text-gray-900 flex items-center gap-2"> <h1 className="text-xl sm:text-2xl md:text-3xl font-semibold text-gray-900 flex items-center gap-2">
<User className="w-7 h-7 text-[--re-green]" /> <User className="w-5 h-5 sm:w-6 sm:h-6 md:w-7 md:h-7 text-[--re-green]" />
My Requests My Requests
</h1> </h1>
<p className="text-gray-600 mt-1"> <p className="text-sm sm:text-base text-gray-600 mt-1">
Track and manage all your submitted requests Track and manage all your submitted requests
</p> </p>
</div> </div>
</div> </div>
{/* Stats Overview */} {/* Stats Overview */}
<div className="grid grid-cols-2 md:grid-cols-5 gap-4"> <div className="grid grid-cols-2 md:grid-cols-5 gap-3 sm:gap-4">
<Card className="bg-gradient-to-br from-blue-50 to-blue-100 border-blue-200"> <Card className="bg-gradient-to-br from-blue-50 to-blue-100 border-blue-200">
<CardContent className="p-4"> <CardContent className="p-3 sm:p-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm text-blue-700 font-medium">Total</p> <p className="text-xs sm:text-sm text-blue-700 font-medium">Total</p>
<p className="text-2xl font-bold text-blue-900">{stats.total}</p> <p className="text-xl sm:text-2xl font-bold text-blue-900">{stats.total}</p>
</div> </div>
<FileText className="w-8 h-8 text-blue-600" /> <FileText className="w-6 h-6 sm:w-8 sm:h-8 text-blue-600" />
</div> </div>
</CardContent> </CardContent>
</Card> </Card>
<Card className="bg-gradient-to-br from-orange-50 to-orange-100 border-orange-200"> <Card className="bg-gradient-to-br from-orange-50 to-orange-100 border-orange-200">
<CardContent className="p-4"> <CardContent className="p-3 sm:p-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm text-orange-700 font-medium">In Progress</p> <p className="text-xs sm:text-sm text-orange-700 font-medium">In Progress</p>
<p className="text-2xl font-bold text-orange-900">{stats.pending + stats.inReview}</p> <p className="text-xl sm:text-2xl font-bold text-orange-900">{stats.pending + stats.inReview}</p>
</div> </div>
<Clock className="w-8 h-8 text-orange-600" /> <Clock className="w-6 h-6 sm:w-8 sm:h-8 text-orange-600" />
</div> </div>
</CardContent> </CardContent>
</Card> </Card>
<Card className="bg-gradient-to-br from-green-50 to-green-100 border-green-200"> <Card className="bg-gradient-to-br from-green-50 to-green-100 border-green-200">
<CardContent className="p-4"> <CardContent className="p-3 sm:p-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm text-green-700 font-medium">Approved</p> <p className="text-xs sm:text-sm text-green-700 font-medium">Approved</p>
<p className="text-2xl font-bold text-green-900">{stats.approved}</p> <p className="text-xl sm:text-2xl font-bold text-green-900">{stats.approved}</p>
</div> </div>
<CheckCircle className="w-8 h-8 text-green-600" /> <CheckCircle className="w-6 h-6 sm:w-8 sm:h-8 text-green-600" />
</div> </div>
</CardContent> </CardContent>
</Card> </Card>
<Card className="bg-gradient-to-br from-red-50 to-red-100 border-red-200"> <Card className="bg-gradient-to-br from-red-50 to-red-100 border-red-200">
<CardContent className="p-4"> <CardContent className="p-3 sm:p-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm text-red-700 font-medium">Rejected</p> <p className="text-xs sm:text-sm text-red-700 font-medium">Rejected</p>
<p className="text-2xl font-bold text-red-900">{stats.rejected}</p> <p className="text-xl sm:text-2xl font-bold text-red-900">{stats.rejected}</p>
</div> </div>
<XCircle className="w-8 h-8 text-red-600" /> <XCircle className="w-6 h-6 sm:w-8 sm:h-8 text-red-600" />
</div> </div>
</CardContent> </CardContent>
</Card> </Card>
<Card className="bg-gradient-to-br from-gray-50 to-gray-100 border-gray-200"> <Card className="bg-gradient-to-br from-gray-50 to-gray-100 border-gray-200">
<CardContent className="p-4"> <CardContent className="p-3 sm:p-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm text-gray-700 font-medium">Draft</p> <p className="text-xs sm:text-sm text-gray-700 font-medium">Draft</p>
<p className="text-2xl font-bold text-gray-900">{stats.draft}</p> <p className="text-xl sm:text-2xl font-bold text-gray-900">{stats.draft}</p>
</div> </div>
<Edit className="w-8 h-8 text-gray-600" /> <Edit className="w-6 h-6 sm:w-8 sm:h-8 text-gray-600" />
</div> </div>
</CardContent> </CardContent>
</Card> </Card>
@ -289,21 +289,21 @@ export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsPr
{/* Filters and Search */} {/* Filters and Search */}
<Card className="border-gray-200"> <Card className="border-gray-200">
<CardContent className="p-6"> <CardContent className="p-3 sm:p-4 md:p-6">
<div className="flex flex-col md:flex-row gap-4 items-start md:items-center"> <div className="flex flex-col md:flex-row gap-3 sm:gap-4 items-start md:items-center">
<div className="flex-1 relative"> <div className="flex-1 relative w-full">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" /> <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-3.5 h-3.5 sm:w-4 sm:h-4" />
<Input <Input
placeholder="Search requests by title, description, or ID..." placeholder="Search requests by title, description, or ID..."
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
className="pl-9 bg-white border-gray-300 hover:border-gray-400 focus:border-re-green focus:ring-1 focus:ring-re-green" className="pl-9 text-sm sm:text-base bg-white border-gray-300 hover:border-gray-400 focus:border-re-green focus:ring-1 focus:ring-re-green h-9 sm:h-10"
/> />
</div> </div>
<div className="flex gap-3"> <div className="flex gap-2 sm:gap-3 w-full md:w-auto">
<Select value={statusFilter} onValueChange={setStatusFilter}> <Select value={statusFilter} onValueChange={setStatusFilter}>
<SelectTrigger className="w-32 bg-white border-gray-300 hover:border-gray-400 focus:border-re-green focus:ring-1 focus:ring-re-green"> <SelectTrigger className="flex-1 md:w-28 lg:w-32 text-xs sm:text-sm bg-white border-gray-300 hover:border-gray-400 focus:border-re-green focus:ring-1 focus:ring-re-green h-9 sm:h-10">
<SelectValue placeholder="Status" /> <SelectValue placeholder="Status" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -317,7 +317,7 @@ export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsPr
</Select> </Select>
<Select value={priorityFilter} onValueChange={setPriorityFilter}> <Select value={priorityFilter} onValueChange={setPriorityFilter}>
<SelectTrigger className="w-32 bg-white border-gray-300 hover:border-gray-400 focus:border-re-green focus:ring-1 focus:ring-re-green"> <SelectTrigger className="flex-1 md:w-28 lg:w-32 text-xs sm:text-sm bg-white border-gray-300 hover:border-gray-400 focus:border-re-green focus:ring-1 focus:ring-re-green h-9 sm:h-10">
<SelectValue placeholder="Priority" /> <SelectValue placeholder="Priority" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -361,61 +361,65 @@ export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsPr
className="group hover:shadow-lg transition-all duration-300 cursor-pointer border border-gray-200 shadow-sm hover:shadow-md" className="group hover:shadow-lg transition-all duration-300 cursor-pointer border border-gray-200 shadow-sm hover:shadow-md"
onClick={() => onViewRequest(request.id, request.title)} onClick={() => onViewRequest(request.id, request.title)}
> >
<CardContent className="p-6"> <CardContent className="p-3 sm:p-6">
<div className="space-y-4"> <div className="space-y-3 sm:space-y-4">
{/* Header with Title and Status Badges */} {/* Header with Title and Status Badges */}
<div className="flex items-start justify-between"> <div className="flex items-start justify-between gap-2">
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<h4 className="text-lg font-semibold text-gray-900 mb-2 group-hover:text-blue-600 transition-colors"> <h4 className="text-base sm:text-lg font-semibold text-gray-900 mb-2 group-hover:text-blue-600 transition-colors line-clamp-2">
{request.title} {request.title}
</h4> </h4>
<div className="flex items-center gap-2 mb-2"> <div className="flex flex-wrap items-center gap-1.5 sm:gap-2 mb-2">
<Badge <Badge
variant="outline" variant="outline"
className={`${getStatusConfig(request.status).color} border font-medium text-xs`} className={`${getStatusConfig(request.status).color} border font-medium text-xs shrink-0`}
> >
{(() => { {(() => {
const IconComponent = getStatusConfig(request.status).icon; const IconComponent = getStatusConfig(request.status).icon;
return <IconComponent className="w-3 h-3 mr-1" />; return <IconComponent className="w-3 h-3 mr-1" />;
})()} })()}
{request.status} <span className="capitalize">{request.status}</span>
</Badge> </Badge>
<Badge <Badge
variant="outline" variant="outline"
className={`${getPriorityConfig(request.priority).color} border font-medium text-xs capitalize`} className={`${getPriorityConfig(request.priority).color} border font-medium text-xs capitalize shrink-0`}
> >
{(() => {
const IconComponent = getPriorityConfig(request.priority).icon;
return <IconComponent className="w-3 h-3 mr-1" />;
})()}
{request.priority} {request.priority}
</Badge> </Badge>
{(request as any).templateType && ( {(request as any).templateType && (
<Badge variant="secondary" className="bg-purple-100 text-purple-700 text-xs"> <Badge variant="secondary" className="bg-purple-100 text-purple-700 text-xs shrink-0 hidden sm:inline-flex">
<FileText className="w-3 h-3 mr-1" /> <FileText className="w-3 h-3 mr-1" />
Template: {(request as any).templateName} Template: {(request as any).templateName}
</Badge> </Badge>
)} )}
</div> </div>
<p className="text-sm text-gray-600 mb-3"> <p className="text-xs sm:text-sm text-gray-600 mb-2 sm:mb-3 line-clamp-2">
{request.description} {request.description}
</p> </p>
<div className="flex items-center gap-4 text-sm text-gray-500"> <div className="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-4 text-xs sm:text-sm text-gray-500">
<span><span className="font-medium">ID:</span> {(request as any).displayId || request.id}</span> <span className="truncate"><span className="font-medium">ID:</span> {(request as any).displayId || request.id}</span>
<span><span className="font-medium">Submitted:</span> {request.submittedDate ? new Date(request.submittedDate).toLocaleDateString() : 'N/A'}</span> <span className="truncate"><span className="font-medium">Submitted:</span> {request.submittedDate ? new Date(request.submittedDate).toLocaleDateString() : 'N/A'}</span>
</div> </div>
</div> </div>
<ArrowRight className="w-5 h-5 text-gray-400 group-hover:text-blue-600 transition-colors flex-shrink-0" /> <ArrowRight className="w-4 h-4 sm:w-5 sm:h-5 text-gray-400 group-hover:text-blue-600 transition-colors flex-shrink-0 mt-1" />
</div> </div>
{/* Current Approver and Level Info */} {/* Current Approver and Level Info */}
<div className="flex items-center justify-between pt-3 border-t border-gray-100"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 sm:gap-4 pt-3 border-t border-gray-100">
<div className="flex items-center gap-4"> <div className="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2 min-w-0">
<User className="w-4 h-4 text-gray-400" /> <User className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-gray-400 flex-shrink-0" />
<span className="text-sm"> <span className="text-xs sm:text-sm truncate">
<span className="text-gray-500">Current:</span> <span className="text-gray-900 font-medium">{request.currentApprover}</span> <span className="text-gray-500">Current:</span> <span className="text-gray-900 font-medium">{request.currentApprover}</span>
</span> </span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<TrendingUp className="w-4 h-4 text-gray-400" /> <TrendingUp className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-gray-400 flex-shrink-0" />
<span className="text-sm"> <span className="text-xs sm:text-sm">
<span className="text-gray-500">Level:</span> <span className="text-gray-900 font-medium">{request.approverLevel}</span> <span className="text-gray-500">Level:</span> <span className="text-gray-900 font-medium">{request.approverLevel}</span>
</span> </span>
</div> </div>

View File

@ -243,43 +243,44 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
].filter(Boolean).length; ].filter(Boolean).length;
return ( return (
<div className="space-y-6 p-6 max-w-7xl mx-auto"> <div className="space-y-4 sm:space-y-6 max-w-7xl mx-auto">
{/* Enhanced Header */} {/* Enhanced Header */}
<div className="flex flex-col lg:flex-row lg:items-center justify-between gap-6"> <div className="flex flex-col lg:flex-row lg:items-center justify-between gap-3 sm:gap-4 md:gap-6">
<div className="space-y-2"> <div className="space-y-1 sm:space-y-2">
<div className="flex items-center gap-3"> <div className="flex items-center gap-2 sm:gap-3">
<div className="w-12 h-12 bg-gradient-to-br from-slate-800 to-slate-900 rounded-xl flex items-center justify-center shadow-lg"> <div className="w-10 h-10 sm:w-12 sm:h-12 bg-gradient-to-br from-slate-800 to-slate-900 rounded-xl flex items-center justify-center shadow-lg">
<FileText className="w-6 h-6 text-white" /> <FileText className="w-5 h-5 sm:w-6 sm:h-6 text-white" />
</div> </div>
<div> <div>
<h1 className="text-3xl font-bold text-gray-900">Open Requests</h1> <h1 className="text-xl sm:text-2xl md:text-3xl font-bold text-gray-900">Open Requests</h1>
<p className="text-gray-600">Manage and track active approval requests</p> <p className="text-sm sm:text-base text-gray-600">Manage and track active approval requests</p>
</div> </div>
</div> </div>
</div> </div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-2 sm:gap-3">
<Badge variant="secondary" className="text-lg px-4 py-2 bg-slate-100 text-slate-800 font-semibold"> <Badge variant="secondary" className="text-xs sm:text-sm md:text-base px-2 sm:px-3 md:px-4 py-1 sm:py-1.5 md:py-2 bg-slate-100 text-slate-800 font-semibold">
{loading ? 'Loading…' : `${filteredAndSortedRequests.length} open requests`} {loading ? 'Loading…' : `${filteredAndSortedRequests.length} open`}
<span className="hidden sm:inline ml-1">requests</span>
</Badge> </Badge>
<Button variant="outline" size="sm" className="gap-2"> <Button variant="outline" size="sm" className="gap-1 sm:gap-2 h-8 sm:h-9">
<RefreshCw className="w-4 h-4" /> <RefreshCw className="w-3.5 h-3.5 sm:w-4 sm:h-4" />
Refresh <span className="hidden sm:inline">Refresh</span>
</Button> </Button>
</div> </div>
</div> </div>
{/* Enhanced Filters Section */} {/* Enhanced Filters Section */}
<Card className="shadow-lg border-0"> <Card className="shadow-lg border-0">
<CardHeader className="pb-4"> <CardHeader className="pb-3 sm:pb-4 px-3 sm:px-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-3"> <div className="flex items-center gap-2 sm:gap-3">
<div className="p-2 bg-blue-100 rounded-lg"> <div className="p-1.5 sm:p-2 bg-blue-100 rounded-lg">
<Filter className="h-5 w-5 text-blue-600" /> <Filter className="h-4 w-4 sm:h-5 sm:w-5 text-blue-600" />
</div> </div>
<div> <div>
<CardTitle className="text-lg">Filters & Search</CardTitle> <CardTitle className="text-base sm:text-lg">Filters & Search</CardTitle>
<CardDescription> <CardDescription className="text-xs sm:text-sm">
{activeFiltersCount > 0 && ( {activeFiltersCount > 0 && (
<span className="text-blue-600 font-medium"> <span className="text-blue-600 font-medium">
{activeFiltersCount} filter{activeFiltersCount > 1 ? 's' : ''} active {activeFiltersCount} filter{activeFiltersCount > 1 ? 's' : ''} active
@ -288,45 +289,45 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
</CardDescription> </CardDescription>
</div> </div>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-1 sm:gap-2">
{activeFiltersCount > 0 && ( {activeFiltersCount > 0 && (
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={clearFilters} onClick={clearFilters}
className="text-red-600 hover:bg-red-50 gap-1" className="text-red-600 hover:bg-red-50 gap-1 h-8 sm:h-9 px-2 sm:px-3"
> >
<X className="w-3 h-3" /> <X className="w-3 h-3 sm:w-3.5 sm:h-3.5" />
Clear <span className="text-xs sm:text-sm">Clear</span>
</Button> </Button>
)} )}
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={() => setShowAdvancedFilters(!showAdvancedFilters)} onClick={() => setShowAdvancedFilters(!showAdvancedFilters)}
className="gap-2" className="gap-1 sm:gap-2 h-8 sm:h-9 px-2 sm:px-3"
> >
<Settings2 className="w-4 h-4" /> <Settings2 className="w-3.5 h-3.5 sm:w-4 sm:h-4" />
{showAdvancedFilters ? 'Basic' : 'Advanced'} <span className="text-xs sm:text-sm hidden md:inline">{showAdvancedFilters ? 'Basic' : 'Advanced'}</span>
</Button> </Button>
</div> </div>
</div> </div>
</CardHeader> </CardHeader>
<CardContent className="space-y-4"> <CardContent className="space-y-3 sm:space-y-4 px-3 sm:px-6">
{/* Primary filters */} {/* Primary filters */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3 sm:gap-4">
<div className="relative"> <div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" /> <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-3.5 h-3.5 sm:w-4 sm:h-4" />
<Input <Input
placeholder="Search requests, IDs, or initiators..." placeholder="Search requests, IDs..."
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
className="pl-10 h-11 bg-gray-50 border-gray-200 focus:bg-white transition-colors" className="pl-9 sm:pl-10 h-9 sm:h-10 md:h-11 text-sm sm:text-base bg-gray-50 border-gray-200 focus:bg-white transition-colors"
/> />
</div> </div>
<Select value={priorityFilter} onValueChange={setPriorityFilter}> <Select value={priorityFilter} onValueChange={setPriorityFilter}>
<SelectTrigger className="h-11 bg-gray-50 border-gray-200 focus:bg-white"> <SelectTrigger className="h-9 sm:h-10 md:h-11 text-sm sm:text-base bg-gray-50 border-gray-200 focus:bg-white">
<SelectValue placeholder="All Priorities" /> <SelectValue placeholder="All Priorities" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -347,7 +348,7 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
</Select> </Select>
<Select value={statusFilter} onValueChange={setStatusFilter}> <Select value={statusFilter} onValueChange={setStatusFilter}>
<SelectTrigger className="h-11 bg-gray-50 border-gray-200 focus:bg-white"> <SelectTrigger className="h-9 sm:h-10 md:h-11 text-sm sm:text-base bg-gray-50 border-gray-200 focus:bg-white">
<SelectValue placeholder="All Statuses" /> <SelectValue placeholder="All Statuses" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -359,7 +360,7 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
<div className="flex gap-2"> <div className="flex gap-2">
<Select value={sortBy} onValueChange={(value: any) => setSortBy(value)}> <Select value={sortBy} onValueChange={(value: any) => setSortBy(value)}>
<SelectTrigger className="h-11 bg-gray-50 border-gray-200 focus:bg-white"> <SelectTrigger className="h-9 sm:h-10 md:h-11 text-sm sm:text-base bg-gray-50 border-gray-200 focus:bg-white">
<SelectValue placeholder="Sort by" /> <SelectValue placeholder="Sort by" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -374,9 +375,9 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
variant="outline" variant="outline"
size="sm" size="sm"
onClick={() => setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc')} onClick={() => setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc')}
className="px-3 h-11" className="px-2 sm:px-3 h-9 sm:h-10 md:h-11"
> >
{sortOrder === 'asc' ? <SortAsc className="w-4 h-4" /> : <SortDesc className="w-4 h-4" />} {sortOrder === 'asc' ? <SortAsc className="w-3.5 h-3.5 sm:w-4 sm:h-4" /> : <SortDesc className="w-3.5 h-3.5 sm:w-4 sm:h-4" />}
</Button> </Button>
</div> </div>
</div> </div>
@ -396,69 +397,69 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
className="group hover:shadow-xl transition-all duration-300 cursor-pointer border-0 shadow-md hover:scale-[1.01]" className="group hover:shadow-xl transition-all duration-300 cursor-pointer border-0 shadow-md hover:scale-[1.01]"
onClick={() => onViewRequest?.(request.id, request.title)} onClick={() => onViewRequest?.(request.id, request.title)}
> >
<CardContent className="p-6"> <CardContent className="p-3 sm:p-6">
<div className="flex items-start gap-6"> <div className="flex flex-col sm:flex-row items-start gap-3 sm:gap-6">
{/* Priority Indicator */} {/* Priority Indicator */}
<div className="flex flex-col items-center gap-2 pt-1"> <div className="flex sm:flex-col items-center gap-2 pt-1 w-full sm:w-auto">
<div className={`p-3 rounded-xl ${priorityConfig.color} border`}> <div className={`p-2 sm:p-3 rounded-xl ${priorityConfig.color} border flex-shrink-0`}>
<priorityConfig.icon className={`w-5 h-5 ${priorityConfig.iconColor}`} /> <priorityConfig.icon className={`w-4 h-4 sm:w-5 sm:h-5 ${priorityConfig.iconColor}`} />
</div> </div>
<Badge <Badge
variant="outline" variant="outline"
className={`text-xs font-medium ${priorityConfig.color} capitalize`} className={`text-xs font-medium ${priorityConfig.color} capitalize flex-shrink-0`}
> >
{request.priority} {request.priority}
</Badge> </Badge>
</div> </div>
{/* Main Content */} {/* Main Content */}
<div className="flex-1 min-w-0 space-y-4"> <div className="flex-1 min-w-0 space-y-3 sm:space-y-4 w-full">
{/* Header */} {/* Header */}
<div className="flex items-start justify-between gap-4"> <div className="flex items-start justify-between gap-2 sm:gap-4">
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="flex items-center gap-3 mb-2"> <div className="flex flex-wrap items-center gap-1.5 sm:gap-3 mb-2">
<h3 className="text-lg font-semibold text-gray-900 group-hover:text-blue-600 transition-colors"> <h3 className="text-sm sm:text-lg font-semibold text-gray-900 group-hover:text-blue-600 transition-colors">
{(request as any).displayId || request.id} {(request as any).displayId || request.id}
</h3> </h3>
<Badge <Badge
variant="outline" variant="outline"
className={`${statusConfig.color} border font-medium`} className={`${statusConfig.color} border font-medium text-xs shrink-0`}
> >
<statusConfig.icon className="w-3 h-3 mr-1" /> <statusConfig.icon className="w-3 h-3 mr-1" />
{request.status} <span className="capitalize">{request.status}</span>
</Badge> </Badge>
{request.department && ( {request.department && (
<Badge variant="secondary" className="bg-gray-100 text-gray-700"> <Badge variant="secondary" className="bg-gray-100 text-gray-700 text-xs hidden sm:inline-flex shrink-0">
{request.department} {request.department}
</Badge> </Badge>
)} )}
</div> </div>
<h4 className="text-xl font-bold text-gray-900 mb-2 line-clamp-1"> <h4 className="text-base sm:text-xl font-bold text-gray-900 mb-2 line-clamp-2">
{request.title} {request.title}
</h4> </h4>
<p className="text-gray-600 line-clamp-2 leading-relaxed"> <p className="text-xs sm:text-sm text-gray-600 line-clamp-2 leading-relaxed">
{request.description} {request.description}
</p> </p>
</div> </div>
<div className="flex flex-col items-end gap-2"> <div className="flex flex-col items-end gap-2 flex-shrink-0">
<ArrowRight className="w-5 h-5 text-gray-400 group-hover:text-blue-600 transition-colors" /> <ArrowRight className="w-4 h-4 sm:w-5 sm:h-5 text-gray-400 group-hover:text-blue-600 transition-colors" />
</div> </div>
</div> </div>
{/* SLA Progress */} {/* SLA Progress */}
<div className="bg-gray-50 rounded-lg p-4"> <div className="bg-gray-50 rounded-lg p-3 sm:p-4">
<div className="flex items-center justify-between mb-2"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 mb-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Clock className="w-4 h-4 text-gray-500" /> <Clock className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-gray-500 flex-shrink-0" />
<span className="text-sm font-medium text-gray-700">SLA Progress</span> <span className="text-xs sm:text-sm font-medium text-gray-700">SLA Progress</span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2 flex-wrap">
<span className={`text-sm font-semibold ${slaConfig.textColor}`}> <span className={`text-xs sm:text-sm font-semibold ${slaConfig.textColor}`}>
{request.slaRemaining} remaining {request.slaRemaining} remaining
</span> </span>
{slaConfig.urgency === 'critical' && ( {slaConfig.urgency === 'critical' && (
<Badge variant="destructive" className="animate-pulse text-xs"> <Badge variant="destructive" className="animate-pulse text-xs shrink-0">
URGENT URGENT
</Badge> </Badge>
)} )}
@ -466,59 +467,59 @@ export function OpenRequests({ onViewRequest }: OpenRequestsProps) {
</div> </div>
<Progress <Progress
value={request.slaProgress} value={request.slaProgress}
className="h-3 bg-gray-200" className="h-2 sm:h-3 bg-gray-200"
/> />
</div> </div>
{/* Status Info */} {/* Status Info */}
<div className="flex items-center gap-4 p-4 bg-gray-50 rounded-lg"> <div className="flex items-center gap-2 sm:gap-4 p-3 sm:p-4 bg-gray-50 rounded-lg">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2 min-w-0">
<AlertCircle className="w-4 h-4 text-blue-500" /> <AlertCircle className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-blue-500 flex-shrink-0" />
<span className="text-sm text-gray-700 font-medium"> <span className="text-xs sm:text-sm text-gray-700 font-medium truncate">
{request.approvalStep} {request.approvalStep}
</span> </span>
</div> </div>
</div> </div>
{/* Participants & Metadata */} {/* Participants & Metadata */}
<div className="flex items-center justify-between"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 sm:gap-4">
<div className="flex items-center gap-6"> <div className="flex flex-col sm:flex-row sm:items-center gap-3 sm:gap-6 min-w-0">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2 min-w-0">
<Avatar className="h-8 w-8 ring-2 ring-white shadow-sm"> <Avatar className="h-7 w-7 sm:h-8 sm:w-8 ring-2 ring-white shadow-sm flex-shrink-0">
<AvatarFallback className="bg-slate-700 text-white text-sm font-semibold"> <AvatarFallback className="bg-slate-700 text-white text-xs sm:text-sm font-semibold">
{request.initiator.avatar} {request.initiator.avatar}
</AvatarFallback> </AvatarFallback>
</Avatar> </Avatar>
<div> <div className="min-w-0">
<p className="text-sm font-medium text-gray-900">{request.initiator.name}</p> <p className="text-xs sm:text-sm font-medium text-gray-900 truncate">{request.initiator.name}</p>
<p className="text-xs text-gray-500">Initiator</p> <p className="text-xs text-gray-500">Initiator</p>
</div> </div>
</div> </div>
{request.currentApprover && ( {request.currentApprover && (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2 min-w-0">
<Avatar className="h-8 w-8 ring-2 ring-yellow-200 shadow-sm"> <Avatar className="h-7 w-7 sm:h-8 sm:w-8 ring-2 ring-yellow-200 shadow-sm flex-shrink-0">
<AvatarFallback className="bg-yellow-500 text-white text-sm font-semibold"> <AvatarFallback className="bg-yellow-500 text-white text-xs sm:text-sm font-semibold">
{request.currentApprover.avatar} {request.currentApprover.avatar}
</AvatarFallback> </AvatarFallback>
</Avatar> </Avatar>
<div> <div className="min-w-0">
<p className="text-sm font-medium text-gray-900">{request.currentApprover.name}</p> <p className="text-xs sm:text-sm font-medium text-gray-900 truncate">{request.currentApprover.name}</p>
<p className="text-xs text-gray-500">Current Approver</p> <p className="text-xs text-gray-500">Current Approver</p>
</div> </div>
</div> </div>
)} )}
</div> </div>
<div className="text-right"> <div className="text-left sm:text-right">
<div className="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-4 text-xs text-gray-500"> <div className="flex flex-col gap-1 text-xs text-gray-500">
<span className="flex items-center gap-1"> <span className="flex items-center gap-1">
<Calendar className="w-3 h-3" /> <Calendar className="w-3 h-3 flex-shrink-0" />
Created: {request.createdAt !== '—' ? formatDateShort(request.createdAt) : '—'} <span className="truncate">Created: {request.createdAt !== '—' ? formatDateShort(request.createdAt) : '—'}</span>
</span> </span>
<span className="flex items-center gap-1"> <span className="flex items-center gap-1">
<Clock className="w-3 h-3" /> <Clock className="w-3 h-3 flex-shrink-0" />
Due: {request.dueDate ? formatDateShort(request.dueDate) : 'Not set'} <span className="truncate">Due: {request.dueDate ? formatDateShort(request.dueDate) : 'Not set'}</span>
</span> </span>
</div> </div>
</div> </div>

View File

@ -169,16 +169,16 @@ const getSLAConfig = (progress: number) => {
const getStepIcon = (status: string) => { const getStepIcon = (status: string) => {
switch (status) { switch (status) {
case 'approved': case 'approved':
return <CheckCircle className="w-5 h-5 text-green-600" />; return <CheckCircle className="w-4 h-4 sm:w-5 sm:h-5 text-green-600" />;
case 'rejected': case 'rejected':
return <XCircle className="w-5 h-5 text-red-600" />; return <XCircle className="w-4 h-4 sm:w-5 sm:h-5 text-red-600" />;
case 'pending': case 'pending':
case 'in-review': case 'in-review':
return <Clock className="w-5 h-5 text-blue-600" />; return <Clock className="w-4 h-4 sm:w-5 sm:h-5 text-blue-600" />;
case 'waiting': case 'waiting':
return <Clock className="w-5 h-5 text-gray-400" />; return <Clock className="w-4 h-4 sm:w-5 sm:h-5 text-gray-400" />;
default: default:
return <Clock className="w-5 h-5 text-gray-400" />; return <Clock className="w-4 h-4 sm:w-5 sm:h-5 text-gray-400" />;
} }
}; };
@ -996,63 +996,65 @@ function RequestDetailInner({
return ( return (
<> <>
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gray-50">
<div className="max-w-7xl mx-auto p-6"> <div className="max-w-7xl mx-auto">
{/* Header Section */} {/* Header Section */}
<div className="bg-white rounded-lg shadow-sm border border-gray-300 mb-6"> <div className="bg-white rounded-lg shadow-sm border border-gray-300 mb-4 sm:mb-6">
{/* Top Header */} {/* Top Header */}
<div className="p-6 border-b border-gray-300"> <div className="p-3 sm:p-4 md:p-6 border-b border-gray-300">
<div className="flex items-center justify-between"> <div className="flex items-start sm:items-center justify-between gap-2 sm:gap-4">
<div className="flex items-center gap-4"> <div className="flex items-start sm:items-center gap-2 sm:gap-4 min-w-0 flex-1">
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
onClick={onBack} onClick={onBack}
className="rounded-lg" className="rounded-lg flex-shrink-0 h-8 w-8 sm:h-10 sm:w-10"
> >
<ArrowLeft className="h-5 w-5" /> <ArrowLeft className="h-4 w-4 sm:h-5 sm:w-5" />
</Button> </Button>
<div className="flex items-center gap-3"> <div className="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-3 min-w-0 flex-1">
<div className="w-10 h-10 rounded-full bg-blue-100 flex items-center justify-center"> <div className="w-8 h-8 sm:w-10 sm:h-10 rounded-full bg-blue-100 flex items-center justify-center flex-shrink-0">
<FileText className="w-5 h-5 text-blue-600" /> <FileText className="w-4 h-4 sm:w-5 sm:h-5 text-blue-600" />
</div> </div>
<div className="flex items-center gap-2"> <div className="flex flex-col sm:flex-row sm:items-center gap-2 min-w-0 flex-1">
<h1 className="text-lg font-bold text-gray-900">{request.id || 'N/A'}</h1> <h1 className="text-sm sm:text-base md:text-lg font-bold text-gray-900 truncate">{request.id || 'N/A'}</h1>
<Badge className={`${priorityConfig.color} rounded-full px-3`} variant="outline"> <div className="flex flex-wrap items-center gap-1.5 sm:gap-2">
{priorityConfig.label} <Badge className={`${priorityConfig.color} rounded-full px-2 sm:px-3 text-xs capitalize shrink-0`} variant="outline">
</Badge> {priorityConfig.label}
<Badge className={`${statusConfig.color} rounded-full px-3`} variant="outline"> </Badge>
{statusConfig.label} <Badge className={`${statusConfig.color} rounded-full px-2 sm:px-3 text-xs capitalize shrink-0`} variant="outline">
</Badge> {statusConfig.label}
</Badge>
</div>
</div> </div>
</div> </div>
</div> </div>
<Button variant="outline" size="sm" className="gap-2"> <Button variant="outline" size="sm" className="gap-1 sm:gap-2 flex-shrink-0 h-8 sm:h-9">
<RefreshCw className="w-4 h-4" /> <RefreshCw className="w-3.5 h-3.5 sm:w-4 sm:h-4" />
Refresh <span className="hidden sm:inline">Refresh</span>
</Button> </Button>
</div> </div>
<div className="mt-3 ml-14"> <div className="mt-3 ml-0 sm:ml-14">
<h2 className="text-xl font-semibold text-gray-900">{request.title}</h2> <h2 className="text-base sm:text-lg md:text-xl font-semibold text-gray-900 line-clamp-2">{request.title}</h2>
</div> </div>
</div> </div>
{/* SLA Progress */} {/* SLA Progress */}
<div className={`${slaConfig.bg} px-6 py-4`}> <div className={`${slaConfig.bg} px-3 sm:px-4 md:px-6 py-3 sm:py-4`}>
<div className="flex items-center justify-between mb-2"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 mb-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Clock className={`h-4 w-4 ${slaConfig.textColor}`} /> <Clock className={`h-3.5 w-3.5 sm:h-4 sm:w-4 ${slaConfig.textColor} flex-shrink-0`} />
<span className="text-sm font-medium text-gray-900">SLA Progress</span> <span className="text-xs sm:text-sm font-medium text-gray-900">SLA Progress</span>
</div> </div>
<span className={`text-sm font-semibold ${slaConfig.textColor}`}> <span className={`text-xs sm:text-sm font-semibold ${slaConfig.textColor}`}>
{request.slaRemaining} {request.slaRemaining}
</span> </span>
</div> </div>
<Progress value={request.slaProgress} className="h-2 mb-2" /> <Progress value={request.slaProgress} className="h-2 mb-2" />
<p className="text-xs text-gray-600"> <p className="text-[10px] sm:text-xs text-gray-600">
Due: {request.slaEndDate ? formatDateTime(request.slaEndDate) : 'Not set'} {request.slaProgress}% elapsed Due: {request.slaEndDate ? formatDateTime(request.slaEndDate) : 'Not set'} {request.slaProgress}% elapsed
</p> </p>
</div> </div>
@ -1060,28 +1062,28 @@ function RequestDetailInner({
{/* Tabs */} {/* Tabs */}
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full"> <Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
<TabsList className="grid w-full grid-cols-5 bg-gray-100 h-10 mb-6"> <TabsList className="grid w-full grid-cols-5 bg-gray-100 h-auto sm:h-10 mb-4 sm:mb-6 p-1">
<TabsTrigger value="overview" className="flex items-center gap-2 text-xs sm:text-sm px-2"> <TabsTrigger value="overview" className="flex flex-col sm:flex-row items-center justify-center gap-1 sm:gap-2 text-[10px] sm:text-xs md:text-sm px-1 sm:px-2 py-2 sm:py-0 min-h-[44px] sm:min-h-0">
<ClipboardList className="w-3 h-3 sm:w-4 sm:h-4" /> <ClipboardList className="w-4 h-4 sm:w-3.5 sm:h-3.5 md:w-4 md:h-4 flex-shrink-0" />
Overview <span className="leading-tight">Overview</span>
</TabsTrigger> </TabsTrigger>
<TabsTrigger value="workflow" className="flex items-center gap-2 text-xs sm:text-sm px-2"> <TabsTrigger value="workflow" className="flex flex-col sm:flex-row items-center justify-center gap-1 sm:gap-2 text-[10px] sm:text-xs md:text-sm px-1 sm:px-2 py-2 sm:py-0 min-h-[44px] sm:min-h-0">
<TrendingUp className="w-3 h-3 sm:w-4 sm:h-4" /> <TrendingUp className="w-4 h-4 sm:w-3.5 sm:h-3.5 md:w-4 md:h-4 flex-shrink-0" />
Workflow <span className="leading-tight">Workflow</span>
</TabsTrigger> </TabsTrigger>
<TabsTrigger value="documents" className="flex items-center gap-2 text-xs sm:text-sm px-2"> <TabsTrigger value="documents" className="flex flex-col sm:flex-row items-center justify-center gap-1 sm:gap-2 text-[10px] sm:text-xs md:text-sm px-1 sm:px-2 py-2 sm:py-0 min-h-[44px] sm:min-h-0">
<FileText className="w-3 h-3 sm:w-4 sm:h-4" /> <FileText className="w-4 h-4 sm:w-3.5 sm:h-3.5 md:w-4 md:h-4 flex-shrink-0" />
Documents <span className="leading-tight">Docs</span>
</TabsTrigger> </TabsTrigger>
<TabsTrigger value="activity" className="flex items-center gap-2 text-xs sm:text-sm px-2"> <TabsTrigger value="activity" className="flex flex-col sm:flex-row items-center justify-center gap-1 sm:gap-2 text-[10px] sm:text-xs md:text-sm px-1 sm:px-2 py-2 sm:py-0 min-h-[44px] sm:min-h-0">
<Activity className="w-3 h-3 sm:w-4 sm:h-4" /> <Activity className="w-4 h-4 sm:w-3.5 sm:h-3.5 md:w-4 md:h-4 flex-shrink-0" />
Activity <span className="leading-tight">Activity</span>
</TabsTrigger> </TabsTrigger>
<TabsTrigger value="worknotes" className="flex items-center gap-2 text-xs sm:text-sm px-2 relative"> <TabsTrigger value="worknotes" className="flex flex-col sm:flex-row items-center justify-center gap-1 sm:gap-2 text-[10px] sm:text-xs md:text-sm px-1 sm:px-2 py-2 sm:py-0 relative min-h-[44px] sm:min-h-0">
<MessageSquare className="w-3 h-3 sm:w-4 sm:h-4" /> <MessageSquare className="w-4 h-4 sm:w-3.5 sm:h-3.5 md:w-4 md:h-4 flex-shrink-0" />
Work Notes <span className="leading-tight">Notes</span>
{unreadWorkNotes > 0 && ( {unreadWorkNotes > 0 && (
<Badge className="absolute -top-1 -right-1 h-5 w-5 rounded-full bg-red-500 text-white text-[10px] flex items-center justify-center p-0"> <Badge className="absolute top-1 right-1 sm:-top-1 sm:-right-1 h-4 w-4 sm:h-5 sm:w-5 rounded-full bg-red-500 text-white text-[8px] sm:text-[10px] flex items-center justify-center p-0">
{unreadWorkNotes > 9 ? '9+' : unreadWorkNotes} {unreadWorkNotes > 9 ? '9+' : unreadWorkNotes}
</Badge> </Badge>
)} )}
@ -1095,34 +1097,34 @@ function RequestDetailInner({
{/* Overview Tab */} {/* Overview Tab */}
<TabsContent value="overview" className="mt-0"> <TabsContent value="overview" className="mt-0">
<div className="space-y-6"> <div className="space-y-4 sm:space-y-6">
{/* Request Initiator */} {/* Request Initiator */}
<Card> <Card>
<CardHeader className="pb-4"> <CardHeader className="pb-3 sm:pb-4">
<CardTitle className="flex items-center gap-2 text-base"> <CardTitle className="flex items-center gap-2 text-sm sm:text-base">
<User className="w-5 h-5 text-blue-600" /> <User className="w-4 h-4 sm:w-5 sm:h-5 text-blue-600" />
Request Initiator Request Initiator
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="flex items-start gap-4"> <div className="flex items-start gap-3 sm:gap-4">
<Avatar className="h-12 w-12 ring-2 ring-white shadow-sm"> <Avatar className="h-10 w-10 sm:h-12 sm:w-12 ring-2 ring-white shadow-sm flex-shrink-0">
<AvatarFallback className="bg-gray-700 text-white font-semibold"> <AvatarFallback className="bg-gray-700 text-white font-semibold text-sm">
{request.initiator?.avatar || 'U'} {request.initiator?.avatar || 'U'}
</AvatarFallback> </AvatarFallback>
</Avatar> </Avatar>
<div className="flex-1"> <div className="flex-1 min-w-0">
<h3 className="font-semibold text-gray-900">{request.initiator?.name || 'N/A'}</h3> <h3 className="font-semibold text-gray-900 text-sm sm:text-base truncate">{request.initiator?.name || 'N/A'}</h3>
<p className="text-sm text-gray-600">{request.initiator?.role || 'N/A'}</p> <p className="text-xs sm:text-sm text-gray-600 truncate">{request.initiator?.role || 'N/A'}</p>
<p className="text-sm text-gray-500">{request.initiator?.department || 'N/A'}</p> <p className="text-xs sm:text-sm text-gray-500 truncate">{request.initiator?.department || 'N/A'}</p>
<div className="mt-3 space-y-2"> <div className="mt-2 sm:mt-3 space-y-1.5 sm:space-y-2">
<div className="flex items-center gap-2 text-sm text-gray-600"> <div className="flex items-center gap-2 text-xs sm:text-sm text-gray-600 min-w-0">
<Mail className="w-4 h-4" /> <Mail className="w-3.5 h-3.5 sm:w-4 sm:h-4 flex-shrink-0" />
<span>{request.initiator?.email || 'N/A'}</span> <span className="truncate">{request.initiator?.email || 'N/A'}</span>
</div> </div>
<div className="flex items-center gap-2 text-sm text-gray-600"> <div className="flex items-center gap-2 text-xs sm:text-sm text-gray-600">
<Phone className="w-4 h-4" /> <Phone className="w-3.5 h-3.5 sm:w-4 sm:h-4 flex-shrink-0" />
<span>{request.initiator?.phone || 'N/A'}</span> <span>{request.initiator?.phone || 'N/A'}</span>
</div> </div>
</div> </div>
@ -1133,17 +1135,17 @@ function RequestDetailInner({
{/* Request Details */} {/* Request Details */}
<Card> <Card>
<CardHeader className="pb-4"> <CardHeader className="pb-3 sm:pb-4">
<CardTitle className="flex items-center gap-2 text-base"> <CardTitle className="flex items-center gap-2 text-sm sm:text-base">
<FileText className="w-5 h-5 text-blue-600" /> <FileText className="w-4 h-4 sm:w-5 sm:h-5 text-blue-600" />
Request Details Request Details
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
<CardContent className="space-y-4"> <CardContent className="space-y-3 sm:space-y-4">
<div> <div>
<label className="text-sm font-medium text-gray-700 block mb-2">Description</label> <label className="text-xs sm:text-sm font-medium text-gray-700 block mb-2">Description</label>
<div className="bg-gray-50 rounded-lg p-4 border border-gray-300"> <div className="bg-gray-50 rounded-lg p-3 sm:p-4 border border-gray-300">
<p className="text-sm text-gray-700 whitespace-pre-line leading-relaxed break-words"> <p className="text-xs sm:text-sm text-gray-700 whitespace-pre-line leading-relaxed break-words">
{request.description} {request.description}
</p> </p>
</div> </div>
@ -1242,18 +1244,18 @@ function RequestDetailInner({
<TabsContent value="workflow" className="mt-0"> <TabsContent value="workflow" className="mt-0">
<Card> <Card>
<CardHeader> <CardHeader>
<div className="flex items-center justify-between"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 sm:gap-4">
<div> <div>
<CardTitle className="flex items-center gap-2"> <CardTitle className="flex items-center gap-2 text-sm sm:text-base">
<TrendingUp className="w-5 h-5 text-blue-600" /> <TrendingUp className="w-4 h-4 sm:w-5 sm:h-5 text-blue-600" />
Approval Workflow Approval Workflow
</CardTitle> </CardTitle>
<CardDescription className="mt-2"> <CardDescription className="mt-1 sm:mt-2 text-xs sm:text-sm">
Track the approval progress through each step Track the approval progress through each step
</CardDescription> </CardDescription>
</div> </div>
{request.totalSteps && ( {request.totalSteps && (
<Badge variant="outline" className="font-medium"> <Badge variant="outline" className="font-medium text-xs sm:text-sm shrink-0">
Step {request.currentStep} of {request.totalSteps} Step {request.currentStep} of {request.totalSteps}
</Badge> </Badge>
)} )}
@ -1261,7 +1263,7 @@ function RequestDetailInner({
</CardHeader> </CardHeader>
<CardContent> <CardContent>
{request.approvalFlow && request.approvalFlow.length > 0 ? ( {request.approvalFlow && request.approvalFlow.length > 0 ? (
<div className="space-y-4"> <div className="space-y-3 sm:space-y-4">
{request.approvalFlow.map((step: any, index: number) => { {request.approvalFlow.map((step: any, index: number) => {
const isActive = step.status === 'pending' || step.status === 'in-review'; const isActive = step.status === 'pending' || step.status === 'in-review';
const isCompleted = step.status === 'approved'; const isCompleted = step.status === 'approved';
@ -1271,7 +1273,7 @@ function RequestDetailInner({
return ( return (
<div <div
key={index} key={index}
className={`relative p-5 rounded-lg border-2 transition-all ${ className={`relative p-3 sm:p-4 md:p-5 rounded-lg border-2 transition-all ${
isActive isActive
? 'border-blue-500 bg-blue-50 shadow-md' ? 'border-blue-500 bg-blue-50 shadow-md'
: isCompleted : isCompleted
@ -1283,8 +1285,8 @@ function RequestDetailInner({
: 'border-gray-200 bg-white' : 'border-gray-200 bg-white'
}`} }`}
> >
<div className="flex items-start gap-4"> <div className="flex items-start gap-2 sm:gap-3 md:gap-4">
<div className={`p-3 rounded-xl ${ <div className={`p-2 sm:p-2.5 md:p-3 rounded-xl flex-shrink-0 ${
isActive ? 'bg-blue-100' : isActive ? 'bg-blue-100' :
isCompleted ? 'bg-green-100' : isCompleted ? 'bg-green-100' :
isRejected ? 'bg-red-100' : isRejected ? 'bg-red-100' :
@ -1295,25 +1297,25 @@ function RequestDetailInner({
</div> </div>
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="flex items-start justify-between gap-4 mb-2"> <div className="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-2 sm:gap-4 mb-2">
<div> <div className="min-w-0 flex-1">
<div className="flex items-center gap-2 mb-1"> <div className="flex flex-wrap items-center gap-1.5 sm:gap-2 mb-1">
<h4 className="font-semibold text-gray-900"> <h4 className="font-semibold text-gray-900 text-sm sm:text-base">
{step.step ? `Step ${step.step}: ` : ''}{step.role} {step.step ? `Step ${step.step}: ` : ''}{step.role}
</h4> </h4>
<Badge variant="outline" className={ <Badge variant="outline" className={`text-xs shrink-0 ${
isActive ? 'bg-blue-100 text-blue-800 border-blue-200' : isActive ? 'bg-blue-100 text-blue-800 border-blue-200' :
isCompleted ? 'bg-green-100 text-green-800 border-green-200' : isCompleted ? 'bg-green-100 text-green-800 border-green-200' :
isRejected ? 'bg-red-100 text-red-800 border-red-200' : isRejected ? 'bg-red-100 text-red-800 border-red-200' :
isWaiting ? 'bg-gray-200 text-gray-600 border-gray-300' : isWaiting ? 'bg-gray-200 text-gray-600 border-gray-300' :
'bg-gray-100 text-gray-800 border-gray-200' 'bg-gray-100 text-gray-800 border-gray-200'
}> }`}>
{step.status} {step.status}
</Badge> </Badge>
</div> </div>
<p className="text-sm text-gray-600">{step.approver}</p> <p className="text-xs sm:text-sm text-gray-600 truncate">{step.approver}</p>
</div> </div>
<div className="text-right"> <div className="text-left sm:text-right flex-shrink-0">
{step.tatHours && ( {step.tatHours && (
<p className="text-xs text-gray-500">TAT: {step.tatHours}h</p> <p className="text-xs text-gray-500">TAT: {step.tatHours}h</p>
)} )}
@ -1321,24 +1323,24 @@ function RequestDetailInner({
<p className="text-xs text-gray-600 font-medium">Elapsed: {step.elapsedHours}h</p> <p className="text-xs text-gray-600 font-medium">Elapsed: {step.elapsedHours}h</p>
)} )}
{step.actualHours !== undefined && ( {step.actualHours !== undefined && (
<p className="text-xs text-gray-600 font-medium">Completed in: {step.actualHours.toFixed(2)}h</p> <p className="text-xs text-gray-600 font-medium">Done: {step.actualHours.toFixed(2)}h</p>
)} )}
</div> </div>
</div> </div>
{step.comment && ( {step.comment && (
<div className="mt-3 p-3 bg-white rounded-lg border border-gray-300"> <div className="mt-2 sm:mt-3 p-2 sm:p-3 bg-white rounded-lg border border-gray-300">
<p className="text-sm text-gray-700 whitespace-pre-line leading-relaxed break-words">{step.comment}</p> <p className="text-xs sm:text-sm text-gray-700 whitespace-pre-line leading-relaxed break-words">{step.comment}</p>
</div> </div>
)} )}
{/* TAT Alerts/Reminders */} {/* TAT Alerts/Reminders */}
{step.tatAlerts && step.tatAlerts.length > 0 && ( {step.tatAlerts && step.tatAlerts.length > 0 && (
<div className="mt-3 space-y-2"> <div className="mt-2 sm:mt-3 space-y-2">
{step.tatAlerts.map((alert: any, alertIndex: number) => ( {step.tatAlerts.map((alert: any, alertIndex: number) => (
<div <div
key={alertIndex} key={alertIndex}
className={`p-3 rounded-lg border ${ className={`p-2 sm:p-3 rounded-lg border ${
alert.isBreached alert.isBreached
? 'bg-red-50 border-red-200' ? 'bg-red-50 border-red-200'
: (alert.thresholdPercentage || 0) === 75 : (alert.thresholdPercentage || 0) === 75
@ -1347,34 +1349,34 @@ function RequestDetailInner({
}`} }`}
> >
<div className="flex items-start gap-2"> <div className="flex items-start gap-2">
<div className="text-lg"> <div className="text-base sm:text-lg flex-shrink-0">
{(alert.thresholdPercentage || 0) === 50 && '⏳'} {(alert.thresholdPercentage || 0) === 50 && '⏳'}
{(alert.thresholdPercentage || 0) === 75 && '⚠️'} {(alert.thresholdPercentage || 0) === 75 && '⚠️'}
{(alert.thresholdPercentage || 0) === 100 && '⏰'} {(alert.thresholdPercentage || 0) === 100 && '⏰'}
</div> </div>
<div className="flex-1"> <div className="flex-1 min-w-0">
<div className="flex items-center justify-between mb-1"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-1 sm:gap-2 mb-1">
<p className="text-sm font-semibold text-gray-900"> <p className="text-xs sm:text-sm font-semibold text-gray-900">
Reminder {alertIndex + 1} - {alert.thresholdPercentage || 0}% TAT Threshold Reminder {alertIndex + 1} - {alert.thresholdPercentage || 0}% TAT
</p> </p>
<Badge <Badge
variant="outline" variant="outline"
className={ className={`text-[10px] sm:text-xs shrink-0 ${
alert.isBreached alert.isBreached
? 'bg-red-100 text-red-800 border-red-300' ? 'bg-red-100 text-red-800 border-red-300'
: 'bg-amber-100 text-amber-800 border-amber-300' : 'bg-amber-100 text-amber-800 border-amber-300'
} }`}
> >
{alert.isBreached ? 'BREACHED' : 'WARNING'} {alert.isBreached ? 'BREACHED' : 'WARNING'}
</Badge> </Badge>
</div> </div>
<p className="text-sm text-gray-700 mt-1"> <p className="text-[10px] sm:text-xs md:text-sm text-gray-700 mt-1">
{alert.thresholdPercentage || 0}% of SLA breach reminder have been sent {alert.thresholdPercentage || 0}% of SLA breach reminder have been sent
</p> </p>
{/* Time Tracking Details */} {/* Time Tracking Details */}
<div className="mt-2 grid grid-cols-2 gap-2 text-xs"> <div className="mt-2 grid grid-cols-2 gap-1.5 sm:gap-2 text-[10px] sm:text-xs">
<div className="bg-white/50 rounded px-2 py-1"> <div className="bg-white/50 rounded px-2 py-1">
<span className="text-gray-500">Allocated:</span> <span className="text-gray-500">Allocated:</span>
<span className="ml-1 font-medium text-gray-900"> <span className="ml-1 font-medium text-gray-900">
@ -1414,17 +1416,17 @@ function RequestDetailInner({
</div> </div>
<div className="mt-2 pt-2 border-t border-gray-200"> <div className="mt-2 pt-2 border-t border-gray-200">
<div className="flex items-center justify-between"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-1">
<p className="text-xs text-gray-500"> <p className="text-[10px] sm:text-xs text-gray-500">
Reminder sent by system automatically Reminder sent by system automatically
</p> </p>
{(alert.metadata?.testMode || alert.metadata?.tatTestMode) && ( {(alert.metadata?.testMode || alert.metadata?.tatTestMode) && (
<Badge variant="outline" className="bg-purple-50 text-purple-700 border-purple-300 text-[10px] px-1.5 py-0"> <Badge variant="outline" className="bg-purple-50 text-purple-700 border-purple-300 text-[10px] px-1.5 py-0 shrink-0">
TEST MODE TEST MODE
</Badge> </Badge>
)} )}
</div> </div>
<p className="text-xs text-gray-600 font-medium mt-0.5"> <p className="text-[10px] sm:text-xs text-gray-600 font-medium mt-0.5">
Sent at: {alert.alertSentAt ? formatDateTime(alert.alertSentAt) : 'N/A'} Sent at: {alert.alertSentAt ? formatDateTime(alert.alertSentAt) : 'N/A'}
</p> </p>
{(alert.metadata?.testMode || alert.metadata?.tatTestMode) && ( {(alert.metadata?.testMode || alert.metadata?.tatTestMode) && (
@ -1448,11 +1450,11 @@ function RequestDetailInner({
{/* Skip Approver Button - Only show for pending/in-review levels */} {/* Skip Approver Button - Only show for pending/in-review levels */}
{(isActive || step.status === 'pending') && !isCompleted && !isRejected && step.levelId && ( {(isActive || step.status === 'pending') && !isCompleted && !isRejected && step.levelId && (
<div className="mt-3 pt-3 border-t border-gray-200"> <div className="mt-2 sm:mt-3 pt-2 sm:pt-3 border-t border-gray-200">
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
className="w-full border-orange-300 text-orange-700 hover:bg-orange-50" className="w-full border-orange-300 text-orange-700 hover:bg-orange-50 h-9 sm:h-10 text-xs sm:text-sm"
onClick={() => { onClick={() => {
if (!step.levelId) { if (!step.levelId) {
alert('Level ID not available'); alert('Level ID not available');
@ -1466,10 +1468,10 @@ function RequestDetailInner({
} }
}} }}
> >
<AlertCircle className="w-4 h-4 mr-2" /> <AlertCircle className="w-3.5 h-3.5 sm:w-4 sm:h-4 mr-2" />
Skip This Approver Skip This Approver
</Button> </Button>
<p className="text-xs text-gray-500 mt-1 text-center"> <p className="text-[10px] sm:text-xs text-gray-500 mt-1 text-center">
Skip if approver is unavailable and move to next level Skip if approver is unavailable and move to next level
</p> </p>
</div> </div>
@ -1489,25 +1491,29 @@ function RequestDetailInner({
{/* Documents Tab */} {/* Documents Tab */}
<TabsContent value="documents" className="mt-0"> <TabsContent value="documents" className="mt-0">
<div className="space-y-6"> <div className="space-y-4 sm:space-y-6">
{/* Section 1: Request Documents */} {/* Section 1: Request Documents */}
<Card> <Card>
<CardHeader> <CardHeader>
<div className="flex items-center justify-between"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 sm:gap-4">
<CardTitle className="flex items-center gap-2"> <div>
<FileText className="w-5 h-5 text-blue-600" /> <CardTitle className="flex items-center gap-2 text-sm sm:text-base">
Request Documents <FileText className="w-4 h-4 sm:w-5 sm:h-5 text-blue-600" />
</CardTitle> Request Documents
</CardTitle>
<CardDescription className="text-xs sm:text-sm mt-1">Documents attached while creating the request</CardDescription>
</div>
<Button <Button
size="sm" size="sm"
onClick={triggerFileInput} onClick={triggerFileInput}
disabled={uploadingDocument} disabled={uploadingDocument}
className="gap-1 sm:gap-2 h-8 sm:h-9 text-xs sm:text-sm shrink-0"
> >
<Upload className="w-4 h-4 mr-2" /> <Upload className="w-3.5 h-3.5 sm:w-4 sm:h-4" />
{uploadingDocument ? 'Uploading...' : 'Upload Document'} {uploadingDocument ? 'Uploading...' : 'Upload'}
<span className="hidden sm:inline">Document</span>
</Button> </Button>
</div> </div>
<CardDescription>Documents attached while creating the request</CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
{request.documents && request.documents.length > 0 ? ( {request.documents && request.documents.length > 0 ? (
@ -1585,11 +1591,11 @@ function RequestDetailInner({
{/* Section 2: Work Note Attachments */} {/* Section 2: Work Note Attachments */}
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle className="flex items-center gap-2"> <CardTitle className="flex items-center gap-2 text-sm sm:text-base">
<MessageSquare className="w-5 h-5 text-purple-600" /> <MessageSquare className="w-4 h-4 sm:w-5 sm:h-5 text-purple-600" />
Work Note Attachments Work Note Attachments
</CardTitle> </CardTitle>
<CardDescription>Files shared in work notes discussions</CardDescription> <CardDescription className="text-xs sm:text-sm">Files shared in work notes discussions</CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
{workNoteAttachments && workNoteAttachments.length > 0 ? ( {workNoteAttachments && workNoteAttachments.length > 0 ? (
@ -1682,16 +1688,16 @@ function RequestDetailInner({
<TabsContent value="activity" className="mt-0"> <TabsContent value="activity" className="mt-0">
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle className="flex items-center gap-2"> <CardTitle className="flex items-center gap-2 text-sm sm:text-base">
<Activity className="w-5 h-5 text-orange-600" /> <Activity className="w-4 h-4 sm:w-5 sm:h-5 text-orange-600" />
Activity Timeline Activity Timeline
</CardTitle> </CardTitle>
<CardDescription> <CardDescription className="text-xs sm:text-sm">
Complete audit trail of all request activities Complete audit trail of all request activities
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="space-y-6"> <div className="space-y-4 sm:space-y-6">
{request.auditTrail && request.auditTrail.length > 0 ? request.auditTrail.map((entry: any, index: number) => ( {request.auditTrail && request.auditTrail.length > 0 ? request.auditTrail.map((entry: any, index: number) => (
<div key={index} className="flex items-start gap-4"> <div key={index} className="flex items-start gap-4">
{/* Icon */} {/* Icon */}
@ -1746,50 +1752,50 @@ function RequestDetailInner({
{/* Right Column - Quick Actions Sidebar (1/3 width) - Hidden for Work Notes Tab */} {/* Right Column - Quick Actions Sidebar (1/3 width) - Hidden for Work Notes Tab */}
{activeTab !== 'worknotes' && ( {activeTab !== 'worknotes' && (
<div className="space-y-6"> <div className="space-y-4 sm:space-y-6">
{/* Quick Actions */} {/* Quick Actions */}
<Card> <Card>
<CardHeader className="pb-2"> <CardHeader className="pb-2">
<CardTitle className="text-base">Quick Actions</CardTitle> <CardTitle className="text-sm sm:text-base">Quick Actions</CardTitle>
</CardHeader> </CardHeader>
<CardContent className="space-y-2"> <CardContent className="space-y-2">
{!isSpectator && ( {!isSpectator && (
<Button <Button
variant="outline" variant="outline"
className="w-full justify-start gap-2 bg-white text-gray-700 border-gray-300 hover:bg-gray-50 hover:text-gray-900" className="w-full justify-start gap-2 bg-white text-gray-700 border-gray-300 hover:bg-gray-50 hover:text-gray-900 h-9 sm:h-10 text-xs sm:text-sm"
onClick={() => setShowAddApproverModal(true)} onClick={() => setShowAddApproverModal(true)}
> >
<UserPlus className="w-4 h-4" /> <UserPlus className="w-3.5 h-3.5 sm:w-4 sm:h-4" />
Add Approver Add Approver
</Button> </Button>
)} )}
{!isSpectator && ( {!isSpectator && (
<Button <Button
variant="outline" variant="outline"
className="w-full justify-start gap-2 bg-white text-gray-700 border-gray-300 hover:bg-gray-50 hover:text-gray-900" className="w-full justify-start gap-2 bg-white text-gray-700 border-gray-300 hover:bg-gray-50 hover:text-gray-900 h-9 sm:h-10 text-xs sm:text-sm"
onClick={() => setShowAddSpectatorModal(true)} onClick={() => setShowAddSpectatorModal(true)}
> >
<Eye className="w-4 h-4" /> <Eye className="w-3.5 h-3.5 sm:w-4 sm:h-4" />
Add Spectator Add Spectator
</Button> </Button>
)} )}
<div className="pt-4 space-y-2"> <div className="pt-3 sm:pt-4 space-y-2">
{!isSpectator && currentApprovalLevel && ( {!isSpectator && currentApprovalLevel && (
<> <>
<Button <Button
className="w-full bg-green-600 hover:bg-green-700 text-white" className="w-full bg-green-600 hover:bg-green-700 text-white h-9 sm:h-10 text-xs sm:text-sm"
onClick={() => setShowApproveModal(true)} onClick={() => setShowApproveModal(true)}
> >
<CheckCircle className="w-4 h-4 mr-2" /> <CheckCircle className="w-3.5 h-3.5 sm:w-4 sm:h-4 mr-2" />
Approve Request Approve Request
</Button> </Button>
<Button <Button
variant="destructive" variant="destructive"
className="w-full" className="w-full h-9 sm:h-10 text-xs sm:text-sm"
onClick={() => setShowRejectModal(true)} onClick={() => setShowRejectModal(true)}
> >
<XCircle className="w-4 h-4 mr-2" /> <XCircle className="w-3.5 h-3.5 sm:w-4 sm:h-4 mr-2" />
Reject Request Reject Request
</Button> </Button>
</> </>
@ -1802,7 +1808,7 @@ function RequestDetailInner({
{request.spectators && request.spectators.length > 0 && ( {request.spectators && request.spectators.length > 0 && (
<Card> <Card>
<CardHeader className="pb-2"> <CardHeader className="pb-2">
<CardTitle className="text-base">Spectators</CardTitle> <CardTitle className="text-sm sm:text-base">Spectators</CardTitle>
</CardHeader> </CardHeader>
<CardContent className="space-y-3"> <CardContent className="space-y-3">
{request.spectators.map((spectator: any, index: number) => ( {request.spectators.map((spectator: any, index: number) => (