This commit is contained in:
Iliyan Angelov
2025-11-19 12:27:01 +02:00
parent 2043ac897c
commit 34b4c969d4
469 changed files with 26870 additions and 8329 deletions

View File

@@ -29,6 +29,8 @@ import adminPrivacyService, {
import systemSettingsService, {
StripeSettingsResponse,
UpdateStripeSettingsRequest,
PayPalSettingsResponse,
UpdatePayPalSettingsRequest,
SmtpSettingsResponse,
UpdateSmtpSettingsRequest,
CompanySettingsResponse,
@@ -71,6 +73,15 @@ const SettingsPage: React.FC = () => {
const [showSecretKey, setShowSecretKey] = useState(false);
const [showWebhookSecret, setShowWebhookSecret] = useState(false);
// PayPal Settings State
const [paypalSettings, setPaypalSettings] = useState<PayPalSettingsResponse['data'] | null>(null);
const [paypalFormData, setPaypalFormData] = useState<UpdatePayPalSettingsRequest>({
paypal_client_id: '',
paypal_client_secret: '',
paypal_mode: 'sandbox',
});
const [showPayPalSecret, setShowPayPalSecret] = useState(false);
// SMTP Settings State
const [smtpSettings, setSmtpSettings] = useState<SmtpSettingsResponse['data'] | null>(null);
const [smtpFormData, setSmtpFormData] = useState<UpdateSmtpSettingsRequest>({
@@ -144,11 +155,12 @@ const SettingsPage: React.FC = () => {
const loadAllSettings = async () => {
try {
setLoading(true);
const [policyRes, integrationRes, currencyRes, stripeRes] = await Promise.all([
const [policyRes, integrationRes, currencyRes, stripeRes, paypalRes] = await Promise.all([
adminPrivacyService.getCookiePolicy(),
adminPrivacyService.getIntegrations(),
systemSettingsService.getPlatformCurrency(),
systemSettingsService.getStripeSettings(),
systemSettingsService.getPayPalSettings(),
]);
setPolicy(policyRes.data);
@@ -166,6 +178,12 @@ const SettingsPage: React.FC = () => {
stripe_publishable_key: stripeRes.data.stripe_publishable_key || '',
stripe_webhook_secret: '',
});
setPaypalSettings(paypalRes.data);
setPaypalFormData({
paypal_client_id: '',
paypal_client_secret: '',
paypal_mode: paypalRes.data.paypal_mode || 'sandbox',
});
} catch (error: any) {
toast.error(error.message || 'Failed to load settings');
} finally {
@@ -307,6 +325,45 @@ const SettingsPage: React.FC = () => {
}
};
// PayPal Settings Handlers
const handleSavePayPal = async () => {
try {
setSaving(true);
const updateData: UpdatePayPalSettingsRequest = {};
if (paypalFormData.paypal_client_id && paypalFormData.paypal_client_id.trim()) {
updateData.paypal_client_id = paypalFormData.paypal_client_id.trim();
}
if (paypalFormData.paypal_client_secret && paypalFormData.paypal_client_secret.trim()) {
updateData.paypal_client_secret = paypalFormData.paypal_client_secret.trim();
}
if (paypalFormData.paypal_mode) {
updateData.paypal_mode = paypalFormData.paypal_mode;
}
await systemSettingsService.updatePayPalSettings(updateData);
await loadAllSettings();
setPaypalFormData({
...paypalFormData,
paypal_client_id: '',
paypal_client_secret: '',
});
toast.success('PayPal settings updated successfully');
} catch (error: any) {
toast.error(
error.response?.data?.message ||
error.response?.data?.detail ||
'Failed to update PayPal settings'
);
} finally {
setSaving(false);
}
};
// SMTP Settings Handlers
const handleSaveSmtp = async () => {
try {
@@ -1305,6 +1362,180 @@ const SettingsPage: React.FC = () => {
</div>
</div>
</div>
{/* PayPal Payment Settings Section */}
<div className="relative bg-white/90 backdrop-blur-xl rounded-2xl shadow-xl border border-gray-200/50 p-8">
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-6 mb-8">
<div className="space-y-3">
<div className="flex items-center gap-3">
<div className="p-2.5 rounded-xl bg-gradient-to-br from-blue-500/10 to-cyan-500/10 border border-blue-200/40">
<CreditCard className="w-6 h-6 text-blue-600" />
</div>
<h2 className="text-3xl font-extrabold text-gray-900">PayPal Payment Settings</h2>
</div>
<p className="text-gray-600 text-base max-w-2xl leading-relaxed">
Configure your PayPal account credentials to enable PayPal payments. All PayPal payments will be processed through your PayPal account.
</p>
</div>
<button
type="button"
onClick={handleSavePayPal}
disabled={saving}
className="group relative px-8 py-4 bg-gradient-to-r from-amber-500 via-amber-500 to-amber-600 text-white font-semibold rounded-xl shadow-xl shadow-amber-500/30 hover:shadow-2xl hover:shadow-amber-500/40 transition-all duration-300 hover:scale-105 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100 overflow-hidden"
>
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent translate-x-[-100%] group-hover:translate-x-[100%] transition-transform duration-1000"></div>
<div className="relative flex items-center gap-3">
<Save className={`w-5 h-5 ${saving ? 'animate-spin' : ''}`} />
{saving ? 'Saving...' : 'Save Changes'}
</div>
</button>
</div>
{/* Info Card */}
<div className="relative bg-gradient-to-br from-blue-50/80 via-cyan-50/60 to-blue-50/80 backdrop-blur-xl rounded-2xl shadow-xl border border-blue-200/50 p-8 overflow-hidden mb-8">
<div className="absolute top-0 right-0 w-64 h-64 bg-gradient-to-br from-blue-400/20 to-transparent rounded-bl-full"></div>
<div className="relative flex gap-6">
<div className="flex-shrink-0">
<div className="p-4 rounded-xl bg-gradient-to-br from-blue-500 to-cyan-600 shadow-lg border border-blue-400/50">
<Info className="w-6 h-6 text-white" />
</div>
</div>
<div className="space-y-3 flex-1">
<p className="font-bold text-gray-900 text-lg">
How PayPal payments work
</p>
<p className="text-gray-700 leading-relaxed">
PayPal handles all PayPal payments securely. You need to provide your PayPal API credentials from your PayPal Developer Dashboard. The client ID and client secret are used to process payments. You can use sandbox mode for testing or live mode for production.
</p>
<div className="pt-3 border-t border-blue-200/50">
<p className="text-sm text-gray-600">
<strong className="text-gray-900">Note:</strong> Leave fields empty to keep existing values. Only enter new values when you want to update them.
</p>
</div>
</div>
</div>
</div>
{/* PayPal Settings Form */}
<div className="space-y-8">
<div className="flex items-start gap-4 pb-6 border-b border-gray-200/60">
<div className="p-3 rounded-xl bg-gradient-to-br from-blue-500/10 to-cyan-500/10 border border-blue-200/40">
<Key className="w-6 h-6 text-blue-600" />
</div>
<div className="space-y-2 flex-1">
<p className="font-bold text-gray-900 text-xl">PayPal API Credentials</p>
<p className="text-sm text-gray-600 leading-relaxed">
Get these credentials from your{' '}
<a
href="https://developer.paypal.com/dashboard"
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 hover:text-blue-700 underline font-medium"
>
PayPal Developer Dashboard
</a>
</p>
</div>
</div>
<div className="space-y-8">
{/* Client ID */}
<div className="space-y-4">
<label className="flex items-center gap-2 text-sm font-bold text-gray-900 tracking-wide">
<Globe className="w-4 h-4 text-gray-600" />
PayPal Client ID
<span className="text-red-500">*</span>
</label>
<input
type="text"
value={paypalFormData.paypal_client_id}
onChange={(e) =>
setPaypalFormData({ ...paypalFormData, paypal_client_id: e.target.value })
}
placeholder={
paypalSettings?.has_client_id
? `Current: ${paypalSettings.paypal_client_id || '****'}`
: 'Client ID from PayPal Dashboard'
}
className="w-full px-4 py-3.5 bg-white border border-gray-300 rounded-xl shadow-sm focus:ring-2 focus:ring-amber-500/50 focus:border-amber-500 transition-all duration-200 text-sm"
/>
<div className="flex items-center gap-2 text-xs text-gray-500">
{paypalSettings?.has_client_id && (
<span className="text-emerald-600 font-medium flex items-center gap-1">
<span className="w-2 h-2 bg-emerald-500 rounded-full"></span>
Currently configured
</span>
)}
</div>
</div>
{/* Client Secret */}
<div className="space-y-4">
<label className="flex items-center gap-2 text-sm font-bold text-gray-900 tracking-wide">
<Lock className="w-4 h-4 text-gray-600" />
PayPal Client Secret
<span className="text-red-500">*</span>
</label>
<div className="relative">
<input
type={showPayPalSecret ? 'text' : 'password'}
value={paypalFormData.paypal_client_secret}
onChange={(e) =>
setPaypalFormData({ ...paypalFormData, paypal_client_secret: e.target.value })
}
placeholder={
paypalSettings?.has_client_secret
? `Current: ${paypalSettings.paypal_client_secret_masked || '****'}`
: 'Client Secret from PayPal Dashboard'
}
className="w-full px-4 py-3.5 pr-12 bg-white border border-gray-300 rounded-xl shadow-sm focus:ring-2 focus:ring-amber-500/50 focus:border-amber-500 transition-all duration-200 text-sm font-mono"
/>
<button
type="button"
onClick={() => setShowPayPalSecret(!showPayPalSecret)}
className="absolute right-4 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-700 transition-colors p-1"
>
{showPayPalSecret ? (
<EyeOff className="w-5 h-5" />
) : (
<Eye className="w-5 h-5" />
)}
</button>
</div>
<div className="flex items-center gap-2 text-xs text-gray-500">
{paypalSettings?.has_client_secret && (
<span className="text-emerald-600 font-medium flex items-center gap-1">
<span className="w-2 h-2 bg-emerald-500 rounded-full"></span>
Currently configured
</span>
)}
</div>
</div>
{/* Mode */}
<div className="space-y-4">
<label className="flex items-center gap-2 text-sm font-bold text-gray-900 tracking-wide">
<Globe className="w-4 h-4 text-gray-600" />
PayPal Mode
<span className="text-red-500">*</span>
</label>
<select
value={paypalFormData.paypal_mode}
onChange={(e) =>
setPaypalFormData({ ...paypalFormData, paypal_mode: e.target.value })
}
className="w-full px-4 py-3.5 bg-white border border-gray-300 rounded-xl shadow-sm focus:ring-2 focus:ring-amber-500/50 focus:border-amber-500 transition-all duration-200 text-sm"
>
<option value="sandbox">Sandbox (Testing)</option>
<option value="live">Live (Production)</option>
</select>
<div className="flex items-center gap-2 text-xs text-gray-500">
<p>Use sandbox mode for testing with test credentials, or live mode for production payments.</p>
</div>
</div>
</div>
</div>
</div>
</div>
)}