import React, { useState, useEffect } from "react";
import axios from "../../../lib/axios_instance";
import { Form, Row, Col, Button, Spinner, Alert } from "react-bootstrap";
import InformationLineIcon from "remixicon-react/InformationLineIcon";
import { Tooltip } from "@mui/material";
import PropTypes from 'prop-types';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell from '@mui/material/TableCell';
import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import { Trans } from "react-i18next";
import Loading from "../general/loading";
import ErrorBoundary from "../general/ErrorBoundary";
const token = localStorage.getItem('token');
// Modification du composant WebhookRow pour passer l'objet webhook complet
function WebhookRow(props) {
const { webhook, onEdit, onTest } = props;
return (
*': { borderBottom: 'unset' } }}>
{webhook.name}
{webhook.url}
{webhook.webhook_type || 'generic'}
{webhook.trigger_type}
{webhook.enabled ? : }
);
}
WebhookRow.propTypes = {
webhook: PropTypes.shape({
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
url: PropTypes.string.isRequired,
webhook_type: PropTypes.string,
trigger_type: PropTypes.string.isRequired,
enabled: PropTypes.bool.isRequired
}).isRequired,
onEdit: PropTypes.func.isRequired,
onTest: PropTypes.func.isRequired
};
function WebhooksSettings() {
const [webhooks, setWebhooks] = useState([]);
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [error, setError] = useState(null);
const [success, setSuccess] = useState(false);
const [currentWebhook, setCurrentWebhook] = useState({
name: 'Monthly Report of Most watched movies and series',
url: '',
enabled: false,
trigger_type: 'scheduled',
schedule: '0 9 1 * *',
method: 'POST',
webhook_type: 'discord'
});
const [eventWebhooks, setEventWebhooks] = useState({
playback_started: { exists: false, enabled: false },
playback_ended: { exists: false, enabled: false },
media_recently_added: { exists: false, enabled: false }
});
useEffect(() => {
const fetchWebhooks = async () => {
try {
const response = await axios.get('/webhooks', {
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
});
if (response.data !== webhooks) {
setWebhooks(response.data);
await loadEventWebhooks();
}
if (loading) {
setLoading(false);
}
} catch (err) {
console.error("Error loading webhooks:", err);
if (loading) {
setLoading(false);
}
}
};
fetchWebhooks();
const intervalId = setInterval(fetchWebhooks, 1000 * 10);
return () => clearInterval(intervalId);
}, [webhooks.length]);
const handleInputChange = (e) => {
const { name, value } = e.target;
setCurrentWebhook(prev => ({ ...prev, [name]: value }));
};
const handleToggleEnabled = () => {
setCurrentWebhook(prev => ({ ...prev, enabled: !prev.enabled }));
};
const handleFormSubmit = async (e) => {
e.preventDefault();
try {
setSaving(true);
setError(null);
setSuccess(false);
if (!currentWebhook.url) {
setError("Webhook URL is required");
setSaving(false);
return;
}
if (currentWebhook.trigger_type === 'event' && !currentWebhook.event_type) {
setError("Event type is required for an event based webhook");
setSaving(false);
return;
}
let response;
if (currentWebhook.id) {
response = await axios.put(`/webhooks/${currentWebhook.id}`, currentWebhook, {
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
}
});
} else {
response = await axios.post('/webhooks', currentWebhook, {
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
}
});
}
const webhooksResponse = await axios.get('/webhooks', {
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
});
setWebhooks(webhooksResponse.data);
await loadEventWebhooks();
setCurrentWebhook({
name: 'New Webhook',
url: '',
enabled: false,
trigger_type: 'scheduled',
schedule: '0 9 1 * *',
method: 'POST',
webhook_type: 'discord'
});
setSuccess("Webhook saved successfully!");
setSaving(false);
} catch (err) {
setError("Error while saving webhook " + (err.response?.data?.error || err.message));
setSaving(false);
}
};
const handleEdit = (webhook) => {
setCurrentWebhook(webhook);
};
const handleTest = async (webhook) => {
if (!webhook || !webhook.id) {
setError("Impossible to test the webhook: no webhook provided");
setLoading(false);
return;
}
try {
setLoading(true);
setError(null);
let endpoint = `/webhooks/${webhook.id}/test`;
if (webhook.trigger_type === 'scheduled' && webhook.schedule && webhook.schedule.includes('1 * *')) {
endpoint = `/webhooks/${webhook.id}/trigger-monthly`;
}
await axios.post(endpoint, {}, {
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
}
});
setSuccess(`Webhook ${webhook.name} test triggered successfully!`);
setLoading(false);
} catch (err) {
setError("Error during the test of the webhook: " + (err.response?.data?.message || err.message));
setLoading(false);
}
};
const getEventWebhookStatus = (eventType) => {
return eventWebhooks[eventType]?.enabled || false;
};
const loadEventWebhooks = async () => {
try {
const eventTypes = ['playback_started', 'playback_ended', 'media_recently_added'];
const status = {};
eventTypes.forEach(eventType => {
const matchingWebhooks = webhooks.filter(
webhook => webhook.trigger_type === 'event' && webhook.event_type === eventType
);
status[eventType] = {
exists: matchingWebhooks.length > 0,
enabled: matchingWebhooks.some(webhook => webhook.enabled)
};
});
setEventWebhooks(status);
} catch (error) {
console.error('Error loading event webhook status:', error);
}
};
const toggleEventWebhook = async (eventType) => {
try {
setLoading(true);
setError(null);
const isCurrentlyEnabled = getEventWebhookStatus(eventType);
const matchingWebhooks = webhooks.filter(
webhook => webhook.trigger_type === 'event' && webhook.event_type === eventType
);
if (matchingWebhooks.length === 0 && !isCurrentlyEnabled) {
const newWebhook = {
name: `Notification - ${getEventDisplayName(eventType)}`,
url: '',
enabled: true,
trigger_type: 'event',
event_type: eventType,
method: 'POST',
webhook_type: 'discord'
};
setCurrentWebhook(newWebhook);
setLoading(false);
return;
}
for (const webhook of matchingWebhooks) {
await axios.put(`/webhooks/${webhook.id}`,
{ ...webhook, enabled: !isCurrentlyEnabled },
{
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
}
}
);
}
setEventWebhooks(prev => ({
...prev,
[eventType]: {
...prev[eventType],
enabled: !isCurrentlyEnabled
}
}));
const response = await axios.get('/webhooks', {
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
});
setWebhooks(response.data);
setLoading(false);
setSuccess(`Webhook for ${getEventDisplayName(eventType)} ${!isCurrentlyEnabled ? 'enabled' : 'disabled'} with success!`);
} catch (error) {
setError("Error while editing webhook: " + (error.response?.data?.error || error.message));
setLoading(false);
}
};
const getEventDisplayName = (eventType) => {
switch(eventType) {
case 'playback_started':
return 'Playback started';
case 'playback_ended':
return 'Playback ended';
case 'media_recently_added':
return 'New media added';
default:
return eventType;
}
};
if (loading && !webhooks.length) {
return ;
}
return (
{" "}
}>
{error && setError(null)} dismissible>{error}}
{success && setSuccess(false)} dismissible>
{typeof success === 'string' ? success : }
}
}
checked={currentWebhook.enabled}
onChange={handleToggleEnabled}
/>
{/* Ajout de la section pour les webhooks événementiels */}
}>
toggleEventWebhook('playback_started')}
/>
Send a webhook notification when a user starts watching a media
toggleEventWebhook('playback_ended')}
/>
Send a webhook notification when a user finishes watching a media
toggleEventWebhook('media_recently_added')}
/>
Send a webhook notification when new media is added to the library
{webhooks.map((webhook) => (
))}
{webhooks.length === 0 && (
)}
);
}
export default WebhooksSettings;