Full commit for review
This commit is contained in:
2
webapp/scripts/a81368914c.js
Normal file
2
webapp/scripts/a81368914c.js
Normal file
File diff suppressed because one or more lines are too long
172
webapp/scripts/app.js
Normal file
172
webapp/scripts/app.js
Normal file
@@ -0,0 +1,172 @@
|
||||
const api = 'https://aostia.me/api';
|
||||
const loginForm = document.getElementById('loginForm');
|
||||
const registerForm = document.getElementById('registerForm');
|
||||
const resetPasswordForm = document.getElementById('resetPasswordForm');
|
||||
const verifyResetPasswordForm = document.getElementById('verifyResetPasswordForm');
|
||||
const requestResetPasswordForm = document.getElementById('requestResetPasswordForm');
|
||||
|
||||
const home = document.getElementById('home');
|
||||
const login = document.getElementById('login');
|
||||
const register = document.getElementById('register');
|
||||
const forgot = document.getElementById('forgotPassword');
|
||||
|
||||
function events() {
|
||||
document.querySelector('.toggleThemeBtn').addEventListener('click', toggleTheme);
|
||||
document.querySelectorAll('.toggleForgotPasswordBtn').forEach(button => button.addEventListener('click', () => togglePage('forgotPassword')));
|
||||
document.querySelectorAll('.toggleRegisterBtn').forEach(button => button.addEventListener('click', () => togglePage('register')));
|
||||
document.querySelectorAll('.toggleLoginBtn').forEach(button => button.addEventListener('click', () => togglePage('login')));
|
||||
document.querySelectorAll('.toggleHomeBtn').forEach(button => button.addEventListener('click', () => togglePage('home')));
|
||||
document.querySelectorAll('.registerNextBtn').forEach(button => button.addEventListener('click', () => togglePage('registerNext')));
|
||||
document.querySelectorAll('.registerBackBtn').forEach(button => button.addEventListener('click', () => togglePage('registerBack')));
|
||||
document.querySelector('.hamburger-menu').addEventListener('click', () => {
|
||||
document.querySelector('.big-wrapper').classList.toggle('active');
|
||||
});
|
||||
}
|
||||
|
||||
function togglePage(page) {
|
||||
if (page == 'home') {
|
||||
localStorage.setItem('page', 'home');
|
||||
transition(home, login, register, forgot, 'appear-transition', 'disintegrate-transition');
|
||||
}
|
||||
else if (page == 'login') {
|
||||
localStorage.setItem('page', 'login');
|
||||
transition(login, home, register, forgot, 'appear-transition', 'disintegrate-transition');
|
||||
}
|
||||
else if (page == 'register') {
|
||||
localStorage.setItem('page', 'register');
|
||||
transition(register, home, login, forgot, 'appear-transition', 'disintegrate-transition');
|
||||
}
|
||||
else if (page == 'registerNext') {
|
||||
document.getElementById('register-form1').classList.add('hidden');
|
||||
document.getElementById('register-next').classList.add('hidden');
|
||||
document.getElementById('register-form2').classList.remove('hidden');
|
||||
document.getElementById('register-btn-2').classList.add('flex');
|
||||
document.getElementById('register-btn-2').classList.remove('hidden');
|
||||
}
|
||||
else if (page == 'registerBack') {
|
||||
document.getElementById('register-form2').classList.add('hidden');
|
||||
document.getElementById('register-btn-2').classList.add('hidden');
|
||||
document.getElementById('register-btn-2').classList.remove('flex');
|
||||
document.getElementById('register-form1').classList.remove('hidden');
|
||||
document.getElementById('register-next').classList.remove('hidden');
|
||||
}
|
||||
else if (page == 'forgotPassword') {
|
||||
localStorage.setItem('page', 'forgotPassword');
|
||||
transition(forgot, home, login, register, 'appear-transition', 'disintegrate-transition');
|
||||
}
|
||||
}
|
||||
|
||||
function transition(d1, d2, d3, d4, a1, a2) {
|
||||
d2.classList.add(a2);
|
||||
d3.classList.add(a2);
|
||||
d4.classList.add(a2);
|
||||
setTimeout(() => {
|
||||
d1.classList.replace('hidden', a1);
|
||||
d2.classList.replace(a2, 'hidden');
|
||||
d3.classList.replace(a2, 'hidden');
|
||||
d4.classList.replace(a2, 'hidden');
|
||||
setTimeout(() => {
|
||||
d1.classList.remove(a1);
|
||||
}, 950);
|
||||
}, 950);
|
||||
}
|
||||
|
||||
function dbox(msg) {
|
||||
if (msg !== undefined) {
|
||||
document.getElementById('boxTxt').innerHTML = msg;
|
||||
document.getElementById('diag').classList.remove('hidden');
|
||||
}
|
||||
else {
|
||||
document.getElementById('diag').classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
window.onload = (event) => {
|
||||
events();
|
||||
togglePage(localStorage.getItem('page') || 'home');
|
||||
loginForm.addEventListener('submit', async (event) => {
|
||||
event.preventDefault();
|
||||
const { status, data } = await post(`${api}/users/login`, { usernameOrEmail: loginForm.elements['usernameOrEmail'].value, password: loginForm.elements['password'].value });
|
||||
|
||||
if (status != 200) {
|
||||
dbox(`${data.message}`);
|
||||
console.error(data);
|
||||
}
|
||||
else {
|
||||
dbox('Login successful!\nRedirecting...');
|
||||
localStorage.setItem('jwt', data.JSON.token);
|
||||
setTimeout(() => {
|
||||
window.location.href = './dash.html';
|
||||
}, 950);
|
||||
}
|
||||
});
|
||||
|
||||
registerForm.addEventListener('submit', async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const username = registerForm.elements['username'].value;
|
||||
const email = registerForm.elements['email'].value;
|
||||
const password = registerForm.elements['password'].value;
|
||||
const first_name = registerForm.elements['name'].value;
|
||||
const last_name = registerForm.elements['lastname'].value;
|
||||
const phone = registerForm.elements['phone'].value || 'None';
|
||||
|
||||
const { status, data } = await post(`${api}/users/register`, { username: username, email: email, password: password, first_name: first_name, last_name: last_name, phone: phone });
|
||||
|
||||
if (status != 200) {
|
||||
const data = await response.json();
|
||||
dbox(`${data.message}`);
|
||||
console.error(data);
|
||||
}
|
||||
else {
|
||||
localStorage.setItem('jwt', data.token);
|
||||
dbox('Successfully registered!');
|
||||
togglePage('login');
|
||||
}
|
||||
});
|
||||
|
||||
requestResetPasswordForm.addEventListener('submit', async (event) => {
|
||||
event.preventDefault();
|
||||
const { status, data } = await post(`${api}/users/changepassword`, { usernameOrEmail: requestResetPasswordForm.elements['usernameOrEmail'].value });
|
||||
|
||||
if (status != 200) {
|
||||
dbox(`${data.message}`);
|
||||
console.error(data);
|
||||
}
|
||||
else {
|
||||
dbox('Reset password email has been sent!');
|
||||
verifyResetPasswordForm.classList.remove('hidden');
|
||||
requestResetPasswordForm.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
verifyResetPasswordForm.addEventListener('submit', async (event) => {
|
||||
event.preventDefault();
|
||||
const { status, data } = await get(`${api}/users/verify?c=${verifyResetPasswordForm.elements['code'].value}&u=${requestResetPasswordForm.elements['usernameOrEmail'].value}`);
|
||||
if (status != 200) {
|
||||
dbox(`${data.message}`);
|
||||
console.error(data);
|
||||
}
|
||||
else {
|
||||
dbox('Valid verification code!');
|
||||
resetPasswordForm.classList.remove('hidden');
|
||||
verifyResetPasswordForm.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
resetPasswordForm.addEventListener('submit', async (event) => {
|
||||
event.preventDefault();
|
||||
const { status, data } = await patch(`${api}/users/changepassword`, { code: verifyResetPasswordForm.elements['code'].value, usernameOrEmail: requestResetPasswordForm.elements['usernameOrEmail'].value, password: resetPasswordForm.elements['password'].value });
|
||||
|
||||
if (status != 200) {
|
||||
dbox(`${data.message}`);
|
||||
console.error(data);
|
||||
}
|
||||
else {
|
||||
dbox('Password successfully reset!');
|
||||
requestResetPasswordForm.classList.remove('hidden');
|
||||
resetPasswordForm.classList.add('hidden');
|
||||
togglePage('login');
|
||||
}
|
||||
});
|
||||
};
|
||||
20
webapp/scripts/chart.js
Normal file
20
webapp/scripts/chart.js
Normal file
File diff suppressed because one or more lines are too long
264
webapp/scripts/dash.js
Normal file
264
webapp/scripts/dash.js
Normal file
@@ -0,0 +1,264 @@
|
||||
// exemple
|
||||
const chartData = {
|
||||
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
|
||||
datasets: [{
|
||||
label: 'Dataset 1',
|
||||
data: [12, 19, 3, 5, 2, 3, 9],
|
||||
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
||||
borderColor: 'rgba(255, 99, 132, 1)',
|
||||
borderWidth: 1,
|
||||
},
|
||||
{
|
||||
label: 'Dataset 2',
|
||||
data: [6, 12, 8, 2, 10, 5, 3],
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.2)',
|
||||
borderColor: 'rgba(54, 162, 235, 1)',
|
||||
borderWidth: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const api = 'https://aostia.me/api/'
|
||||
|
||||
const cookieValue = document.cookie
|
||||
.split('; ')
|
||||
.find(row => row.startsWith('token='))
|
||||
?.split('=')[1];
|
||||
|
||||
const token = cookieValue ? cookieValue : localStorage.getItem('jwt');
|
||||
if (!token) {
|
||||
toastNotifications('error', 'Missing token' + token);
|
||||
setTimeout(() => {
|
||||
window.location.href = './index.html';
|
||||
}, 950);
|
||||
}
|
||||
|
||||
fetchPage();
|
||||
|
||||
document.querySelectorAll('.sidebarBtn').forEach((btn) => {
|
||||
btn.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
const url = btn.getAttribute('href');
|
||||
window.history.pushState({}, '', url);
|
||||
fetchPage();
|
||||
});
|
||||
});
|
||||
|
||||
async function fetchPage() {
|
||||
const childElements = document.querySelector('.tiles-container').querySelectorAll('div');
|
||||
childElements.forEach((child) => {
|
||||
document.querySelector('.tiles-container').removeChild(child);
|
||||
});
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const type = params.get('page');
|
||||
if (!type) {
|
||||
try {
|
||||
const airlines = await get(api + 'airlines', token);
|
||||
const airplanes = await get(api + 'airplanes', token);
|
||||
const airports = await get(api + 'airports', token);
|
||||
const flights = await get(api + 'flights', token);
|
||||
const pilots = await get(api + 'pilots', token);
|
||||
const seats = await get(api + 'seats', token);
|
||||
if (airlines.status == 200) {
|
||||
renderTable(airlines.data.JSON);
|
||||
}
|
||||
else {
|
||||
toastNotifications('error', data.message);
|
||||
}
|
||||
if (airplanes.status == 200) {
|
||||
renderTable(airplanes.data.JSON);
|
||||
}
|
||||
else {
|
||||
toastNotifications('error', data.message);
|
||||
}
|
||||
if (airports.status == 200) {
|
||||
renderTable(airports.data.JSON);
|
||||
}
|
||||
else {
|
||||
toastNotifications('error', data.message);
|
||||
}
|
||||
if (flights.status == 200) {
|
||||
renderTable(flights.data.JSON);
|
||||
}
|
||||
else {
|
||||
toastNotifications('error', data.message);
|
||||
}
|
||||
if (pilots.status == 200) {
|
||||
renderTable(pilots.data.JSON);
|
||||
}
|
||||
else {
|
||||
toastNotifications('error', data.message);
|
||||
}
|
||||
if (seats.status == 200) {
|
||||
renderTable(seats.data.JSON);
|
||||
}
|
||||
else {
|
||||
toastNotifications('error', data.message);
|
||||
}
|
||||
addEventTiles();
|
||||
}
|
||||
catch (err) {
|
||||
console.error(error);
|
||||
toastNotifications('error', 'Failed to fetch data');
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
let endpoint = api + type;
|
||||
if (type === 'profile') {
|
||||
endpoint = api + 'users/me';
|
||||
}
|
||||
|
||||
const { status, data } = await get(endpoint, token);
|
||||
|
||||
if (status == 200 && type === 'profile') {
|
||||
renderProfile(data.JSON);
|
||||
} else if (status == 200) {
|
||||
renderTable(data.JSON);
|
||||
addEventTiles();
|
||||
} else {
|
||||
toastNotifications('error', data.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toastNotifications('error', 'Failed to fetch data');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderProfile(userData) {
|
||||
const table = document.createElement('table');
|
||||
|
||||
for (const [key, value] of Object.entries(userData)) {
|
||||
const row = document.createElement('tr');
|
||||
const dataName = document.createElement('td');
|
||||
const dataValue = document.createElement('td');
|
||||
const editCell = document.createElement('td');
|
||||
|
||||
dataName.textContent = key;
|
||||
dataValue.textContent = value;
|
||||
|
||||
const editButton = document.createElement('button');
|
||||
editButton.textContent = 'Edit';
|
||||
editButton.addEventListener('click', async () => {
|
||||
console.log('Edit button clicked for row:', key);
|
||||
const newValue = prompt(`Enter new value for ${key}:`);
|
||||
if (newValue !== null) {
|
||||
const { status, data } = await patch(api + 'users/', {type:key,value:newValue}, token);
|
||||
if (status == 200) {
|
||||
fetchPage();
|
||||
}
|
||||
else {
|
||||
toastNotifications('error', data.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
editCell.appendChild(editButton);
|
||||
|
||||
row.appendChild(dataName);
|
||||
row.appendChild(dataValue);
|
||||
row.appendChild(editCell);
|
||||
table.appendChild(row);
|
||||
}
|
||||
|
||||
const tableTile = createTile('Profile', table);
|
||||
document.querySelector('.tiles-container').appendChild(tableTile);
|
||||
}
|
||||
|
||||
|
||||
function renderTable(tableData) {
|
||||
const table = document.createElement('table');
|
||||
for (const data of tableData) {
|
||||
const row = document.createElement('tr');
|
||||
for (const prop in data) {
|
||||
const cell = document.createElement('td');
|
||||
cell.innerText = data[prop];
|
||||
row.appendChild(cell);
|
||||
}
|
||||
table.appendChild(row);
|
||||
}
|
||||
|
||||
const tableTile = createTile('Table', table);
|
||||
document.querySelector('.tiles-container').appendChild(tableTile);
|
||||
}
|
||||
|
||||
function renderList(listData) {
|
||||
const list = document.createElement('ul');
|
||||
for (const data of listData) {
|
||||
const item = document.createElement('li');
|
||||
const fields = [];
|
||||
for (const key in data) {
|
||||
fields.push(data[key]);
|
||||
}
|
||||
item.innerText = fields.join(', ');
|
||||
list.appendChild(item);
|
||||
}
|
||||
const listTile = createTile('List', list);
|
||||
document.querySelector('.tiles-container').appendChild(listTile);
|
||||
}
|
||||
|
||||
function renderChart(chartData) {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.id = 'chart';
|
||||
canvas.width = '1920';
|
||||
canvas.height = '1080';
|
||||
const ctx = canvas.getContext('2d');
|
||||
const chart = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: chartData,
|
||||
options: {
|
||||
scales: {
|
||||
y: {
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
maintainAspectRatio: false,
|
||||
});
|
||||
const chartTile = createTile('Chart', canvas);
|
||||
document.querySelector('.tiles-container').appendChild(chartTile);
|
||||
}
|
||||
|
||||
function createTile(title, content) {
|
||||
const tile = document.createElement('div');
|
||||
tile.classList.add('tile');
|
||||
const header = document.createElement('h2');
|
||||
header.innerText = title;
|
||||
tile.appendChild(header);
|
||||
tile.appendChild(content);
|
||||
return tile;
|
||||
}
|
||||
|
||||
function addEventTiles() {
|
||||
const tiles = document.querySelectorAll('.tile');
|
||||
for (const tile of tiles) {
|
||||
tile.addEventListener('click', () => {
|
||||
if (!tile.classList.contains('fullscreen')) {
|
||||
tile.classList.add('fullscreen');
|
||||
}
|
||||
else {
|
||||
tile.classList.remove('fullscreen');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// toast
|
||||
function toastNotifications(type, msg) {
|
||||
const toast = document.getElementById('toast');
|
||||
if (type == 'success') {
|
||||
toast.innerHTML = `<div class="checkicon success"> <i class="fa fa-check-square"> ${msg}</i></div>`;
|
||||
}
|
||||
else if (type == 'error') {
|
||||
toast.innerHTML = `<div class="checkicon error"> <i class="fa fa-minus-square"> ${msg}</i></div>`;
|
||||
}
|
||||
else {
|
||||
toast.innerHTML = msg;
|
||||
}
|
||||
toast.className = 'show';
|
||||
setTimeout(function() {
|
||||
toast.className = toast.className.replace('show', '');
|
||||
}, 3000);
|
||||
}
|
||||
58
webapp/scripts/fetcher.js
Normal file
58
webapp/scripts/fetcher.js
Normal file
@@ -0,0 +1,58 @@
|
||||
async function get(url, token) {
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
authorization: `${token}`
|
||||
},
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
return { status: response.status, data: data };
|
||||
}
|
||||
|
||||
async function post(url, json, token) {
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
authorization: `${token}`
|
||||
},
|
||||
body: JSON.stringify(json),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
return { status: response.status, data: data };
|
||||
}
|
||||
|
||||
async function patch(url, json, token) {
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
authorization: `${token}`
|
||||
},
|
||||
body: JSON.stringify(json),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
return { status: response.status, data: data };
|
||||
}
|
||||
|
||||
async function put(url, json, token) {
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
authorization: `${token}`
|
||||
},
|
||||
body: JSON.stringify(json),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
return { status: response.status, data: data };
|
||||
}
|
||||
51
webapp/scripts/notification.js
Normal file
51
webapp/scripts/notification.js
Normal file
@@ -0,0 +1,51 @@
|
||||
// notifications
|
||||
const notificationMenu = document.getElementById('notification-menu');
|
||||
const notificationIcon = document.getElementById('notification-icon');
|
||||
|
||||
window.addEventListener('load', generateNotifications);
|
||||
notificationIcon.addEventListener('click', generateNotifications);
|
||||
|
||||
function generateNotifications() {
|
||||
const notificationLoading = document.getElementById('notification-loading');
|
||||
notificationLoading.classList.remove('hidden');
|
||||
|
||||
const notifications = [
|
||||
{ title: 'Notification 1', link: '#' },
|
||||
{ title: 'Notification 2', link: '#' },
|
||||
{ title: 'Notification 3', link: '#' },
|
||||
];
|
||||
|
||||
const childElements = notificationMenu.querySelectorAll('li');
|
||||
childElements.forEach((child) => {
|
||||
notificationMenu.removeChild(child);
|
||||
});
|
||||
|
||||
notifications.forEach(notification => {
|
||||
const listItem = document.createElement('li');
|
||||
const link = document.createElement('a');
|
||||
link.href = notification.link;
|
||||
link.textContent = notification.title;
|
||||
listItem.appendChild(link);
|
||||
notificationMenu.appendChild(listItem);
|
||||
});
|
||||
notificationLoading.classList.add('hidden');
|
||||
}
|
||||
|
||||
|
||||
// Notification block
|
||||
let timeout;
|
||||
notificationIcon.addEventListener('mouseover', showNotificationMenu);
|
||||
notificationIcon.addEventListener('mouseout', hideNotificationMenu);
|
||||
notificationMenu.addEventListener('mouseover', showNotificationMenu);
|
||||
notificationMenu.addEventListener('mouseout', hideNotificationMenu);
|
||||
|
||||
function showNotificationMenu() {
|
||||
notificationMenu.style.display = 'block';
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
|
||||
function hideNotificationMenu() {
|
||||
timeout = setTimeout(() => {
|
||||
notificationMenu.style.display = 'none';
|
||||
}, 500);
|
||||
}
|
||||
42
webapp/scripts/theme.js
Normal file
42
webapp/scripts/theme.js
Normal file
@@ -0,0 +1,42 @@
|
||||
function checkNavTheme() {
|
||||
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { return true; }
|
||||
else { return false; }
|
||||
}
|
||||
let dark = (localStorage.getItem('dark') == 'false') ? false : checkNavTheme();
|
||||
if (!dark) {
|
||||
document.querySelector('.big-wrapper').classList.add('light');
|
||||
}
|
||||
else {
|
||||
document.querySelector('.big-wrapper').classList.add('dark');
|
||||
}
|
||||
|
||||
function toggleTheme() {
|
||||
dark = !dark;
|
||||
localStorage.setItem('dark', dark);
|
||||
setTheme(dark);
|
||||
}
|
||||
|
||||
function setTheme() {
|
||||
const wrapper = document.querySelector('.big-wrapper');
|
||||
const btn = document.getElementById('toggle-theme');
|
||||
btn.setAttribute('disabled', 'disabled');
|
||||
setTimeout(() => {
|
||||
if (dark) {
|
||||
wrapper.classList.replace('light', 'dark');
|
||||
}
|
||||
else {
|
||||
wrapper.classList.replace('dark', 'light');
|
||||
}
|
||||
if (window.location.pathname.split('/').pop() == 'index.html') {
|
||||
wrapper.classList.add('appear-transition');
|
||||
setTimeout(() => {
|
||||
wrapper.classList.remove('appear-transition');
|
||||
btn.removeAttribute('disabled');
|
||||
}, 1500);
|
||||
} else {
|
||||
btn.removeAttribute('disabled');
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
document.querySelector('.toggleThemeBtn').addEventListener('click', toggleTheme);
|
||||
Reference in New Issue
Block a user