From e3e3a167dae2f2a2a357141b13f17988c0db43cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20MARQUET?= Date: Fri, 25 Apr 2025 12:02:11 +0200 Subject: [PATCH] Add webhooks settings component and integrate into settings page --- public/locales/en-UK/translation.json | 15 +- src/pages/components/settings/webhooks.jsx | 313 +++++++++++++++++++++ src/pages/settings.jsx | 10 + vite.config.js | 1 + 4 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 src/pages/components/settings/webhooks.jsx 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" }} + > + + +