diff --git a/public/locales/en-UK/translation.json b/public/locales/en-UK/translation.json
index 4b9357b..58ce159 100644
--- a/public/locales/en-UK/translation.json
+++ b/public/locales/en-UK/translation.json
@@ -213,7 +213,20 @@
},
"SELECT_LIBRARIES_TO_IMPORT": "Select Libraries to Import",
"SELECT_LIBRARIES_TO_IMPORT_TOOLTIP": "Activity for Items within these libraries are still Tracked - Even when not imported.",
- "DATE_ADDED": "Date Added"
+ "DATE_ADDED": "Date Added",
+ "WEBHOOKS": "Webhooks",
+ "WEBHOOK_TYPE": "Webhook Type",
+ "TEST_NOW": "Test Now",
+ "WEBHOOKS_CONFIGURATION": "Webhook Configuration",
+ "WEBHOOKS_TOOLTIP": "Webhook URL to send Playback Activity to",
+ "WEBHOOK_SAVED": "Webhook Saved",
+ "WEBHOOK_NAME": "Webhook Name",
+ "DISCORD_WEBHOOK_URL": "Discord Webhook URL",
+ "ENABLE_WEBHOOK": "Enable Webhook",
+ "URL": "URL",
+ "TYPE": "Type",
+ "TRIGGER": "Trigger",
+ "STATUS": "Status"
},
"TASK_TYPE": {
"JOB": "Job",
diff --git a/src/pages/components/settings/webhooks.jsx b/src/pages/components/settings/webhooks.jsx
new file mode 100644
index 0000000..445a586
--- /dev/null
+++ b/src/pages/components/settings/webhooks.jsx
@@ -0,0 +1,313 @@
+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 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');
+
+function WebhookRow(props) {
+ const { webhook, onEdit, onTest } = props;
+
+ return (
+
+ *': { borderBottom: 'unset' } }}>
+ {webhook.name}
+ {webhook.url}
+ {webhook.webhook_type || 'generic'}
+ {webhook.trigger_type}
+
+
+ {webhook.enabled ? : }
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+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: 'Rapport mensuel films et séries',
+ url: '',
+ enabled: false,
+ trigger_type: 'scheduled',
+ schedule: '0 9 1 * *', // 9h le 1er du mois
+ method: 'POST',
+ webhook_type: 'discord'
+ });
+
+ useEffect(() => {
+ const fetchWebhooks = async () => {
+ try {
+ setLoading(true);
+ const response = await axios.get('/webhooks', {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ "Content-Type": "application/json",
+ },
+ });
+
+ setWebhooks(response.data);
+ setLoading(false);
+ } catch (err) {
+ console.log("Erreur lors du chargement des webhooks:", err);
+ setLoading(false);
+ }
+ };
+
+ fetchWebhooks();
+
+ const intervalId = setInterval(fetchWebhooks, 1000 * 5);
+ return () => clearInterval(intervalId);
+ }, []);
+
+ 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("L'URL du webhook Discord est requise");
+ 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",
+ }
+ });
+ }
+
+ setCurrentWebhook({
+ name: 'Nouveau webhook',
+ url: '',
+ enabled: false,
+ trigger_type: 'scheduled',
+ schedule: '0 9 1 * *',
+ method: 'POST',
+ webhook_type: 'discord'
+ });
+ setSuccess("Webhook enregistré avec succès");
+ setSaving(false);
+ } catch (err) {
+ setError("Erreur lors de l'enregistrement du webhook: " + (err.response?.data?.error || err.message));
+ setSaving(false);
+ }
+ };
+
+ const handleEdit = (webhook) => {
+ setCurrentWebhook(webhook);
+ };
+
+ const handleTest = async (webhookId) => {
+ if (!webhookId) {
+ setError("Impossible de tester ce webhook");
+ return;
+ }
+
+ try {
+ setLoading(true);
+ setError(null);
+
+ await axios.post(`/webhooks/${webhookId}/trigger-monthly`, {}, {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ "Content-Type": "application/json",
+ }
+ });
+
+ setSuccess("Webhook testé avec succès !");
+ setLoading(false);
+ } catch (err) {
+ setError("Erreur lors du test du webhook: " + (err.response?.data?.message || err.message));
+ setLoading(false);
+ }
+ };
+
+ if (loading && !webhooks.length) {
+ return ;
+ }
+
+ return (
+
+
+ {" "}
+ }>
+
+
+
+
+
+
+
+ {error && setError(null)} dismissible>{error}}
+ {success && setSuccess(false)} dismissible>
+ {typeof success === 'string' ? success : }
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ checked={currentWebhook.enabled}
+ onChange={handleToggleEnabled}
+ />
+
+
+
+
+
+
+
+
+
+
+ {webhooks.length > 0 ? (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {webhooks.map((webhook) => (
+
+ ))}
+ {webhooks.length === 0 && (
+
+
+
+
+
+ )}
+
+
+
+ ) : (
+
+ )}
+
+
+ );
+}
+
+export default WebhooksSettings;
\ No newline at end of file
diff --git a/src/pages/settings.jsx b/src/pages/settings.jsx
index d96a224..e0ca521 100644
--- a/src/pages/settings.jsx
+++ b/src/pages/settings.jsx
@@ -5,6 +5,7 @@ import SettingsConfig from "./components/settings/settingsConfig";
import Tasks from "./components/settings/Tasks";
import SecuritySettings from "./components/settings/security";
import ApiKeys from "./components/settings/apiKeys";
+import WebhooksSettings from "./components/settings/webhooks";
import LibrarySelector from "./library_selector";
import Logs from "./components/settings/logs";
@@ -53,6 +54,15 @@ export default function Settings() {
+ }
+ style={{ minHeight: "500px" }}
+ >
+
+
+