wulf-pulse/app/setup/page.tsx
Lorentz Hinrichsen 3c3124d8c9 Restructure: rename to Pulse and move app to root
- Renamed project from PSA-Utils to Pulse
- Moved all app files from autotask-app/ to root
- Updated package.json name to 'pulse'
- Updated Docker container names to pulse-app and pulse-redis
- Updated Docker network name to pulse-network
2025-10-28 23:08:54 -04:00

336 lines
12 KiB
TypeScript

'use client';
import { useState, useEffect } from 'react';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import {
CheckCircle,
XCircle,
AlertCircle,
RefreshCw,
Copy,
ExternalLink,
FileText
} from 'lucide-react';
interface HealthStatus {
status: 'healthy' | 'error';
message: string;
configuration?: {
apiUrl: string;
username: string;
password: string;
integrationCode: string;
};
instructions?: string[];
apiResponse?: {
status: number;
statusText: string;
error: string;
};
possibleIssues?: string[];
}
export default function SetupPage() {
const [healthStatus, setHealthStatus] = useState<HealthStatus | null>(null);
const [loading, setLoading] = useState(false);
const [copied, setCopied] = useState(false);
const checkHealth = async () => {
setLoading(true);
try {
const response = await fetch('/api/health');
const data = await response.json();
setHealthStatus(data);
} catch (error) {
setHealthStatus({
status: 'error',
message: 'Failed to check API health',
instructions: ['Make sure the Next.js server is running']
});
} finally {
setLoading(false);
}
};
useEffect(() => {
checkHealth();
}, []);
const copyEnvTemplate = () => {
const template = `# Autotask API Configuration
AUTOTASK_API_URL=https://webservices1.autotask.net/atservicesrest/v1.0
AUTOTASK_USERNAME=your-api-username@yourdomain.com
AUTOTASK_SECRET=your-api-password
AUTOTASK_API_INTEGRATION_CODE=your-tracking-code`;
navigator.clipboard.writeText(template);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
const getStatusIcon = (status: string) => {
if (status === 'configured') {
return <CheckCircle className="w-4 h-4 text-green-500" />;
}
return <XCircle className="w-4 h-4 text-red-500" />;
};
return (
<div className="min-h-screen bg-gradient-to-b from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-950 p-8">
<div className="max-w-4xl mx-auto space-y-6">
<div className="text-center mb-8">
<h1 className="text-3xl font-bold text-gray-900 dark:text-white mb-2">
Autotask API Setup & Diagnostics
</h1>
<p className="text-gray-600 dark:text-gray-400">
Configure and test your Autotask API connection
</p>
</div>
{/* Health Status Card */}
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle>API Connection Status</CardTitle>
<CardDescription>
Current status of your Autotask API configuration
</CardDescription>
</div>
<Button
onClick={checkHealth}
disabled={loading}
variant="outline"
>
{loading ? (
<RefreshCw className="w-4 h-4 mr-2 animate-spin" />
) : (
<RefreshCw className="w-4 h-4 mr-2" />
)}
Refresh
</Button>
</div>
</CardHeader>
<CardContent>
{healthStatus && (
<div className="space-y-4">
{/* Overall Status */}
<div className="flex items-center gap-3">
{healthStatus.status === 'healthy' ? (
<>
<CheckCircle className="w-6 h-6 text-green-500" />
<span className="text-lg font-semibold text-green-600">
{healthStatus.message}
</span>
</>
) : (
<>
<XCircle className="w-6 h-6 text-red-500" />
<span className="text-lg font-semibold text-red-600">
{healthStatus.message}
</span>
</>
)}
</div>
{/* Configuration Status */}
{healthStatus.configuration && (
<div className="border rounded-lg p-4 space-y-2">
<h3 className="font-semibold mb-2">Configuration Status:</h3>
<div className="grid grid-cols-2 gap-2">
<div className="flex items-center gap-2">
{getStatusIcon(healthStatus.configuration.apiUrl)}
<span>API URL</span>
</div>
<div className="flex items-center gap-2">
{getStatusIcon(healthStatus.configuration.username)}
<span>Username</span>
</div>
<div className="flex items-center gap-2">
{getStatusIcon(healthStatus.configuration.password)}
<span>Password</span>
</div>
<div className="flex items-center gap-2">
{getStatusIcon(healthStatus.configuration.integrationCode)}
<span>Integration Code</span>
</div>
</div>
</div>
)}
{/* API Response Error */}
{healthStatus.apiResponse && (
<div className="border border-red-200 bg-red-50 dark:bg-red-900/20 rounded-lg p-4">
<h3 className="font-semibold text-red-700 dark:text-red-400 mb-2">
API Response Error:
</h3>
<div className="space-y-1 text-sm">
<p>Status: {healthStatus.apiResponse.status} - {healthStatus.apiResponse.statusText}</p>
{healthStatus.apiResponse.error && (
<p className="text-xs font-mono bg-red-100 dark:bg-red-900/30 p-2 rounded mt-2">
{healthStatus.apiResponse.error}
</p>
)}
</div>
</div>
)}
{/* Possible Issues */}
{healthStatus.possibleIssues && (
<div className="border border-yellow-200 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg p-4">
<h3 className="font-semibold text-yellow-700 dark:text-yellow-400 mb-2 flex items-center gap-2">
<AlertCircle className="w-4 h-4" />
Possible Issues:
</h3>
<ul className="list-disc list-inside space-y-1 text-sm">
{healthStatus.possibleIssues.map((issue, index) => (
<li key={index}>{issue}</li>
))}
</ul>
</div>
)}
{/* Setup Instructions */}
{healthStatus.instructions && (
<div className="border border-blue-200 bg-blue-50 dark:bg-blue-900/20 rounded-lg p-4">
<h3 className="font-semibold text-blue-700 dark:text-blue-400 mb-2">
Setup Instructions:
</h3>
<ol className="space-y-1 text-sm">
{healthStatus.instructions.map((instruction, index) => (
<li key={index} className={instruction.startsWith(' ') ? 'ml-4 font-mono' : ''}>
{instruction}
</li>
))}
</ol>
</div>
)}
</div>
)}
</CardContent>
</Card>
{/* Environment Template Card */}
<Card>
<CardHeader>
<CardTitle>Environment Variables Template</CardTitle>
<CardDescription>
Copy this template to create your .env.local file
</CardDescription>
</CardHeader>
<CardContent>
<div className="relative">
<pre className="bg-gray-900 text-gray-100 p-4 rounded-lg overflow-x-auto text-sm">
<code>{`# Autotask API Configuration
AUTOTASK_API_URL=https://webservices1.autotask.net/atservicesrest/v1.0
AUTOTASK_USERNAME=your-api-username@yourdomain.com
AUTOTASK_SECRET=your-api-password
AUTOTASK_API_INTEGRATION_CODE=your-tracking-code`}</code>
</pre>
<Button
onClick={copyEnvTemplate}
variant="outline"
size="sm"
className="absolute top-2 right-2"
>
{copied ? (
<>
<CheckCircle className="w-4 h-4 mr-2" />
Copied!
</>
) : (
<>
<Copy className="w-4 h-4 mr-2" />
Copy
</>
)}
</Button>
</div>
<div className="mt-4 space-y-2 text-sm text-gray-600 dark:text-gray-400">
<p className="flex items-center gap-2">
<FileText className="w-4 h-4" />
Save this as <code className="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">.env.local</code> in the project root
</p>
<p className="flex items-center gap-2">
<AlertCircle className="w-4 h-4 text-yellow-500" />
Remember to restart the server after adding the file
</p>
</div>
</CardContent>
</Card>
{/* Resources Card */}
<Card>
<CardHeader>
<CardTitle>Helpful Resources</CardTitle>
<CardDescription>
Documentation and guides for Autotask API integration
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-3">
<a
href="https://ww1.autotask.net/help/DeveloperHelp/Content/APIs/REST/REST_API_Home.htm"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-2 text-blue-600 hover:text-blue-700 dark:text-blue-400"
>
<ExternalLink className="w-4 h-4" />
Autotask REST API Documentation
</a>
<a
href="/api/health"
target="_blank"
className="flex items-center gap-2 text-blue-600 hover:text-blue-700 dark:text-blue-400"
>
<ExternalLink className="w-4 h-4" />
API Health Check Endpoint
</a>
<a
href="/"
className="flex items-center gap-2 text-blue-600 hover:text-blue-700 dark:text-blue-400"
>
<ExternalLink className="w-4 h-4" />
Back to Dashboard
</a>
</div>
</CardContent>
</Card>
{/* API Zone Information */}
<Card>
<CardHeader>
<CardTitle>Autotask API Zones</CardTitle>
<CardDescription>
Make sure you're using the correct zone URL for your Autotask instance
</CardDescription>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
<div>
<h4 className="font-semibold mb-2">North America</h4>
<ul className="space-y-1 font-mono text-xs">
<li>Zone 1: webservices1.autotask.net</li>
<li>Zone 2: webservices2.autotask.net</li>
<li>Zone 3: webservices3.autotask.net</li>
<li>Zone 4: webservices4.autotask.net</li>
</ul>
</div>
<div>
<h4 className="font-semibold mb-2">Other Regions</h4>
<ul className="space-y-1 font-mono text-xs">
<li>Zone 5: webservices5.autotask.net</li>
<li>Zone 6: webservices6.autotask.net</li>
<li>PRD: prde.autotask.net</li>
<li>IOC: ioce.autotask.net</li>
</ul>
</div>
</div>
</CardContent>
</Card>
</div>
</div>
);
}