Access Denied implementation
This commit is contained in:
parent
0347ee1342
commit
b22539bf68
|
|
@ -15,7 +15,10 @@ function App() {
|
|||
const [allRules, setAllRules] = useState([]);
|
||||
const [showAllRules, setShowAllRules] = useState(false);
|
||||
const [toast, setToast] = useState(null);
|
||||
const [isAuthenticating, setIsAuthenticating] = useState(false);
|
||||
const [isAuthenticating, setIsAuthenticating] = useState(() => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
return !!(params.get('email') && params.get('expires') && params.get('signature'));
|
||||
});
|
||||
const [singleEmailMode, setSingleEmailMode] = useState(false);
|
||||
|
||||
const showToast = (message, type = 'success') => {
|
||||
|
|
@ -184,106 +187,34 @@ function App() {
|
|||
<Header />
|
||||
|
||||
<main className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
{/* Single Email Mode Header */}
|
||||
{singleEmailMode && email && (
|
||||
<div className="card mb-8 bg-gradient-to-r from-primary-50 to-primary-100 border-primary-200">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="bg-primary-600 text-white rounded-full w-12 h-12 flex items-center justify-center">
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h2 className="text-xl font-bold text-gray-900">Your Email Configuration</h2>
|
||||
<p className="text-sm text-primary-700 font-medium mt-1">{email}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Authenticating State */}
|
||||
{isAuthenticating && (
|
||||
<div className="text-center py-16">
|
||||
<FiRefreshCw className="w-16 h-16 text-primary-600 mx-auto mb-4 animate-spin" />
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-2">
|
||||
Authenticating from Roundcube...
|
||||
</h3>
|
||||
<p className="text-gray-600 max-w-md mx-auto">
|
||||
Please wait while we verify your session
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Email Search Section - Only show if NOT in single email mode */}
|
||||
{!singleEmailMode && (
|
||||
<>
|
||||
<div className="card mb-8">
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900">Manage Email Rules</h2>
|
||||
<p className="text-sm text-gray-600 mt-1">
|
||||
Search for an email address to configure auto-replies and forwarding
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowAllRules(!showAllRules)}
|
||||
className="btn-secondary flex items-center gap-2"
|
||||
>
|
||||
<FiList className="w-4 h-4" />
|
||||
All Rules ({allRules.length})
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2">
|
||||
<div className="flex-1">
|
||||
<input
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value);
|
||||
setEmailError('');
|
||||
}}
|
||||
onKeyPress={handleKeyPress}
|
||||
placeholder="Enter email address (e.g., user@example.com)"
|
||||
className={`input-field ${emailError ? 'border-red-500 focus:ring-red-500' : ''}`}
|
||||
/>
|
||||
{emailError && (
|
||||
<p className="mt-1 text-sm text-red-600">{emailError}</p>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
onClick={handleSearch}
|
||||
disabled={isLoading}
|
||||
className="btn-primary flex items-center gap-2 whitespace-nowrap"
|
||||
>
|
||||
{isLoading ? (
|
||||
<>
|
||||
<FiRefreshCw className="w-4 h-4 animate-spin" />
|
||||
Loading...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<FiSearch className="w-4 h-4" />
|
||||
Search
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
{/* Access Denied / Invalid State */}
|
||||
{!isAuthenticating && !currentRule && (
|
||||
<div className="text-center py-16">
|
||||
<div className="bg-red-100 rounded-full w-20 h-20 flex items-center justify-center mx-auto mb-6">
|
||||
<svg className="w-10 h-10 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{/* All Rules List */}
|
||||
{showAllRules && allRules.length > 0 && (
|
||||
<div className="card mb-8">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">All Configured Rules</h3>
|
||||
<div className="space-y-2 max-h-96 overflow-y-auto">
|
||||
{allRules.map((rule, index) => (
|
||||
<div
|
||||
key={index}
|
||||
onClick={() => handleSelectRule(rule.email_address)}
|
||||
className="flex items-center justify-between p-4 bg-gray-50 hover:bg-gray-100 border border-gray-200 rounded-lg cursor-pointer transition-colors"
|
||||
>
|
||||
<div>
|
||||
<p className="font-medium text-gray-900">{rule.email_address}</p>
|
||||
<div className="flex gap-3 mt-1 text-xs text-gray-600">
|
||||
<span className={rule.ooo_active ? 'text-green-600 font-semibold' : ''}>
|
||||
OOO: {rule.ooo_active ? 'Active' : 'Inactive'}
|
||||
</span>
|
||||
<span>Forwards: {rule.forwards?.length || 0}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
<h3 className="text-2xl font-bold text-gray-900 mb-2">
|
||||
Access Denied
|
||||
</h3>
|
||||
<p className="text-gray-600 max-w-md mx-auto mb-8">
|
||||
This application can only be accessed via the secure link provided in your Roundcube configuration.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Configuration Tabs */}
|
||||
|
|
@ -314,17 +245,15 @@ function App() {
|
|||
<nav className="flex gap-8">
|
||||
<button
|
||||
onClick={() => setActiveTab('ooo')}
|
||||
className={`pb-3 px-1 transition-colors ${
|
||||
activeTab === 'ooo' ? 'tab-active' : 'tab-inactive'
|
||||
}`}
|
||||
className={`pb-3 px-1 transition-colors ${activeTab === 'ooo' ? 'tab-active' : 'tab-inactive'
|
||||
}`}
|
||||
>
|
||||
Out of Office
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('forwarding')}
|
||||
className={`pb-3 px-1 transition-colors ${
|
||||
activeTab === 'forwarding' ? 'tab-active' : 'tab-inactive'
|
||||
}`}
|
||||
className={`pb-3 px-1 transition-colors ${activeTab === 'forwarding' ? 'tab-active' : 'tab-inactive'
|
||||
}`}
|
||||
>
|
||||
Email Forwarding
|
||||
</button>
|
||||
|
|
@ -342,32 +271,6 @@ function App() {
|
|||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Empty State */}
|
||||
{!currentRule && !showAllRules && !isAuthenticating && (
|
||||
<div className="text-center py-16">
|
||||
<FiSearch className="w-16 h-16 text-gray-300 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-2">
|
||||
Search for an email address
|
||||
</h3>
|
||||
<p className="text-gray-600 max-w-md mx-auto">
|
||||
Enter an email address above to view and configure its out-of-office auto-replies and forwarding rules
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Authenticating State */}
|
||||
{isAuthenticating && (
|
||||
<div className="text-center py-16">
|
||||
<FiRefreshCw className="w-16 h-16 text-primary-600 mx-auto mb-4 animate-spin" />
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-2">
|
||||
Authenticating from Roundcube...
|
||||
</h3>
|
||||
<p className="text-gray-600 max-w-md mx-auto">
|
||||
Please wait while we verify your session
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
|
||||
{/* Toast Notifications */}
|
||||
|
|
|
|||
Loading…
Reference in New Issue