375 lines
13 KiB
TypeScript
375 lines
13 KiB
TypeScript
'use client';
|
||
|
||
import { useState, useEffect } from 'react';
|
||
import { useRouter } from 'next/navigation';
|
||
import axios from 'axios';
|
||
|
||
export default function Turn14SettingsClient() {
|
||
const router = useRouter();
|
||
const [ftpHost, setFtpHost] = useState('ftp.motorstateftp.com');
|
||
const [ftpUsername, setFtpUsername] = useState('');
|
||
const [ftpPassword, setFtpPassword] = useState('');
|
||
const [message, setMessage] = useState('');
|
||
const [loading, setLoading] = useState(false);
|
||
const [statusLoaded, setStatusLoaded] = useState(false);
|
||
const [showPassword, setShowPassword] = useState(false);
|
||
const [connectionStatus, setConnectionStatus] = useState<'idle' | 'success' | 'error'>('idle');
|
||
const [userId, setUserId] = useState<string | null>(null);
|
||
const [payment, setPayment] = useState<any>(null);
|
||
|
||
useEffect(() => {
|
||
// Check for user ID in cookies or localStorage
|
||
// Note: Cookies cannot be directly accessed client-side; you may need to sync with the server
|
||
const uid = localStorage.getItem('data4auto_uid');
|
||
|
||
if (uid) {
|
||
setUserId(uid);
|
||
} else {
|
||
router.push('/login');
|
||
}
|
||
}, [router]);
|
||
|
||
useEffect(() => {
|
||
const role = localStorage.getItem("user_role");
|
||
const sessionId = localStorage.getItem("payment_session");
|
||
|
||
// ✅ Admins and Partners can access directly (skip payment check)
|
||
if (role === "admin" || role === "partner") {
|
||
return;
|
||
}
|
||
|
||
// 🚫 If no payment session, redirect to pricing
|
||
if (!sessionId) {
|
||
router.push("/pricing");
|
||
return;
|
||
}
|
||
|
||
// ✅ Otherwise, check payment details
|
||
const fetchPaymentDetails = async () => {
|
||
try {
|
||
const res: any = await axios.get(
|
||
"https://ebay.backend.data4autos.com/api/payment/details",
|
||
{ params: { session_id: sessionId } }
|
||
);
|
||
setPayment(res.data.payment);
|
||
} catch (err) {
|
||
console.error("Error fetching payment details:", err);
|
||
}
|
||
};
|
||
|
||
fetchPaymentDetails();
|
||
}, [router]);
|
||
|
||
const read = async (r: Response) =>
|
||
r.headers.get('content-type')?.includes('application/json')
|
||
? r.json()
|
||
: r.text();
|
||
|
||
// Load FTP Status on Mount
|
||
useEffect(() => {
|
||
if (!userId) return;
|
||
|
||
console.log('Fetching existing FTP status...');
|
||
const fetchStatus = async () => {
|
||
setLoading(true);
|
||
try {
|
||
const res = await fetch('https://ebay.backend.data4autos.com/api/motorstate/auth/motorstate/status', {
|
||
// ^ make sure this path matches the Express route
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ userid: userId }),
|
||
});
|
||
|
||
const data = await read(res);
|
||
if (!res.ok) {
|
||
throw new Error(
|
||
(typeof data === 'object' && (data?.message || data?.error)) ||
|
||
`Status failed (${res.status})`
|
||
);
|
||
}
|
||
|
||
if (data?.hasCredentials) {
|
||
setFtpHost(data.credentials.ftpHost || 'ftp.motorstateftp.com');
|
||
setFtpUsername(data.credentials.ftpUsername || '');
|
||
setFtpPassword(data.credentials.ftpPassword || '');
|
||
setMessage('✅ Existing FTP credentials loaded.');
|
||
setConnectionStatus('success');
|
||
} else {
|
||
setMessage('ℹ️ No credentials saved yet.');
|
||
setConnectionStatus('idle');
|
||
}
|
||
} catch (e: any) {
|
||
console.error('Error fetching FTP status:', e);
|
||
setMessage(`❌ ${e?.message || 'Failed to fetch status.'}`);
|
||
setConnectionStatus('error');
|
||
} finally {
|
||
setLoading(false);
|
||
setStatusLoaded(true);
|
||
}
|
||
};
|
||
|
||
fetchStatus();
|
||
}, [userId]);
|
||
|
||
|
||
// Test and Save Credentials
|
||
const handleTestAndSave = async () => {
|
||
setMessage('');
|
||
if (!ftpUsername || !ftpPassword) {
|
||
setMessage('❌ Please enter FTP Username and Password.');
|
||
setConnectionStatus('error');
|
||
return;
|
||
}
|
||
if (!userId) {
|
||
setMessage('❌ User ID not found. Please log in again.');
|
||
setConnectionStatus('error');
|
||
return;
|
||
}
|
||
setLoading(true);
|
||
try {
|
||
setMessage('Testing FTP connection... Please wait.');
|
||
|
||
// Test the FTP connection
|
||
const testRes = await fetch('https://motorstate.data4autos.com/api/tokens', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
clientName: "motorstate",
|
||
host: ftpHost,
|
||
user: ftpUsername,
|
||
password: ftpPassword,
|
||
}),
|
||
});
|
||
|
||
const testData = await read(testRes);
|
||
|
||
if (!testData.ok) {
|
||
throw new Error(testData.message || 'FTP connection failed');
|
||
}
|
||
|
||
// If test is successful, save the credentials
|
||
setMessage('✅ FTP connection successful. Saving credentials...');
|
||
|
||
const saveRes = await fetch('https://ebay.backend.data4autos.com/api/motorstate/auth/motorstate/save', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
userid: userId,
|
||
ftpHost,
|
||
ftpUsername,
|
||
ftpPassword,
|
||
clientName : "motorstate",
|
||
}),
|
||
});
|
||
|
||
|
||
const saveData = await read(saveRes);
|
||
if (!saveRes.ok) {
|
||
console.error('Save request failed:', saveRes.status, saveData);
|
||
throw new Error(
|
||
(typeof saveData === 'object' &&
|
||
(saveData?.message || saveData?.error)) ||
|
||
`Save failed (${saveRes.status})`
|
||
);
|
||
}
|
||
|
||
setMessage('✅ Credentials tested and saved successfully.');
|
||
setConnectionStatus('success');
|
||
} catch (e: any) {
|
||
console.error('Error in handleTestAndSave:', e);
|
||
setMessage(`❌ ${e?.message || 'Failed to test or save credentials.'}`);
|
||
setConnectionStatus('error');
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
const getStatusColor = () => {
|
||
switch (connectionStatus) {
|
||
case 'success':
|
||
return 'bg-green-100 border-green-400 text-green-800';
|
||
case 'error':
|
||
return 'bg-red-100 border-red-400 text-red-800';
|
||
default:
|
||
return 'bg-blue-100 border-blue-400 text-blue-800';
|
||
}
|
||
};
|
||
|
||
const getStatusIcon = () => {
|
||
switch (connectionStatus) {
|
||
case 'success':
|
||
return (
|
||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||
<path
|
||
fillRule="evenodd"
|
||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
|
||
clipRule="evenodd"
|
||
/>
|
||
</svg>
|
||
);
|
||
case 'error':
|
||
return (
|
||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||
<path
|
||
fillRule="evenodd"
|
||
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
|
||
clipRule="evenodd"
|
||
/>
|
||
</svg>
|
||
);
|
||
default:
|
||
return (
|
||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||
<path
|
||
fillRule="evenodd"
|
||
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
|
||
clipRule="evenodd"
|
||
/>
|
||
</svg>
|
||
);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className="min-h-[83vh] bg-gradient-to-br from-[#00d1ff]/10 via-white to-[#00d1ff]/20 py-12 px-4 sm:px-6 lg:px-8">
|
||
<div className="max-w-2xl mx-auto bg-white border border-gray-200 rounded-2xl shadow-lg overflow-hidden">
|
||
<div className="px-6 py-8">
|
||
{/* Header Section */}
|
||
<div className="text-center mb-8">
|
||
<div className="inline-flex items-center justify-center w-16 h-16 bg-[#00d1ff]/10 rounded-2xl mb-4">
|
||
<svg
|
||
className="w-8 h-8 text-[#00d1ff]"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
viewBox="0 0 24 24"
|
||
>
|
||
<path
|
||
strokeLinecap="round"
|
||
strokeLinejoin="round"
|
||
strokeWidth="2"
|
||
d="M13 10V3L4 14h7v7l9-11h-7z"
|
||
/>
|
||
</svg>
|
||
</div>
|
||
<h1 className="text-3xl font-bold text-gray-900 mb-1">
|
||
<span className="text-[#00d1ff]">MotorState FTP </span> Configuration
|
||
</h1>
|
||
<p className="text-gray-600 text-sm">
|
||
Manage your MotorState FTP credentials securely.
|
||
</p>
|
||
</div>
|
||
|
||
{/* Input Fields */}
|
||
<div className="space-y-6">
|
||
<div>
|
||
<label htmlFor="ftpHost" className="block text-sm font-semibold text-gray-700 mb-2">
|
||
FTP Host
|
||
</label>
|
||
<input
|
||
id="ftpHost"
|
||
type="text"
|
||
value={ftpHost}
|
||
onChange={(e) => setFtpHost(e.target.value)}
|
||
className="block w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00d1ff] focus:border-[#00d1ff] transition-all"
|
||
placeholder="Enter your FTP Host (e.g., ftp.example.com)"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label htmlFor="ftpUsername" className="block text-sm font-semibold text-gray-700 mb-2">
|
||
FTP Username
|
||
</label>
|
||
<input
|
||
id="ftpUsername"
|
||
type="text"
|
||
value={ftpUsername}
|
||
onChange={(e) => setFtpUsername(e.target.value)}
|
||
className="block w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00d1ff] focus:border-[#00d1ff] transition-all"
|
||
placeholder="Enter your FTP Username"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label htmlFor="ftpPassword" className="block text-sm font-semibold text-gray-700 mb-2">
|
||
FTP Password
|
||
</label>
|
||
<div className="relative">
|
||
<input
|
||
id="ftpPassword"
|
||
type={showPassword ? 'text' : 'password'}
|
||
value={ftpPassword}
|
||
onChange={(e) => setFtpPassword(e.target.value)}
|
||
className="block w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00d1ff] focus:border-[#00d1ff] transition-all pr-10"
|
||
placeholder="Enter your FTP Password"
|
||
/>
|
||
<button
|
||
type="button"
|
||
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-[#00d1ff]"
|
||
onClick={() => setShowPassword(!showPassword)}
|
||
>
|
||
{showPassword ? '🙈' : '👁️'}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Test and Save Button */}
|
||
<button
|
||
onClick={handleTestAndSave}
|
||
disabled={loading}
|
||
className="w-full flex justify-center items-center py-3 px-4 border border-transparent rounded-lg shadow-md text-sm font-medium text-white bg-[#00d1ff] hover:bg-[#00bce5] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#00d1ff] transition-all"
|
||
>
|
||
{loading ? (
|
||
<>
|
||
<svg
|
||
className="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
fill="none"
|
||
viewBox="0 0 24 24"
|
||
>
|
||
<circle
|
||
className="opacity-25"
|
||
cx="12"
|
||
cy="12"
|
||
r="10"
|
||
stroke="currentColor"
|
||
strokeWidth="4"
|
||
></circle>
|
||
<path
|
||
className="opacity-75"
|
||
fill="currentColor"
|
||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||
></path>
|
||
</svg>
|
||
Testing...
|
||
</>
|
||
) : (
|
||
'Test and Save'
|
||
)}
|
||
</button>
|
||
|
||
{/* Status Message */}
|
||
{statusLoaded && message && (
|
||
<div
|
||
className={`mt-4 p-4 rounded-lg border ${getStatusColor()} flex items-start space-x-3`}
|
||
>
|
||
<div className="flex-shrink-0">{getStatusIcon()}</div>
|
||
<p className="text-sm font-medium">{message}</p>
|
||
</div>
|
||
)}
|
||
{/* Tips Section */}
|
||
<div className="bg-[#00d1ff]/5 p-4 rounded-lg border border-[#00d1ff]/20 mt-6">
|
||
<h3 className="text-sm font-semibold text-[#00d1ff] mb-2">
|
||
💡 Connection Tips
|
||
</h3>
|
||
<ul className="text-xs text-gray-600 space-y-1">
|
||
<li>• Ensure your FTP credentials are valid and active.</li>
|
||
<li>• The default FTP host is ftp.motorstateftp.com.</li>
|
||
<li>• Your credentials will be tested before saving.</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
} |