Initial commit: existing turf_saas codebase
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
281
n8n-chat.html
Normal file
281
n8n-chat.html
Normal file
@@ -0,0 +1,281 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>N8N Workflow Chat - H3R7Tech</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=Syne:wght@400;700;800&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--bg: #0d0f1a;
|
||||
--bg2: #13162a;
|
||||
--bg3: #1a1f38;
|
||||
--accent: #e94560;
|
||||
--cyan: #00d9ff;
|
||||
--green: #2ec4b6;
|
||||
--yellow: #f4c542;
|
||||
--muted: #4a5080;
|
||||
--text: #e8eaf6;
|
||||
--text2: #8892b0;
|
||||
}
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: 'Syne', sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
min-height: 100vh;
|
||||
}
|
||||
.header {
|
||||
background: linear-gradient(135deg, #0d0f1a 0%, #1a1f38 100%);
|
||||
border-bottom: 1px solid var(--muted);
|
||||
padding: 16px 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
.header h1 { font-size: 20px; font-weight: 800; color: var(--cyan); }
|
||||
.header .status { margin-left: auto; font-family: 'Space Mono', monospace; font-size: 12px; color: var(--text2); }
|
||||
.status-dot {
|
||||
display: inline-block; width: 8px; height: 8px; border-radius: 50%;
|
||||
background: var(--accent); margin-right: 6px;
|
||||
}
|
||||
.status-dot.connected { background: var(--green); }
|
||||
|
||||
.content { max-width: 800px; margin: 0 auto; padding: 20px; }
|
||||
|
||||
.card {
|
||||
background: var(--bg2);
|
||||
border: 1px solid var(--bg3);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.chat-container {
|
||||
height: 400px;
|
||||
overflow-y: auto;
|
||||
background: var(--bg);
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.message {
|
||||
margin-bottom: 12px;
|
||||
padding: 12px 16px;
|
||||
border-radius: 12px;
|
||||
max-width: 85%;
|
||||
}
|
||||
.message.user {
|
||||
background: rgba(0, 217, 255, 0.15);
|
||||
border: 1px solid rgba(0, 217, 255, 0.3);
|
||||
margin-left: auto;
|
||||
}
|
||||
.message.system {
|
||||
background: rgba(46, 196, 182, 0.15);
|
||||
border: 1px solid rgba(46, 196, 182, 0.3);
|
||||
}
|
||||
.message.error {
|
||||
background: rgba(233, 69, 96, 0.15);
|
||||
border: 1px solid rgba(233, 69, 96, 0.3);
|
||||
color: var(--accent);
|
||||
}
|
||||
.message .role {
|
||||
font-size: 10px; font-weight: 700; text-transform: uppercase;
|
||||
color: var(--text2); margin-bottom: 4px;
|
||||
}
|
||||
.message .content { padding: 0; max-width: 100%; }
|
||||
|
||||
.input-row {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
.input-row input {
|
||||
flex: 1;
|
||||
padding: 14px 18px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--muted);
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-family: 'Syne', sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
.input-row input:focus {
|
||||
outline: none;
|
||||
border-color: var(--cyan);
|
||||
}
|
||||
.input-row button {
|
||||
padding: 14px 24px;
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
background: var(--cyan);
|
||||
color: #000;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
font-family: 'Syne', sans-serif;
|
||||
transition: all .2s;
|
||||
}
|
||||
.input-row button:hover { transform: translateY(-2px); }
|
||||
.input-row button:disabled { background: var(--muted); cursor: not-allowed; }
|
||||
|
||||
.loader {
|
||||
display: flex; align-items: center; gap: 8px; padding: 20px;
|
||||
}
|
||||
.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--cyan); animation: bounce .8s infinite; }
|
||||
.dot:nth-child(2) { animation-delay: .15s; }
|
||||
.dot:nth-child(3) { animation-delay: .3s; }
|
||||
@keyframes bounce {
|
||||
0%,100% { transform: translateY(0); opacity: .4; }
|
||||
50% { transform: translateY(-8px); opacity: 1; }
|
||||
}
|
||||
|
||||
.quick-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.quick-actions button {
|
||||
padding: 8px 14px;
|
||||
border-radius: 20px;
|
||||
border: 1px solid var(--muted);
|
||||
background: var(--bg2);
|
||||
color: var(--text2);
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
transition: all .2s;
|
||||
}
|
||||
.quick-actions button:hover {
|
||||
border-color: var(--cyan);
|
||||
color: var(--cyan);
|
||||
}
|
||||
|
||||
.home-btn{position:fixed;top:10px;left:10px;z-index:9999;background:linear-gradient(135deg,#00d9ff,#7b2cbf);color:#fff;border:none;border-radius:8px;padding:10px 15px;font-size:14px;cursor:pointer;text-decoration:none;display:flex;align-items:center;gap:8px;box-shadow:0 2px 10px rgba(0,217,255,0.3);transition:all 0.3s;font-family:-apple-system,BlinkMacSystemFont,sans-serif}.home-btn:hover{transform:translateY(-2px);box-shadow:0 4px 15px rgba(0,217,255,0.5)}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="https://portal-kolifee.duckdns.org/" class="home-btn" title="Retour au portail"><span style="font-size:18px">🏠</span><span>Accueil</span></a>
|
||||
<div class="header">
|
||||
<h1>🤖 N8N Workflow Chat</h1>
|
||||
<div class="status">
|
||||
<span class="status-dot" id="status-dot"></span>
|
||||
<span id="status-text">Connecting...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<div class="card">
|
||||
<div class="quick-actions">
|
||||
<button onclick="sendQuick('Quelle est la date du jour?')">📅 Date</button>
|
||||
<button onclick="sendQuick('Liste les 5 derniers workflows actifs')">📋 Workflows</button>
|
||||
<button onclick="sendQuick('Affiche les statistiques du système')">📊 Stats</button>
|
||||
<button onclick="sendQuick('Test de connexion')">✅ Test</button>
|
||||
</div>
|
||||
|
||||
<div class="chat-container" id="chat-container">
|
||||
<div class="message system">
|
||||
<div class="role">Système</div>
|
||||
<div class="content">👋 Bienvenue! Posez vos questions au workflow n8n.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-row">
|
||||
<input type="text" id="message-input" placeholder="Votre message..."
|
||||
onkeypress="if(event.key==='Enter')sendMessage()">
|
||||
<button id="send-btn" onclick="sendMessage()">Envoyer</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const WEBHOOK_URL = '/turf/api/n8n-proxy';
|
||||
const CHAT_CONTAINER = document.getElementById('chat-container');
|
||||
const MESSAGE_INPUT = document.getElementById('message-input');
|
||||
const SEND_BTN = document.getElementById('send-btn');
|
||||
|
||||
function addMessage(role, content, isError = false) {
|
||||
const div = document.createElement('div');
|
||||
div.className = `message ${role}${isError ? ' error' : ''}`;
|
||||
div.innerHTML = `<div class="role">${role}</div><div class="content">${content}</div>`;
|
||||
CHAT_CONTAINER.appendChild(div);
|
||||
CHAT_CONTAINER.scrollTop = CHAT_CONTAINER.scrollHeight;
|
||||
}
|
||||
|
||||
function showLoader() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'loader';
|
||||
div.className = 'message system';
|
||||
div.innerHTML = `<div class="loader"><div class="dot"></div><div class="dot"></div><div class="dot"></div></div>`;
|
||||
CHAT_CONTAINER.appendChild(div);
|
||||
CHAT_CONTAINER.scrollTop = CHAT_CONTAINER.scrollHeight;
|
||||
}
|
||||
|
||||
function hideLoader() {
|
||||
const loader = document.getElementById('loader');
|
||||
if (loader) loader.remove();
|
||||
}
|
||||
|
||||
async function sendQuick(msg) {
|
||||
MESSAGE_INPUT.value = msg;
|
||||
sendMessage();
|
||||
}
|
||||
|
||||
async function sendMessage() {
|
||||
const msg = MESSAGE_INPUT.value.trim();
|
||||
if (!msg) return;
|
||||
|
||||
addMessage('user', msg);
|
||||
MESSAGE_INPUT.value = '';
|
||||
SEND_BTN.disabled = true;
|
||||
showLoader();
|
||||
|
||||
try {
|
||||
const response = await fetch(WEBHOOK_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
query: msg,
|
||||
timestamp: Date.now(),
|
||||
source: 'n8n-chat-web'
|
||||
})
|
||||
});
|
||||
|
||||
hideLoader();
|
||||
|
||||
try {
|
||||
const data = await response.json();
|
||||
let msg = data.message || 'Message envoyé';
|
||||
addMessage('system', msg);
|
||||
} catch(e) {
|
||||
const text = await response.text();
|
||||
addMessage('system', text || 'Message envoyé');
|
||||
}
|
||||
} catch (e) {
|
||||
hideLoader();
|
||||
addMessage('system', `Erreur de connexion: ${e.message}`, true);
|
||||
}
|
||||
|
||||
SEND_BTN.disabled = false;
|
||||
MESSAGE_INPUT.focus();
|
||||
}
|
||||
|
||||
// Test connection on load
|
||||
async function testConnection() {
|
||||
try {
|
||||
const resp = await fetch(WEBHOOK_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ query: 'ping', timestamp: Date.now() })
|
||||
});
|
||||
|
||||
document.getElementById('status-dot').classList.add('connected');
|
||||
document.getElementById('status-text').textContent = 'Connecté';
|
||||
} catch (e) {
|
||||
document.getElementById('status-text').textContent = 'Erreur: ' + e.message;
|
||||
}
|
||||
}
|
||||
|
||||
testConnection();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user