your_ai_insight 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,153 @@
1
+ <%# app/views/your_ai_insight/chat/_widget.html.erb %>
2
+ <%#
3
+ Drop-in floating chat assistant. Add to any layout before </body>:
4
+ <%= render "your_ai_insight/chat/widget" %>
5
+ %>
6
+
7
+ <div id="yai-root" style="position:fixed;bottom:24px;right:24px;z-index:10000;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;">
8
+
9
+ <%# ── Toggle button ─────────────────────────────────────────────── %>
10
+ <button id="yai-toggle"
11
+ onclick="yaiToggle()"
12
+ title="AI Facility Assistant"
13
+ style="width:56px;height:56px;border-radius:50%;background:#1a56db;color:#fff;border:none;
14
+ box-shadow:0 4px 16px rgba(26,86,219,.45);cursor:pointer;font-size:20px;
15
+ display:flex;align-items:center;justify-content:center;transition:transform .15s;
16
+ margin-left:auto;">
17
+ 💡
18
+ </button>
19
+
20
+ <%# ── Chat panel ─────────────────────────────────────────────────── %>
21
+ <div id="yai-panel"
22
+ style="display:none;position:absolute;bottom:68px;right:0;width:400px;
23
+ background:#fff;border-radius:16px;overflow:hidden;
24
+ box-shadow:0 8px 40px rgba(0,0,0,.18);flex-direction:column;">
25
+
26
+ <%# Header %>
27
+ <div style="background:linear-gradient(135deg,#1a56db,#1e40af);color:#fff;padding:16px 20px;display:flex;align-items:center;justify-content:space-between;">
28
+ <div>
29
+ <div style="font-size:15px;font-weight:600;">AI Facility Assistant</div>
30
+ <div style="font-size:11px;opacity:.75;margin-top:2px;"><%= YourAiInsight.config.company_name %> · Powered by Claude</div>
31
+ </div>
32
+ <button onclick="yaiToggle()" style="background:rgba(255,255,255,.15);border:none;color:#fff;width:28px;height:28px;border-radius:50%;cursor:pointer;font-size:14px;display:flex;align-items:center;justify-content:center;">✕</button>
33
+ </div>
34
+
35
+ <%# Messages %>
36
+ <div id="yai-messages" style="height:320px;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:10px;background:#f8fafc;">
37
+ <div class="yai-msg yai-bot">
38
+ Hello! I have live access to your facility data — jobs, tasks, bids, budgets, vendors, and more. What would you like to know?
39
+ </div>
40
+ </div>
41
+
42
+ <%# Quick prompts %>
43
+ <div id="yai-prompts" style="padding:10px 14px 6px;display:flex;flex-wrap:wrap;gap:6px;background:#fff;border-top:1px solid #e5e7eb;">
44
+ <button class="yai-chip" onclick="yaiSend(this.textContent)">Jobs on hold?</button>
45
+ <button class="yai-chip" onclick="yaiSend(this.textContent)">Overdue tasks?</button>
46
+ <button class="yai-chip" onclick="yaiSend(this.textContent)">Budget vs actual?</button>
47
+ <button class="yai-chip" onclick="yaiSend(this.textContent)">Pending bids?</button>
48
+ <button class="yai-chip" onclick="yaiSend(this.textContent)">Budget requests pending?</button>
49
+ </div>
50
+
51
+ <%# Input %>
52
+ <div style="display:flex;gap:8px;padding:10px 14px;background:#fff;">
53
+ <input id="yai-input"
54
+ type="text"
55
+ placeholder="Ask anything about your facility operations…"
56
+ onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();yaiSubmit();}"
57
+ style="flex:1;border:1.5px solid #d1d5db;border-radius:10px;padding:9px 14px;font-size:14px;
58
+ outline:none;transition:border-color .15s;background:#f9fafb;"
59
+ onfocus="this.style.borderColor='#1a56db'"
60
+ onblur="this.style.borderColor='#d1d5db'">
61
+ <button onclick="yaiSubmit()"
62
+ style="background:#1a56db;color:#fff;border:none;border-radius:10px;padding:9px 18px;
63
+ font-size:14px;cursor:pointer;font-weight:500;white-space:nowrap;transition:background .15s;"
64
+ onmouseover="this.style.background='#1e40af'"
65
+ onmouseout="this.style.background='#1a56db'">
66
+ Send
67
+ </button>
68
+ </div>
69
+ </div>
70
+ </div>
71
+
72
+ <style>
73
+ #yai-panel.open { display:flex !important; }
74
+ .yai-msg { max-width:88%;padding:10px 14px;border-radius:14px;font-size:13.5px;line-height:1.55;word-break:break-word; }
75
+ .yai-bot { background:#fff;color:#1e293b;align-self:flex-start;border-bottom-left-radius:4px;box-shadow:0 1px 3px rgba(0,0,0,.08); }
76
+ .yai-user { background:#1a56db;color:#fff;align-self:flex-end;border-bottom-right-radius:4px; }
77
+ .yai-loading { background:#fff;color:#6b7280;font-style:italic;align-self:flex-start;box-shadow:0 1px 3px rgba(0,0,0,.06); }
78
+ .yai-chip { background:#f0f4ff;color:#1a56db;border:1px solid #c7d7fd;border-radius:20px;
79
+ padding:4px 12px;font-size:12px;cursor:pointer;transition:background .1s; }
80
+ .yai-chip:hover { background:#dbeafe; }
81
+ #yai-messages::-webkit-scrollbar { width:4px; }
82
+ #yai-messages::-webkit-scrollbar-track { background:transparent; }
83
+ #yai-messages::-webkit-scrollbar-thumb { background:#d1d5db;border-radius:2px; }
84
+ .yai-bot strong { color:#1a56db; }
85
+ .yai-bot ul { margin:6px 0;padding-left:18px; }
86
+ .yai-bot li { margin:2px 0; }
87
+ </style>
88
+
89
+ <script>
90
+ (function() {
91
+ const ENDPOINT = "<%= your_ai_insight.chat_path %>";
92
+ const CSRF = (document.querySelector('meta[name="csrf-token"]') || {}).content || '';
93
+ let open = false;
94
+
95
+ window.yaiToggle = function() {
96
+ open = !open;
97
+ const panel = document.getElementById('yai-panel');
98
+ panel.classList.toggle('open', open);
99
+ if (open) document.getElementById('yai-input').focus();
100
+ };
101
+
102
+ window.yaiSend = function(text) {
103
+ document.getElementById('yai-input').value = text;
104
+ yaiSubmit();
105
+ };
106
+
107
+ window.yaiSubmit = function() {
108
+ const input = document.getElementById('yai-input');
109
+ const message = input.value.trim();
110
+ if (!message) return;
111
+
112
+ append(message, 'yai-user');
113
+ input.value = '';
114
+
115
+ // Hide quick prompts after first real message
116
+ const prompts = document.getElementById('yai-prompts');
117
+ if (prompts) prompts.style.display = 'none';
118
+
119
+ const loader = append('Analyzing your facility data…', 'yai-loading');
120
+
121
+ fetch(ENDPOINT, {
122
+ method: 'POST',
123
+ headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': CSRF },
124
+ body: JSON.stringify({ message })
125
+ })
126
+ .then(r => r.json())
127
+ .then(data => {
128
+ loader.remove();
129
+ append(data.response || data.error || 'Sorry, something went wrong.', 'yai-bot');
130
+ })
131
+ .catch(() => {
132
+ loader.remove();
133
+ append('Connection error. Please try again.', 'yai-bot');
134
+ });
135
+ };
136
+
137
+ function append(text, cls) {
138
+ const box = document.getElementById('yai-messages');
139
+ const div = document.createElement('div');
140
+ div.className = 'yai-msg ' + cls;
141
+ // Render markdown bold + bullets
142
+ div.innerHTML = text
143
+ .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
144
+ .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
145
+ .replace(/^- (.+)$/gm, '<li>$1</li>')
146
+ .replace(/(<li>.*<\/li>)/s, '<ul>$1</ul>')
147
+ .replace(/\n/g, '<br>');
148
+ box.appendChild(div);
149
+ box.scrollTop = box.scrollHeight;
150
+ return div;
151
+ }
152
+ })();
153
+ </script>