Chaitu2112 commited on
Commit
74d9449
·
1 Parent(s): 54473dc

Add project files (exclude local DBs, indexes and secrets)

Browse files
.gitignore ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # create/append .gitignore with safe patterns
2
+ @"
3
+ # Local env & secrets
4
+ .env
5
+ token.txt
6
+
7
+ # Chroma DBs / sqlite / index files
8
+ chroma_db/
9
+ chroma_digital_icfai/
10
+ *.sqlite3
11
+ *.sqlite
12
+
13
+ # Faiss / index / serialized binaries
14
+ faiss_index/
15
+ *.faiss
16
+ *.pkl
17
+ *.bin
18
+ *.npy
19
+
20
+ # Large data folders
21
+ data1/
22
+ data/
23
+ datasets/
24
+
25
+ # Python / venv / caches
26
+ venv/
27
+ __pycache__/
28
+ *.pyc
29
+
30
+ # OS files
31
+ .DS_Store
32
+ Thumbs.db
33
+ "@ | Out-File -FilePath .gitignore -Encoding utf8 -Append
README.md ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: IFHE Chatbot
3
+ emoji: 🧠
4
+ colorFrom: blue
5
+ colorTo: indigo
6
+ sdk: docker
7
+ app_file: app.py
8
+ pinned: false
9
+ ---
digitalicfai ADDED
@@ -0,0 +1 @@
 
 
1
+ Subproject commit 98aaebe3b19e2b55d9de2d3e8e08d804e654cef4
llama_api.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # import ollama
2
+
3
+ # # Synchronous ask (kept for caching or non-stream calls)
4
+ # def ask_ollama(prompt: str, model_name: str = "llama3"):
5
+ # response = ollama.chat(
6
+ # model=model_name,
7
+ # messages=[
8
+ # {"role": "system", "content": "You are a helpful assistant for college queries."},
9
+ # {"role": "user", "content": prompt}
10
+ # ]
11
+ # )
12
+ # return response.get("message", {}).get("content", "")
13
+
14
+ # # Streaming generator: yields incremental text chunks
15
+ # def ask_ollama_stream(prompt: str, model_name: str = "llama3"):
16
+ # stream = ollama.chat(
17
+ # model=model_name,
18
+ # messages=[
19
+ # {"role": "system", "content": "You are a helpful assistant for college queries."},
20
+ # {"role": "user", "content": prompt}
21
+ # ],
22
+ # stream=True
23
+ # )
24
+ # buffer = ""
25
+ # for chunk in stream:
26
+ # # chunk may contain partial content; combine
27
+ # text = chunk.get("message", {}).get("content", "")
28
+ # if text:
29
+ # # yield incremental text (could be full or partial)
30
+ # yield text
31
+
32
+ from dotenv import load_dotenv
33
+ import os
34
+ from openai import OpenAI
35
+
36
+ # Load environment variables
37
+ load_dotenv()
38
+
39
+ OPENROUTER_KEY = os.getenv("OPENROUTER_API_KEY")
40
+
41
+ if not OPENROUTER_KEY:
42
+ raise ValueError("❌ Missing OPENROUTER_API_KEY in .env")
43
+
44
+ print("Loaded key prefix:", OPENROUTER_KEY[:15])
45
+
46
+ # Initialize OpenRouter API client
47
+ client = OpenAI(
48
+ base_url="https://openrouter.ai/api/v1",
49
+ api_key=OPENROUTER_KEY,
50
+ )
51
+
52
+
53
+ # 🧠 Non-streaming function
54
+ def ask_ollama(prompt: str, model_name: str = "meta-llama/llama-3.3-8b-instruct:free"):
55
+ """
56
+ Sends a prompt to OpenRouter (Llama-3.3-8B-Instruct) and returns the response text.
57
+ Handles missing fields, errors, and empty responses gracefully.
58
+ """
59
+ try:
60
+ print(f"🚀 Sending request to OpenRouter model: {model_name}")
61
+ completion = client.chat.completions.create(
62
+ extra_headers={
63
+ "HTTP-Referer": "https://ifheindia.org",
64
+ "X-Title": "IFHE Chatbot",
65
+ },
66
+ model=model_name,
67
+ messages=[
68
+ {"role": "system", "content": "You are a helpful academic assistant for IFHE University. Quote only factual content from context."},
69
+ {"role": "user", "content": prompt},
70
+ ],
71
+ )
72
+
73
+ # --- Safe parsing ---
74
+ if not hasattr(completion, "choices") or not completion.choices:
75
+ print("⚠️ No choices returned from OpenRouter.")
76
+ return "⚠️ No valid response received from the model."
77
+
78
+ message = getattr(completion.choices[0].message, "content", None)
79
+ if not message or not message.strip():
80
+ print("⚠️ Empty message content in completion.")
81
+ return "⚠️ The model did not return any text."
82
+
83
+ print("🧠 Model raw response:", message[:250])
84
+ return message.strip()
85
+
86
+ except Exception as e:
87
+ print("❌ OpenRouter / Llama API Error:", e)
88
+ return f"⚠️ Error communicating with the model: {e}"
89
+
90
+
91
+
92
+ def ask_ollama_stream(prompt: str, model_name: str = "meta-llama/llama-3.3-8b-instruct:free"):
93
+ """
94
+ Streams response token-by-token for real-time output.
95
+ Includes detailed logging for debugging.
96
+ """
97
+ try:
98
+ print(f"🚀 Connecting to OpenRouter model: {model_name}")
99
+ stream = client.chat.completions.create(
100
+ extra_headers={
101
+ "HTTP-Referer": "https://ifheindia.org",
102
+ "X-Title": "IFHE Chatbot",
103
+ },
104
+ model=model_name,
105
+ messages=[
106
+ {"role": "system", "content": "You are a helpful academic assistant for IFHE University."},
107
+ {"role": "user", "content": prompt},
108
+ ],
109
+ stream=True,
110
+ )
111
+
112
+ for chunk in stream:
113
+ # Log structure of each chunk
114
+ print(f"📦 Chunk received: {chunk}")
115
+ if hasattr(chunk.choices[0].delta, "content"):
116
+ text = chunk.choices[0].delta.content
117
+ if text:
118
+ print(f"🧩 Token: {text!r}")
119
+ yield text
120
+
121
+ print("✅ Streaming complete.")
122
+
123
+ except Exception as e:
124
+ print("⚠️ Streaming error (inside llama_api):", e)
125
+ yield f"⚠️ Error while streaming: {str(e)}"
126
+
static/chat.js ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const chatBox = document.getElementById("chat-box");
2
+ const chatForm = document.getElementById("chat-form");
3
+ const chatInput = document.getElementById("chat-input");
4
+
5
+ // Add message bubbles to chat
6
+ function addMessage(role, text) {
7
+ const msg = document.createElement("div");
8
+ msg.className = `chat-message ${role}-message`;
9
+
10
+ // Convert Markdown to HTML using marked.js
11
+ try {
12
+ msg.innerHTML = marked.parse(text);
13
+ } catch (err) {
14
+ console.error("Markdown parse error:", err);
15
+ msg.textContent = text; // fallback
16
+ }
17
+
18
+ chatBox.appendChild(msg);
19
+ chatBox.scrollTop = chatBox.scrollHeight;
20
+ }
21
+
22
+ // Simulated typing animation
23
+ function showTypingIndicator() {
24
+ const indicator = document.createElement("div");
25
+ indicator.className = "chat-message bot-message typing";
26
+ indicator.textContent = "Assistant is typing...";
27
+ chatBox.appendChild(indicator);
28
+ chatBox.scrollTop = chatBox.scrollHeight;
29
+ }
30
+
31
+ function removeTypingIndicator() {
32
+ const indicator = chatBox.querySelector(".typing");
33
+ if (indicator) indicator.remove();
34
+ }
35
+
36
+ // Send message to backend
37
+ chatForm.addEventListener("submit", async (e) => {
38
+ e.preventDefault();
39
+ const userMessage = chatInput.value.trim();
40
+ if (!userMessage) return;
41
+
42
+ addMessage("user", userMessage);
43
+ chatInput.value = "";
44
+
45
+ showTypingIndicator();
46
+
47
+ try {
48
+ const res = await fetch("/chat", {
49
+ method: "POST",
50
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
51
+ body: new URLSearchParams({ user_message: userMessage }),
52
+ });
53
+ const data = await res.json();
54
+
55
+ removeTypingIndicator();
56
+ addMessage("bot", data.answer || "⚠️ No response received.");
57
+ } catch (err) {
58
+ removeTypingIndicator();
59
+ addMessage("bot", "⚠️ Error connecting to server.");
60
+ console.error(err);
61
+ }
62
+ });
static/chat_digital.js ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ const chatBox = document.getElementById("chat-box");
3
+ const chatForm = document.getElementById("chat-form");
4
+ const chatInput = document.getElementById("chat-input");
5
+
6
+
7
+ function addMessage(role, text) {
8
+ const msg = document.createElement("div");
9
+ msg.className = `chat-message ${role}-message`;
10
+
11
+ try {
12
+ msg.innerHTML = marked.parse(text);
13
+ } catch {
14
+ msg.textContent = text;
15
+ }
16
+
17
+ chatBox.appendChild(msg);
18
+ chatBox.scrollTop = chatBox.scrollHeight;
19
+ }
20
+
21
+
22
+ function showTyping() {
23
+ const tip = document.createElement("div");
24
+ tip.className = "chat-message bot-message typing";
25
+ tip.textContent = "Assistant is typing";
26
+ chatBox.appendChild(tip);
27
+ chatBox.scrollTop = chatBox.scrollHeight;
28
+ }
29
+
30
+ function removeTyping() {
31
+ const t = chatBox.querySelector(".typing");
32
+ if (t) t.remove();
33
+ }
34
+
35
+ function sleep(ms) {
36
+ return new Promise(resolve => setTimeout(resolve, ms));
37
+ }
38
+
39
+
40
+ chatInput.addEventListener("keydown", function (e) {
41
+ if (e.key === "Enter" && !e.shiftKey) {
42
+ e.preventDefault();
43
+ chatForm.dispatchEvent(new Event("submit"));
44
+ }
45
+ });
46
+
47
+
48
+ chatForm.addEventListener("submit", async (e) => {
49
+ e.preventDefault();
50
+ const userMsg = chatInput.value.trim();
51
+ if (!userMsg) return;
52
+
53
+ addMessage("user", userMsg);
54
+ chatInput.value = "";
55
+ showTyping();
56
+
57
+ try {
58
+ const res = await fetch("/digital_icfai_chat", {
59
+ method: "POST",
60
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
61
+ body: new URLSearchParams({ user_message: userMsg }),
62
+ });
63
+
64
+ const data = await res.json();
65
+ await sleep(400);
66
+ removeTyping();
67
+ addMessage("bot", data.answer || "⚠️ No response received.");
68
+ } catch (err) {
69
+ removeTyping();
70
+ addMessage("bot", "⚠️ Error connecting to server.");
71
+ console.error(err);
72
+ }
73
+ });
static/courses.js ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const courseForm = document.getElementById("course-form");
2
+ const courseResults = document.getElementById("course-results");
3
+
4
+ courseForm.addEventListener("submit", async (e) => {
5
+ e.preventDefault();
6
+
7
+ courseResults.innerHTML = "<p><em>Generating recommendations...</em></p>";
8
+
9
+ const formData = new FormData(courseForm);
10
+
11
+ try {
12
+ const res = await fetch("/recommend", { method: "POST", body: formData });
13
+ const data = await res.json();
14
+
15
+ if (data.recommendations && data.recommendations.length > 0) {
16
+ courseResults.innerHTML = `
17
+ <h3>Recommended Courses:</h3>
18
+ <ul>
19
+ ${data.recommendations.map(c => `<li>${c}</li>`).join("")}
20
+ </ul>
21
+ `;
22
+ } else {
23
+ courseResults.innerHTML = "<p>No matching courses found.</p>";
24
+ }
25
+ } catch (err) {
26
+ courseResults.innerHTML = "<p>⚠️ Error fetching recommendations.</p>";
27
+ console.error(err);
28
+ }
29
+ });
static/employees.js ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.getElementById("employee-form").addEventListener("submit", async (e) => {
2
+ e.preventDefault();
3
+ const formData = new FormData(e.target);
4
+ const resultsDiv = document.getElementById("employee-results");
5
+
6
+ resultsDiv.innerHTML = "⏳ Searching employee data...";
7
+
8
+ try {
9
+ const response = await fetch("/employees/search", {
10
+ method: "POST",
11
+ body: formData,
12
+ });
13
+
14
+ const data = await response.json();
15
+ resultsDiv.innerHTML = "";
16
+
17
+ if (data.error) {
18
+ resultsDiv.innerHTML = `<p style="color:red;">⚠️ ${data.error}</p>`;
19
+ return;
20
+ }
21
+
22
+ if (!data.results || data.results.length === 0) {
23
+ resultsDiv.innerHTML = "<p style='color:red;'>No data found.</p>";
24
+ return;
25
+ }
26
+
27
+ // Build a clean table for results
28
+ const table = document.createElement("table");
29
+ table.classList.add("results-table");
30
+
31
+ // Table header
32
+ const headers = Object.keys(data.results[0]);
33
+ const thead = document.createElement("thead");
34
+ const headerRow = document.createElement("tr");
35
+ headers.forEach((key) => {
36
+ const th = document.createElement("th");
37
+ th.textContent = key;
38
+ headerRow.appendChild(th);
39
+ });
40
+ thead.appendChild(headerRow);
41
+ table.appendChild(thead);
42
+
43
+ // Table body
44
+ const tbody = document.createElement("tbody");
45
+ data.results.forEach((row) => {
46
+ const tr = document.createElement("tr");
47
+ headers.forEach((key) => {
48
+ const td = document.createElement("td");
49
+ td.textContent = row[key];
50
+ tr.appendChild(td);
51
+ });
52
+ tbody.appendChild(tr);
53
+ });
54
+ table.appendChild(tbody);
55
+
56
+ resultsDiv.appendChild(table);
57
+
58
+ } catch (err) {
59
+ console.error(err);
60
+ resultsDiv.innerHTML = "⚠️ Error fetching employee data.";
61
+ }
62
+ });
static/image.jpeg ADDED
static/logo.png ADDED
static/style.css ADDED
@@ -0,0 +1,698 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* ==========================
2
+ IFHE Assistant UI Styling
3
+ ========================== */
4
+
5
+ /* --- Google Fonts --- */
6
+ @import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@500;600;700&family=Poppins:wght@400;500;600&display=swap');
7
+
8
+ /* --- Global Variables --- */
9
+ :root {
10
+ --primary: #1b3a7a; /* Deep IFHE blue */
11
+ --accent: #274b9f; /* Bright blue */
12
+ --highlight: #ffcc00; /* Gold accent */
13
+ --card-bg: #ffffff;
14
+ --light-bg: #f4f8fc;
15
+ --text-dark: #0c1c3a;
16
+ --radius: 12px;
17
+ --shadow: 0 4px 14px rgba(0, 0, 0, 0.1);
18
+ }
19
+
20
+ /* --- Global Reset --- */
21
+ * {
22
+ box-sizing: border-box;
23
+ }
24
+
25
+ html, body {
26
+ overflow-x: hidden;
27
+ margin: 0;
28
+ padding: 0;
29
+ width: 100%;
30
+ font-family: "Poppins", "Segoe UI", Arial, sans-serif;
31
+ background: var(--light-bg);
32
+ color: var(--text-dark);
33
+ line-height: 1.6;
34
+ }
35
+
36
+ /* ==========================
37
+ HEADER (Centered IFHE Assistant)
38
+ ========================== */
39
+ header {
40
+ position: sticky;
41
+ top: 0;
42
+ width: 100%;
43
+ display: flex;
44
+ justify-content: space-between;
45
+ align-items: center;
46
+ background: none;
47
+ background-color: transparent;
48
+ color: #fff;
49
+ padding: 0.2rem 1rem;
50
+ box-shadow: none;
51
+ backdrop-filter: blur(6px);
52
+ z-index: 1000;
53
+ box-sizing: border-box;
54
+ }
55
+
56
+ /* Centered Title */
57
+ .brand-title {
58
+ flex: 1;
59
+ text-align: center;
60
+ font-family: "Montserrat", "Poppins", sans-serif;
61
+ font-weight: 700;
62
+ font-size: 1.4rem;
63
+ letter-spacing: 0.5px;
64
+ color: #ffffff;
65
+ margin: 0;
66
+ text-transform: uppercase;
67
+ text-shadow: 0 2px 10px rgba(0, 0, 0, 0.25);
68
+ }
69
+
70
+ /* Left & Right Containers for layout balance */
71
+ .nav-left {
72
+ flex: 0.2;
73
+ }
74
+
75
+ .nav-right {
76
+ flex: 0.2;
77
+ display: flex;
78
+ justify-content: flex-end;
79
+ align-items: center;
80
+ }
81
+
82
+ /* ==========================
83
+ HAMBURGER DROPDOWN MENU
84
+ ========================== */
85
+ .dropdown {
86
+ position: relative;
87
+ display: inline-block;
88
+ }
89
+
90
+ .dropbtn {
91
+ background: none;
92
+ border: none;
93
+ color: #fff;
94
+ font-size: 24px;
95
+ cursor: pointer;
96
+ padding: 10px 12px;
97
+ display: flex;
98
+ align-items: center;
99
+ justify-content: center;
100
+ transition: color 0.3s ease;
101
+ }
102
+
103
+ .dropbtn:hover {
104
+ color: var(--highlight);
105
+ }
106
+
107
+ .menu-icon {
108
+ font-size: 26px;
109
+ line-height: 1;
110
+ }
111
+
112
+ /* Dropdown content styling */
113
+ .dropdown-content {
114
+ display: none;
115
+ position: absolute;
116
+ right: 0;
117
+ top: 48px; /* perfectly below icon */
118
+ background-color: #fff;
119
+ border-radius: 8px;
120
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);
121
+ overflow: hidden;
122
+ z-index: 1000;
123
+ min-width: 220px;
124
+ }
125
+
126
+ /* Show dropdown on hover */
127
+ .dropdown:hover .dropdown-content {
128
+ display: block;
129
+ }
130
+
131
+ /* Dropdown links */
132
+ .dropdown-content a {
133
+ display: block;
134
+ color: var(--primary);
135
+ text-decoration: none;
136
+ font-weight: 500;
137
+ padding: 12px 16px;
138
+ transition: background 0.2s ease, color 0.2s ease;
139
+ }
140
+
141
+ .dropdown-content a:hover {
142
+ background-color: #f1f5ff;
143
+ color: var(--accent);
144
+ }
145
+
146
+ /* ==========================
147
+ MAIN LAYOUT
148
+ ========================== */
149
+ main {
150
+ max-width: 1200px;
151
+ margin: 0 auto;
152
+ padding: 30px 20px;
153
+ }
154
+
155
+ section {
156
+ margin-top: 15px;
157
+ margin-bottom: 10px;
158
+ }
159
+
160
+ /* ==========================
161
+ CHATBOT SECTION
162
+ ========================== */
163
+ .chat-section {
164
+ background: var(--card-bg);
165
+ border-radius: var(--radius);
166
+ box-shadow: var(--shadow);
167
+ padding: 25px;
168
+ display: flex;
169
+ flex-direction: column;
170
+ align-items: center;
171
+ justify-content: center;
172
+ margin-top: 10px;
173
+ }
174
+
175
+ .chat-container {
176
+ width: 100%;
177
+ max-width: 900px;
178
+ height: calc(100vh - 160px);
179
+ display: flex;
180
+ flex-direction: column;
181
+ justify-content: space-between;
182
+ background: #f9fbff;
183
+ border-radius: var(--radius);
184
+ box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.08);
185
+ padding: 20px 22px;
186
+ margin: 0 auto;
187
+ }
188
+
189
+ .chat-box {
190
+ flex: 1;
191
+ overflow-y: auto;
192
+ display: flex;
193
+ flex-direction: column;
194
+ gap: 12px;
195
+ padding: 10px;
196
+ text-align: left;
197
+ scroll-behavior: smooth;
198
+ margin-bottom: 10px;
199
+ }
200
+
201
+ .user-message {
202
+ align-self: flex-end;
203
+ background: var(--accent);
204
+ color: white;
205
+ border-radius: 12px 12px 0 12px;
206
+ padding: 10px 14px;
207
+ max-width: 75%;
208
+ text-align: left;
209
+ line-height: 1.6;
210
+ }
211
+
212
+ .bot-message {
213
+ align-self: flex-start;
214
+ background: #e7eefb;
215
+ color: var(--primary);
216
+ border-radius: 12px 12px 12px 0;
217
+ padding: 10px 14px;
218
+ max-width: 75%;
219
+ text-align: left;
220
+ line-height: 1.6;
221
+ }
222
+
223
+ /* Input Form */
224
+ .chat-form {
225
+ display: flex;
226
+ justify-content: center;
227
+ align-items: center;
228
+ gap: 10px;
229
+ background: #f8f9fb;
230
+ border-radius: 10px;
231
+ box-shadow: inset 0 0 4px rgba(0, 0, 0, 0.05);
232
+ padding: 10px 12px;
233
+ }
234
+
235
+ .chat-form textarea {
236
+ flex: 1;
237
+ width: 70%;
238
+ max-width: 700px;
239
+ min-height: 45px;
240
+ border: 1px solid #ccc;
241
+ border-radius: 8px;
242
+ padding: 10px 12px;
243
+ font-size: 15px;
244
+ resize: none;
245
+ text-align: left;
246
+ background: #fff;
247
+ }
248
+
249
+ .chat-form textarea:focus {
250
+ border-color: var(--accent);
251
+ outline: none;
252
+ box-shadow: 0 0 4px rgba(27, 58, 122, 0.3);
253
+ }
254
+
255
+ .chat-form button {
256
+ height: 45px;
257
+ padding: 0 20px;
258
+ background: var(--primary);
259
+ color: white;
260
+ border: none;
261
+ border-radius: 8px;
262
+ cursor: pointer;
263
+ font-weight: 600;
264
+ font-size: 15px;
265
+ white-space: nowrap;
266
+ transition: all 0.25s ease;
267
+ box-shadow: 0 3px 8px rgba(0, 0, 0, 0.15);
268
+ }
269
+
270
+ .chat-form button:hover {
271
+ background: var(--accent);
272
+ transform: translateY(-2px);
273
+ }
274
+
275
+ html, body {
276
+ height: 100%;
277
+ overflow: hidden;
278
+ }
279
+
280
+ /* ==========================
281
+ COURSE & EMPLOYEE FORMS
282
+ ========================== */
283
+ .form-section {
284
+ background: var(--card-bg);
285
+ border-radius: var(--radius);
286
+ box-shadow: var(--shadow);
287
+ padding: 2rem;
288
+ max-width: 850px;
289
+ margin: 0 auto;
290
+ }
291
+
292
+ .form-section form {
293
+ display: flex;
294
+ flex-direction: column;
295
+ gap: 1rem;
296
+ }
297
+
298
+ .form-section label {
299
+ font-weight: 600;
300
+ color: var(--primary);
301
+ }
302
+
303
+ form input,
304
+ form select {
305
+ padding: 0.6rem 0.8rem;
306
+ border-radius: 8px;
307
+ border: 1px solid #ccc;
308
+ font-size: 1rem;
309
+ }
310
+
311
+ form input:focus,
312
+ form select:focus {
313
+ border-color: var(--accent);
314
+ outline: none;
315
+ box-shadow: 0 0 4px rgba(27, 58, 122, 0.3);
316
+ }
317
+
318
+ form button {
319
+ background-color: var(--accent);
320
+ color: white;
321
+ border: none;
322
+ border-radius: 8px;
323
+ padding: 10px 16px;
324
+ font-weight: 600;
325
+ cursor: pointer;
326
+ transition: all 0.25s ease;
327
+ }
328
+
329
+ form button:hover {
330
+ background-color: var(--primary);
331
+ transform: translateY(-2px);
332
+ }
333
+
334
+ /* ==========================
335
+ RESULTS BOX
336
+ ========================== */
337
+ .results {
338
+ margin-top: 1.5rem;
339
+ background: #f1f5ff;
340
+ border-radius: var(--radius);
341
+ padding: 1rem;
342
+ box-shadow: var(--shadow);
343
+ }
344
+
345
+ /* ==========================
346
+ FOOTER
347
+ ========================== */
348
+ footer {
349
+ text-align: center;
350
+ padding: 0.35rem 0;
351
+ font-size: 0.8rem;
352
+ color: var(--text-dark);
353
+ background: transparent;
354
+ margin-top: 5px;
355
+ box-shadow: none;
356
+ }
357
+
358
+ /* ==========================
359
+ RESPONSIVE
360
+ ========================== */
361
+ @media (max-width: 768px) {
362
+ header {
363
+ flex-direction: column;
364
+ padding: 1rem;
365
+ }
366
+
367
+ .brand-title {
368
+ font-size: 1.5rem;
369
+ margin-bottom: 10px;
370
+ }
371
+
372
+ .chat-form {
373
+ flex-direction: column;
374
+ }
375
+
376
+ .chat-form button {
377
+ width: 100%;
378
+ }
379
+ }
380
+
381
+ /* === Dynamic height adjustment for smaller screens === */
382
+ @media (max-height: 800px) {
383
+ .chat-container {
384
+ height: 82vh;
385
+ }
386
+ section {
387
+ margin-top: 20px;
388
+ margin-bottom: 20px;
389
+ }
390
+ }
391
+
392
+ /* ==========================
393
+ EMPLOYEE LOOKUP TABLE STYLING
394
+ ========================== */
395
+ .results {
396
+ background: #f9fbff;
397
+ padding: 15px;
398
+ margin-top: 25px;
399
+ border-radius: var(--radius);
400
+ box-shadow: var(--shadow);
401
+ overflow-x: auto;
402
+ }
403
+
404
+ /* Table structure */
405
+ .results table {
406
+ width: 100%;
407
+ border-collapse: collapse;
408
+ margin-top: 10px;
409
+ background: #fff;
410
+ border-radius: var(--radius);
411
+ overflow: hidden;
412
+ }
413
+
414
+ /* Header row */
415
+ .results th {
416
+ background: var(--accent);
417
+ color: #fff;
418
+ text-align: left;
419
+ padding: 10px 12px;
420
+ font-weight: 600;
421
+ text-transform: capitalize;
422
+ border-bottom: 2px solid #e0e0e0;
423
+ }
424
+
425
+ /* Table rows */
426
+ .results td {
427
+ padding: 10px 12px;
428
+ border-bottom: 1px solid #eaeaea;
429
+ color: var(--text-dark);
430
+ }
431
+
432
+ /* Hover effect */
433
+ .results tr:hover {
434
+ background: #f4f8ff;
435
+ transition: 0.2s ease;
436
+ }
437
+
438
+ /* Table scroll fix for small screens */
439
+ @media (max-width: 768px) {
440
+ .results table {
441
+ display: block;
442
+ overflow-x: auto;
443
+ white-space: nowrap;
444
+ }
445
+ }
446
+
447
+ /* ==========================
448
+ FINAL — Perfect Page Fit (Header, Chat, Footer Visible)
449
+ ========================== */
450
+
451
+ html, body {
452
+ height: 100%;
453
+ margin: 0;
454
+ padding: 0;
455
+ overflow: hidden; /* prevent page scroll */
456
+ display: flex;
457
+ flex-direction: column;
458
+ }
459
+
460
+ /* Keep header and footer in natural flow */
461
+ header {
462
+ flex-shrink: 0;
463
+ }
464
+
465
+ footer {
466
+ flex-shrink: 0;
467
+ }
468
+
469
+ /* Main fills the space between header and footer */
470
+ main.content {
471
+ flex: 1;
472
+ display: flex;
473
+ flex-direction: column;
474
+ align-items: center;
475
+ justify-content: center;
476
+ overflow: hidden;
477
+ padding: 0;
478
+ margin: 0;
479
+ }
480
+
481
+ /* Center chat section properly */
482
+ .chat-section {
483
+ width: 100%;
484
+ flex: 1;
485
+ display: flex;
486
+ justify-content: center;
487
+ align-items: center;
488
+ padding: 0;
489
+ margin: 0;
490
+ }
491
+
492
+ /* Keep the same size and styling */
493
+ .chat-container {
494
+ width: 100%;
495
+ max-width: 900px;
496
+ height: calc(100vh - 140px); /* fits exactly with header+footer */
497
+ display: flex;
498
+ flex-direction: column;
499
+ justify-content: space-between;
500
+ background: #f9fbff;
501
+ border-radius: var(--radius);
502
+ box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.08);
503
+ padding: 20px 22px;
504
+ margin: 0 auto;
505
+ }
506
+
507
+ /* Only the chat box scrolls */
508
+ .chat-box {
509
+ flex: 1;
510
+ overflow-y: auto;
511
+ display: flex;
512
+ flex-direction: column;
513
+ gap: 12px;
514
+ padding: 10px;
515
+ text-align: left;
516
+ scroll-behavior: smooth;
517
+ margin-bottom: 10px;
518
+ }
519
+
520
+ /* ==========================
521
+ FINAL — Keep Header/Footer Same, Center Chat Perfectly
522
+ ========================== */
523
+
524
+ main.content {
525
+ flex: 1;
526
+ display: flex;
527
+ align-items: center; /* ✅ vertically center */
528
+ justify-content: center; /* ✅ horizontally center */
529
+ padding: 10px 0;
530
+ margin: 0 auto;
531
+ overflow: hidden;
532
+ width: 100%;
533
+ }
534
+
535
+ /* Chat section centered within main */
536
+ .chat-section {
537
+ width: 100%;
538
+ max-width: 1200px;
539
+ display: flex;
540
+ justify-content: center; /* ✅ horizontally center */
541
+ align-items: center; /* ✅ vertically center */
542
+ margin: 10px 0;
543
+ padding: 0;
544
+ }
545
+
546
+ /* Chat container remains same style but centered perfectly */
547
+ .chat-container {
548
+ width: 100%;
549
+ max-width: 900px;
550
+ height: calc(100vh - 180px); /* fits between header & footer */
551
+ display: flex;
552
+ flex-direction: column;
553
+ justify-content: space-between;
554
+ background: #f9fbff;
555
+ border-radius: var(--radius);
556
+ box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.08);
557
+ padding: 20px 22px;
558
+ margin: 0 auto; /* ✅ ensures it's perfectly centered horizontally */
559
+ }
560
+ .form-section {
561
+ max-height: calc(100vh - 180px); /* fits between header & footer */
562
+ overflow-y: auto; /* enable scroll inside box */
563
+ scrollbar-width: thin; /* Firefox */
564
+ scrollbar-color: var(--accent) #f1f5ff;
565
+ }
566
+
567
+ /* Employee results box scroll */
568
+ .results {
569
+ max-height: calc(100vh - 180px); /* fits between header & footer */
570
+ overflow-y: auto; /* enable scroll inside box */
571
+ scrollbar-width: thin;
572
+ scrollbar-color: var(--accent) #f1f5ff;
573
+ }
574
+
575
+ .apply-btn {
576
+ margin-top: 1rem;
577
+ background: var(--primary);
578
+ color: white;
579
+ border: none;
580
+ border-radius: 8px;
581
+ padding: 10px 18px;
582
+ font-size: 15px;
583
+ font-weight: 600;
584
+ cursor: pointer;
585
+ transition: all 0.25s ease;
586
+ box-shadow: 0 3px 8px rgba(0, 0, 0, 0.15);
587
+ }
588
+
589
+ .apply-btn:hover {
590
+ background: var(--accent);
591
+ transform: translateY(-2px);
592
+ }
593
+ .brand-container {
594
+ display: flex;
595
+ align-items: center;
596
+ gap: 10px; /* space between logo and title */
597
+ }
598
+
599
+ .brand-logo {
600
+ height: 50px; /* adjust size as needed */
601
+ width: auto;
602
+ }
603
+
604
+ .brand-title {
605
+ font-size: 1.8rem;
606
+ font-weight: bold;
607
+ color: #003366;
608
+ margin: 0;
609
+ }
610
+
611
+ .brand-highlight {
612
+ color: #00509e;
613
+ }
614
+ .menu-icon {
615
+ color: #003366; /* IFHE blue shade */
616
+ }
617
+ /* ==========================
618
+ CHAT UI ANIMATIONS & EFFECTS
619
+ ========================== */
620
+
621
+ /* Smooth fade-in for new messages */
622
+ .chat-message {
623
+ animation: fadeIn 0.3s ease-in-out;
624
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
625
+ }
626
+
627
+ @keyframes fadeIn {
628
+ from { opacity: 0; transform: translateY(5px); }
629
+ to { opacity: 1; transform: translateY(0); }
630
+ }
631
+
632
+ /* Subtle pop effect on hover */
633
+ .chat-message:hover {
634
+ transform: scale(1.02);
635
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
636
+ }
637
+
638
+ /* Soft gradient background for user & bot */
639
+ .user-message {
640
+ background: linear-gradient(135deg, #1b3a7a, #274b9f);
641
+ color: #fff;
642
+ border-radius: 16px 16px 0 16px;
643
+ box-shadow: 0 2px 8px rgba(27, 58, 122, 0.25);
644
+ }
645
+
646
+ .bot-message {
647
+ background: linear-gradient(135deg, #eaf0ff, #f8faff);
648
+ color: #1b3a7a;
649
+ border-radius: 16px 16px 16px 0;
650
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
651
+ }
652
+
653
+ /* Typing indicator animation */
654
+ .typing::after {
655
+ content: " ";
656
+ display: inline-block;
657
+ width: 0.7em;
658
+ text-align: left;
659
+ animation: dots 1s steps(3, end) infinite;
660
+ }
661
+
662
+ @keyframes dots {
663
+ 0%, 20% { content: ""; }
664
+ 40% { content: "."; }
665
+ 60% { content: ".."; }
666
+ 80%, 100% { content: "..."; }
667
+ }
668
+
669
+ /* Chat input glow when focused */
670
+ .chat-form textarea:focus {
671
+ border-color: #0074d9;
672
+ box-shadow: 0 0 8px rgba(0, 116, 217, 0.4);
673
+ transition: all 0.3s ease;
674
+ }
675
+
676
+ /* Button hover animation */
677
+ .chat-form button {
678
+ position: relative;
679
+ overflow: hidden;
680
+ transition: background 0.3s ease, transform 0.2s ease;
681
+ }
682
+
683
+ .chat-form button::after {
684
+ content: "";
685
+ position: absolute;
686
+ left: 0; top: 0;
687
+ width: 100%; height: 100%;
688
+ background: rgba(255, 255, 255, 0.15);
689
+ opacity: 0;
690
+ transition: opacity 0.3s ease;
691
+ }
692
+
693
+ .chat-form button:hover::after {
694
+ opacity: 1;
695
+ }
696
+ .chat-section {
697
+ background: linear-gradient(180deg, #f4f8fc 0%, #ffffff 100%);
698
+ }
templates/base.html ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
6
+ <title>IFHE Assistant</title>
7
+ <link rel="stylesheet" href="/static/style.css">
8
+ </head>
9
+ <body>
10
+ <header>
11
+ <div class="nav-left"></div>
12
+
13
+ <div class="brand-container">
14
+ <img src="/static/image.jpeg" alt="IFHE Logo" class="brand-logo">
15
+ <h1 class="brand-title">
16
+ <span class="brand-highlight">DIGITAL </span> ICFAI
17
+ </h1>
18
+ </div>
19
+
20
+
21
+ <nav class="nav-right">
22
+ <ul>
23
+ <div class="dropdown">
24
+ <button class="dropbtn"><span class="menu-icon">☰</span></button>
25
+ <div class="dropdown-content">
26
+ <a href="/chat">Chatbot</a>
27
+ <a href="/courses">Course Recommender</a>
28
+ <a href="/employees">Employee Lookup</a>
29
+ </div>
30
+ </div>
31
+
32
+ </ul>
33
+ </nav>
34
+ </header>
35
+
36
+
37
+ <main class="content">
38
+ {% block content %}{% endblock %}
39
+ </main>
40
+
41
+ <footer class="footer">
42
+ <p>© 2025 ICFAI University | Powered by CIT</p>
43
+ </footer>
44
+ </body>
45
+ </html>
templates/base_digital.html ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
6
+ <title>Digital ICFAI Assistant</title>
7
+ <link rel="stylesheet" href="/static/style.css">
8
+ <style>
9
+ header {
10
+ display: flex;
11
+ align-items: center;
12
+ justify-content: center;
13
+ padding: 12px 24px;
14
+ background: linear-gradient(to right, #0074d9, #003366);
15
+ border: none;
16
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
17
+ }
18
+
19
+ .brand-container {
20
+ display: flex;
21
+ align-items: center;
22
+ justify-content: center;
23
+ gap: 10px;
24
+ }
25
+
26
+ .brand-logo {
27
+ height: 40px;
28
+ width: auto;
29
+ border-radius: 50%;
30
+ box-shadow: 0 0 6px rgba(0,0,0,0.15);
31
+ }
32
+
33
+ .brand-title {
34
+ font-size: 1.6rem;
35
+ font-weight: 700;
36
+ color: #ffffff;
37
+ letter-spacing: 0.5px;
38
+ text-transform: uppercase;
39
+ font-family: "Montserrat", "Poppins", sans-serif;
40
+ }
41
+
42
+ .brand-highlight {
43
+ color: #ffffff;
44
+ text-shadow: 0 1px 2px rgba(0,0,0,0.25);
45
+ }
46
+
47
+ footer.footer {
48
+ text-align: center;
49
+ font-size: 0.78rem;
50
+ color: rgba(0, 0, 0, 0.55);
51
+ padding: 3px 0;
52
+ border-top: 1px solid rgba(0, 0, 0, 0.04);
53
+ background: rgba(255, 255, 255, 0.85);
54
+ backdrop-filter: blur(6px);
55
+ }
56
+ </style>
57
+
58
+ </head>
59
+
60
+ <body>
61
+
62
+ <header>
63
+ <div class="brand-container">
64
+ <img src="/static/image.jpeg" alt="Digital ICFAI Logo" class="brand-logo">
65
+ <h1 class="brand-title">
66
+ <span class="brand-highlight">Digital</span> ICFAI Assistant
67
+ </h1>
68
+ </div>
69
+ </header>
70
+
71
+
72
+ <main class="content">
73
+ {% block content %}{% endblock %}
74
+ </main>
75
+
76
+
77
+ <footer class="footer">
78
+ <p>© 2025 ICFAI University | Powered by CIT</p>
79
+ </footer>
80
+ </body>
81
+ </html>
templates/chat.html ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+ {% block content %}
3
+ <section class="chat-section">
4
+ <div class="chat-container">
5
+ <div id="chat-box" class="chat-box"></div>
6
+
7
+ <form id="chat-form" class="chat-form">
8
+ <textarea id="chat-input" placeholder="Ask your question..." rows="1"></textarea>
9
+ <button type="submit">Send</button>
10
+ </form>
11
+ </div>
12
+ </section>
13
+
14
+ <!-- ✅ Markdown Parser (for rendering ##, **, lists, and links properly) -->
15
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
16
+
17
+ <!-- ✅ Your custom chat logic -->
18
+ <script src="/static/chat.js"></script>
19
+ {% endblock %}
templates/courses.html ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+ {% block content %}
3
+ <section class="form-section">
4
+ <h2>🎓 Course Recommender</h2>
5
+
6
+ <form id="course-form">
7
+ <!-- Stream Selection -->
8
+ <label for="stream">Stream:</label>
9
+ <select id="stream" name="stream" required>
10
+ <option value="">-- Select Stream --</option>
11
+ <option value="Science">Science</option>
12
+ <option value="Commerce">Commerce</option>
13
+ <option value="Arts">Arts</option>
14
+ </select>
15
+
16
+ <!-- Area of Interest (hidden initially) -->
17
+ <div id="interest-container" style="display: none;">
18
+ <label for="interest">Area of Interest:</label>
19
+ <select id="interest" name="interest" required>
20
+ <option value="">-- Select Area --</option>
21
+ <option value="Tech">Technology</option>
22
+ <option value="Law">Law</option>
23
+ <option value="Management">Management</option>
24
+ <option value="Economics">Economics</option>
25
+ <option value="Media">Media</option>
26
+ </select>
27
+ </div>
28
+
29
+ <!-- English Proficiency -->
30
+ <label for="english">Comfortable in English:</label>
31
+ <select id="english" name="english" required>
32
+ <option value="">-- Select --</option>
33
+ <option value="Yes">Yes</option>
34
+ <option value="No">No</option>
35
+ </select>
36
+
37
+ <!-- Academic Details -->
38
+ <label for="tenth">10th Percentage:</label>
39
+ <input type="number" id="tenth" name="tenth" min="0" max="100" step="0.1" required>
40
+
41
+ <label for="twelfth">12th Percentage:</label>
42
+ <input type="number" id="twelfth" name="twelfth" min="0" max="100" step="0.1" required>
43
+
44
+ <!-- Submit -->
45
+ <button type="submit">Recommend Courses</button>
46
+ </form>
47
+
48
+ <!-- Results Section -->
49
+ <div id="course-results" class="results"></div>
50
+ </section>
51
+
52
+ <script>
53
+ // === Show "Area of Interest" only after Stream selection ===
54
+ document.addEventListener("DOMContentLoaded", () => {
55
+ const streamSelect = document.getElementById("stream");
56
+ const interestContainer = document.getElementById("interest-container");
57
+
58
+ streamSelect.addEventListener("change", () => {
59
+ interestContainer.style.display = streamSelect.value.trim() !== "" ? "block" : "none";
60
+ });
61
+ });
62
+
63
+ // === Handle Course Recommendation Request ===
64
+ document.getElementById("course-form").addEventListener("submit", async (e) => {
65
+ e.preventDefault();
66
+
67
+ const formData = new FormData(e.target);
68
+ const resultsDiv = document.getElementById("course-results");
69
+ resultsDiv.innerHTML = "<p>⏳ Fetching recommendations...</p>";
70
+
71
+ try {
72
+ const response = await fetch("/recommend_courses", {
73
+ method: "POST",
74
+ body: formData,
75
+ });
76
+
77
+ const data = await response.json();
78
+
79
+ if (!data.eligible) {
80
+ resultsDiv.innerHTML = `<div class="error-box">⚠️ ${data.message}</div>`;
81
+ return;
82
+ }
83
+
84
+ if (data.recommendations && data.recommendations.length > 0) {
85
+ const courseList = data.recommendations.map(c => `<li>🎓 ${c}</li>`).join("");
86
+ const applyButton = `<a href="https://ifheindia.org/online-registration" target="_blank">
87
+ <button class="apply-btn">Apply Now</button>
88
+ </a>`;
89
+
90
+ resultsDiv.innerHTML = `
91
+ <div class="result-card">
92
+ <h3>Recommended Courses</h3>
93
+ <ul>${courseList}</ul>
94
+ <p class="success-msg">${data.message}</p>
95
+ ${applyButton}
96
+ </div>
97
+ `;
98
+ } else {
99
+ resultsDiv.innerHTML = `<div class="info-box">ℹ️ No matching courses found.</div>`;
100
+ }
101
+
102
+ } catch (error) {
103
+ console.error(error);
104
+ resultsDiv.innerHTML = `<div class="error-box">⚠️ Error fetching recommendations.</div>`;
105
+ }
106
+ });
107
+ </script>
108
+ {% endblock %}
templates/digital_icfai_chat.html ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base_digital.html" %}
2
+ {% block content %}
3
+ <section class="chat-section">
4
+
5
+
6
+ <div class="chat-container">
7
+ <div id="chat-box" class="chat-box"></div>
8
+
9
+ <form id="chat-form" class="chat-form">
10
+ <textarea id="chat-input" placeholder="Ask about Digital ICFAI ERP System..." rows="1"></textarea>
11
+ <button type="submit">Send</button>
12
+ </form>
13
+ </div>
14
+ </section>
15
+
16
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
17
+ <script src="/static/chat_digital.js"></script>
18
+ {% endblock %}
templates/employees.html ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+ {% block content %}
3
+ <section class="form-section">
4
+ <h2>👨‍💼 Employee Lookup</h2>
5
+
6
+ <form id="employee-form">
7
+ <label>Search by:</label>
8
+ <select name="search_type" required>
9
+ <option value="id">Employee ID</option>
10
+ <option value="salary">Salary (Above)</option>
11
+ <option value="experience">Experience (Above)</option>
12
+ </select>
13
+
14
+ <label>Value:</label>
15
+ <input type="text" name="value" required>
16
+ <button type="submit">Search</button>
17
+ </form>
18
+
19
+ <!-- ✅ Results container (scrolls internally) -->
20
+ <div id="employee-results" class="results">
21
+ <!-- Table will be dynamically inserted here by employees.js -->
22
+ </div>
23
+ </section>
24
+
25
+ <script src="/static/employees.js"></script>
26
+ {% endblock %}