mirror of
https://github.com/0xShay/SupportMe.git
synced 2026-03-09 21:11:22 +00:00
Initial commit
This commit is contained in:
46
templates/base.html
Normal file
46
templates/base.html
Normal file
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>SupportMe</title>
|
||||
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<link rel="icon" href="/icon.png">
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<p id="loggedInMessage">...</p>
|
||||
|
||||
<br>
|
||||
|
||||
<a href="/home" style="text-decoration: none; color: black;">
|
||||
<h1>💬 SupportMe 💁</h1>
|
||||
</a>
|
||||
|
||||
<script src="/src/constants.js"></script>
|
||||
<script src="/src/jwt-decode.js"></script>
|
||||
<script src="/src/userTools.js"></script>
|
||||
<script src="/src/ticketTools.js"></script>
|
||||
|
||||
<script>
|
||||
const currentUser = getLoggedInUser();
|
||||
if (currentUser != null) {
|
||||
getProfile(currentUser["user_id"]).then(profile => {
|
||||
document.getElementById("loggedInMessage").innerText = "Logged in: " + profile["username"];
|
||||
});
|
||||
} else {
|
||||
document.getElementById("loggedInMessage").innerText = "You are not logged in.";
|
||||
};
|
||||
</script>
|
||||
|
||||
{% block content %} {% endblock %}
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
91
templates/home.html
Normal file
91
templates/home.html
Normal file
@@ -0,0 +1,91 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Home</h2>
|
||||
|
||||
<br>
|
||||
|
||||
<a href="/ticket/new"><button id="open_ticket_button">Open a support ticket</button></a>
|
||||
<br>
|
||||
<a href="/profile"><button>Edit profile</button></a>
|
||||
<button onclick=logout()>Log out</button>
|
||||
|
||||
<section>
|
||||
|
||||
<h3>Open tickets</h3>
|
||||
|
||||
<ul id="open_tickets_list"></ul>
|
||||
|
||||
</section>
|
||||
|
||||
<section id="claimable_tickets">
|
||||
|
||||
<h3>Claimable tickets</h3>
|
||||
|
||||
<ul id="unclaimed_tickets_list"></ul>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
||||
<h3>Closed tickets</h3>
|
||||
|
||||
<ul id="closed_tickets_list"></ul>
|
||||
|
||||
</section>
|
||||
|
||||
<script>
|
||||
if (currentUser == null) window.location.href = "/login";
|
||||
|
||||
const openTicketsList = document.getElementById("open_tickets_list")
|
||||
const closedTicketsList = document.getElementById("closed_tickets_list")
|
||||
const unclaimedTicketsList = document.getElementById("unclaimed_tickets_list")
|
||||
|
||||
getOpenTicketsByUserID(currentUser["user_id"]).then(openTickets => {
|
||||
if (openTickets.length == 0) openTicketsList.innerHTML = "<i>No tickets to show.</i>"
|
||||
for (t of openTickets) {
|
||||
let _a = document.createElement("a");
|
||||
_a.innerText = "#" + t["ticket_id"] + " - " + t["title"];
|
||||
_a.href = "/ticket/" + t["ticket_id"];
|
||||
let _li = document.createElement("li");
|
||||
_li.appendChild(_a);
|
||||
openTicketsList.appendChild(_li);
|
||||
};
|
||||
});
|
||||
|
||||
getClosedTicketsByUserID(currentUser["user_id"]).then(closedTickets => {
|
||||
if (closedTickets.length == 0) closedTicketsList.innerHTML = "<i>No tickets to show.</i>"
|
||||
for (t of closedTickets) {
|
||||
let _a = document.createElement("a");
|
||||
_a.innerText = "#" + t["ticket_id"] + " - " + t["title"];
|
||||
_a.href = "/ticket/" + t["ticket_id"];
|
||||
let _li = document.createElement("li");
|
||||
_li.appendChild(_a);
|
||||
closedTicketsList.appendChild(_li);
|
||||
};
|
||||
});
|
||||
|
||||
if (currentUser["account_type"] == ACCOUNT_TYPE_CUSTOMER) {
|
||||
|
||||
document.getElementById("claimable_tickets").style.display = "none";
|
||||
|
||||
} else if (currentUser["account_type"] == ACCOUNT_TYPE_ASSISTANT) {
|
||||
|
||||
document.getElementById("claimable_tickets").style.display = "block";
|
||||
document.getElementById("open_ticket_button").style.display = "none";
|
||||
|
||||
getUnclaimedTickets().then(unclaimedTickets => {
|
||||
if (unclaimedTickets.length == 0) unclaimedTicketsList.innerHTML = "<i>No tickets to show.</i>"
|
||||
for (t of unclaimedTickets) {
|
||||
_a = document.createElement("a");
|
||||
_a.innerText = "#" + t["ticket_id"] + " - " + t["title"];
|
||||
_a.href = "/ticket/" + t["ticket_id"];
|
||||
_li = document.createElement("li");
|
||||
_li.appendChild(_a);
|
||||
unclaimedTicketsList.appendChild(_li);
|
||||
};
|
||||
});
|
||||
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
27
templates/login.html
Normal file
27
templates/login.html
Normal file
@@ -0,0 +1,27 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Login</h2>
|
||||
|
||||
<br>
|
||||
|
||||
<label for="username">Username</label><br>
|
||||
<input type="text" id="username_input" name="username" />
|
||||
|
||||
<br><br>
|
||||
|
||||
<label for="password">Password</label><br>
|
||||
<input type="password" id="password_input" name="password" />
|
||||
|
||||
<br><br>
|
||||
|
||||
<button type="submit" onclick="login()">Login</button>
|
||||
|
||||
<br><br>
|
||||
|
||||
<a href="register">Don't have an account? Click here to sign up!</a>
|
||||
|
||||
<script>
|
||||
if (currentUser != null) window.location.href = "/home";
|
||||
</script>
|
||||
{% endblock %}
|
||||
68
templates/profile.html
Normal file
68
templates/profile.html
Normal file
@@ -0,0 +1,68 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Profile</h2>
|
||||
|
||||
<br>
|
||||
|
||||
<img id="profile_icon">
|
||||
|
||||
<br><br>
|
||||
|
||||
<label for="user_id">Your user ID</label><br>
|
||||
<input type="text" id="user_id_input" name="user_id" disabled />
|
||||
|
||||
<br><br>
|
||||
|
||||
<label for="username">Your username (cannot be changed)</label><br>
|
||||
<input type="text" id="username_input" name="username" disabled />
|
||||
|
||||
<br><br>
|
||||
|
||||
<label for="new_password">New password</label><br>
|
||||
<input type="password" id="new_password_input" name="new_password" />
|
||||
|
||||
<br><br>
|
||||
|
||||
<label for="new_passwordc">Confirm new password</label><br>
|
||||
<input type="password" id="new_passwordc_input" name="new_passwordc" />
|
||||
|
||||
<br><br>
|
||||
|
||||
<label for="old_password">Old password</label><br>
|
||||
<input type="password" id="old_password_input" name="old_password" />
|
||||
|
||||
<br><br>
|
||||
|
||||
<label for="profile_icon">Profile picture</label><br>
|
||||
<select name="profile_icon" id="profile_icon_select" oninput="updateProfileIconPreview()">
|
||||
<option value="/profile-icons/blue.png">Blue</option>
|
||||
<option value="/profile-icons/green.png">Green</option>
|
||||
<option value="/profile-icons/purple.png">Purple</option>
|
||||
<option value="/profile-icons/red.png">Red</option>
|
||||
</select>
|
||||
|
||||
<br><br>
|
||||
|
||||
<button type="submit" onclick="updateProfile()">Save changes</button>
|
||||
|
||||
<br><br>
|
||||
|
||||
<a href="register">Don't have an account? Click here to sign up!</a>
|
||||
|
||||
<script>
|
||||
if (currentUser == null) window.location.href = "/login";
|
||||
|
||||
getProfile(currentUser["user_id"]).then(profile => {
|
||||
document.getElementById("profile_icon").src = profile["profile_icon"];
|
||||
document.getElementById("profile_icon").width = 150;
|
||||
document.getElementById("user_id_input").value = profile["user_id"];
|
||||
document.getElementById("username_input").value = profile["username"];
|
||||
document.getElementById("profile_icon_select").value = profile["profile_icon"];
|
||||
});
|
||||
|
||||
function updateProfileIconPreview() {
|
||||
document.getElementById("profile_icon").src = document.getElementById("profile_icon_select").value;
|
||||
};
|
||||
</script>
|
||||
{% endblock %}
|
||||
37
templates/register.html
Normal file
37
templates/register.html
Normal file
@@ -0,0 +1,37 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Register</h2>
|
||||
|
||||
<br>
|
||||
|
||||
<label for="username">Username</label><br>
|
||||
<input type="text" id="username_input" name="username" />
|
||||
|
||||
<br><br>
|
||||
|
||||
<label for="email">Email</label><br>
|
||||
<input type="email" id="email_input" name="email" />
|
||||
|
||||
<br><br>
|
||||
|
||||
<label for="password">Password</label><br>
|
||||
<input type="password" id="password_input" name="password" />
|
||||
|
||||
<br><br>
|
||||
|
||||
<label for="passwordc">Confirm Password</label><br>
|
||||
<input type="password" id="passwordc_input" name="passwordc" />
|
||||
|
||||
<br><br>
|
||||
|
||||
<button type="submit" onclick="register()">Register</button>
|
||||
|
||||
<br><br>
|
||||
|
||||
<a href="login">Already have an account? Click here to log in!</a>
|
||||
|
||||
<script>
|
||||
if (currentUser != null) window.location.href = "/home";
|
||||
</script>
|
||||
{% endblock %}
|
||||
25
templates/ticket/new.html
Normal file
25
templates/ticket/new.html
Normal file
@@ -0,0 +1,25 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Open a new support ticket</h2>
|
||||
|
||||
<br>
|
||||
|
||||
<label for="ticket_title">Ticket Title</label><br>
|
||||
<input type="text" id="ticket_title_input" name="ticket_title" />
|
||||
|
||||
<br><br>
|
||||
|
||||
<label for="message">Ticket Description</label><br>
|
||||
<textarea type="text" id="message_input" name="message" rows="10"></textarea>
|
||||
|
||||
<br><br>
|
||||
|
||||
<button type="submit" onclick="openTicket()">Open ticket</button>
|
||||
|
||||
<br><br>
|
||||
|
||||
<script>
|
||||
if (currentUser == null) window.location.href = "/login";
|
||||
</script>
|
||||
{% endblock %}
|
||||
102
templates/ticket/ticket.html
Normal file
102
templates/ticket/ticket.html
Normal file
@@ -0,0 +1,102 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h2 id="ticket_title">...</h2>
|
||||
<p id="ticket_id">Ticket #{{ ticket_id }}</p>
|
||||
|
||||
<p>
|
||||
<span id="opened_at"></span><br>
|
||||
<span id="closed_at"></span>
|
||||
</p>
|
||||
<p>
|
||||
<span id="opened_by">...</span><br>
|
||||
<span id="claimed_by">This ticket has not been claimed</span>
|
||||
</p>
|
||||
|
||||
<br>
|
||||
|
||||
<label for="message">Send a message:</label><br>
|
||||
<textarea type="text" id="message_input" name="message" rows="10"></textarea>
|
||||
|
||||
<br><br>
|
||||
|
||||
<button id="close_ticket_btn" type="submit" onclick="sendMessage('!close')">Close ticket</button>
|
||||
<button id="open_ticket_btn" type="submit" onclick="sendMessage('!open')">Reopen ticket</button>
|
||||
<button id="send_message_btn" type="submit" onclick="sendMessage()">Send</button>
|
||||
<button id="claim_ticket_btn" type="submit" onclick="sendMessage('!claim')">Claim ticket</button>
|
||||
|
||||
<br><br>
|
||||
|
||||
<table id="ticket_messages"></table>
|
||||
|
||||
<script>
|
||||
if (currentUser == null) window.location.href = "/login";
|
||||
|
||||
const profiles = {};
|
||||
|
||||
const ticketID = parseInt("{{ ticket_id }}");
|
||||
|
||||
getTicket(ticketID).then(ticket => {
|
||||
if (ticket != false) {
|
||||
ticket = ticket["ticket_data"];
|
||||
document.getElementById("ticket_title").innerText = "[" + (ticket["closed_at"] == -1 ? "OPEN" : "CLOSED") + "] " + ticket["title"];
|
||||
document.getElementById("opened_at").innerText = "Opened: " + new Date(ticket["opened_at"] * 1000).toLocaleString();
|
||||
if (ticket["closed_at"] != -1) {
|
||||
document.getElementById("closed_at").innerText = "Closed: " + new Date(ticket["closed_at"] * 1000).toLocaleString();
|
||||
document.getElementById("open_ticket_btn").style.display = "inline";
|
||||
} else {
|
||||
document.getElementById("close_ticket_btn").style.display = "inline";
|
||||
};
|
||||
getProfile(ticket["customer_id"]).then(customer => {
|
||||
document.getElementById("opened_by").innerHTML = "Opened by <b>" + customer["username"] + "</b> (ID: " + ticket["customer_id"] + ")";
|
||||
});
|
||||
if (currentUser["account_type"] == ACCOUNT_TYPE_ASSISTANT && ticket["assistant_id"] != currentUser["user_id"]) {
|
||||
document.getElementById("claim_ticket_btn").style.display = "inline";
|
||||
};
|
||||
if (ticket["assistant_id"] != -1) {
|
||||
getProfile(ticket["assistant_id"]).then(assistant => {
|
||||
document.getElementById("claimed_by").innerHTML = "Claimed by <b>" + assistant["username"] + "</b> (ID: " + ticket["assistant_id"] + ")";
|
||||
});
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
const ticketMessagesTable = document.getElementById("ticket_messages");
|
||||
|
||||
getMessages(ticketID).then(async messages => {
|
||||
for (a_id of messages.map(m => m["author_id"])) { profiles[a_id] = await getProfile(a_id); }
|
||||
|
||||
for (msg of messages) {
|
||||
|
||||
let _tr = document.createElement("tr");
|
||||
let _td1 = document.createElement("td");
|
||||
let _img = document.createElement("img");
|
||||
_img.src = profiles[msg["author_id"]]["profile_icon"];
|
||||
_img.width = 150;
|
||||
let _td2 = document.createElement("td");
|
||||
let _h3_sender = document.createElement("h3");
|
||||
_h3_sender.classList.add("message_sender");
|
||||
_h3_sender.innerText = profiles[msg["author_id"]]["username"];
|
||||
let _p_body = document.createElement("p");
|
||||
_p_body.classList.add("message_body");
|
||||
_p_body.innerText = msg["body"];
|
||||
let _br1 = document.createElement("br");
|
||||
let _p_sent_at = document.createElement("p");
|
||||
_p_sent_at.classList.add("message_sent_at");
|
||||
_p_sent_at.innerText = new Date(msg["sent_at"] * 1000).toLocaleString();
|
||||
let _br2 = document.createElement("br");
|
||||
_td1.appendChild(_img);
|
||||
_td2.appendChild(_h3_sender);
|
||||
_td2.appendChild(_p_body);
|
||||
_td2.appendChild(_br1);
|
||||
_td2.appendChild(_p_sent_at);
|
||||
_td2.appendChild(_br2);
|
||||
_tr.appendChild(_td1);
|
||||
_tr.appendChild(_td2);
|
||||
ticketMessagesTable.appendChild(_tr);
|
||||
|
||||
};
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user