editor-cv #26
141
dist/api/index.php
vendored
141
dist/api/index.php
vendored
@ -59,6 +59,139 @@ $app->get("/timelineData/{timeline}", function (Request $request, Response $resp
|
||||
return $response->withStatus(404);
|
||||
});
|
||||
|
||||
$app->patch("/timelineData/{timeline}/{id}", function (Request $request, Response $response, array $args)
|
||||
{
|
||||
global $timelineData;
|
||||
$data = $request->getParsedBody();
|
||||
if ($args["timeline"] == "edu" && $args["id"] != "undefined")
|
||||
{
|
||||
if (empty($data["dateFrom"]) || empty($data["dateTo"]) || empty($data["grade"]) || empty($data["course"]))
|
||||
{
|
||||
// uh oh sent some empty data
|
||||
$response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
|
||||
return $response->withStatus(400);
|
||||
}
|
||||
|
||||
|
||||
if (!$timelineData->updateEduData($data["dateFrom"], $data["dateTo"], $data["grade"], $data["course"], $args["id"]))
|
||||
{
|
||||
// uh oh something went wrong
|
||||
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
|
||||
return $response->withStatus(500);
|
||||
}
|
||||
|
||||
return $response->withStatus(200);
|
||||
}
|
||||
|
||||
if ($args["timeline"] == "work" && $args["id"] != null)
|
||||
{
|
||||
if (empty($data["dateFrom"]) || empty($data["dateTo"]) || empty($data["companyName"]) || empty($data["area"]) || empty($data["title"]))
|
||||
{
|
||||
// uh oh sent some empty data
|
||||
$response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
|
||||
return $response->withStatus(400);
|
||||
}
|
||||
|
||||
if (!$timelineData->updateWorkData($data["dateFrom"], $data["dateTo"], $data["companyName"], $data["area"], $data["title"], $args["id"]))
|
||||
{
|
||||
// uh oh something went wrong
|
||||
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
|
||||
return $response->withStatus(500);
|
||||
}
|
||||
|
||||
return $response->withStatus(200);
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode(array("error" => "The correct data was not sent")));
|
||||
return $response->withStatus(400);
|
||||
});
|
||||
|
||||
$app->delete("/timelineData/{timeline}/{id}", function (Request $request, Response $response, array $args)
|
||||
{
|
||||
global $timelineData;
|
||||
if ($args["timeline"] == "edu" && $args["id"] != null)
|
||||
{
|
||||
if (!$timelineData->deleteEduData($args["id"]))
|
||||
{
|
||||
// uh oh something went wrong
|
||||
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
|
||||
return $response->withStatus(500);
|
||||
}
|
||||
|
||||
return $response->withStatus(200);
|
||||
}
|
||||
|
||||
if ($args["timeline"] == "work" && $args["id"] != null)
|
||||
{
|
||||
if (!$timelineData->deleteWorkData($args["id"]))
|
||||
{
|
||||
// uh oh something went wrong
|
||||
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
|
||||
return $response->withStatus(500);
|
||||
}
|
||||
|
||||
return $response->withStatus(200);
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode(array("error" => "The correct data was not sent")));
|
||||
return $response->withStatus(400);
|
||||
});
|
||||
|
||||
$app->post("/timelineData/{timeline}", function (Request $request, Response $response, array $args)
|
||||
{
|
||||
global $timelineData;
|
||||
$data = $request->getParsedBody();
|
||||
if ($args["timeline"] == "edu")
|
||||
{
|
||||
if (empty($data["dateFrom"]) || empty($data["dateTo"]) || empty($data["grade"]) || empty($data["course"]))
|
||||
{
|
||||
// uh oh sent some empty data
|
||||
$response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
|
||||
return $response->withStatus(400);
|
||||
}
|
||||
|
||||
$insertedID = $timelineData->addEduData($data["dateFrom"], $data["dateTo"], $data["grade"], $data["course"]);
|
||||
if (!is_int($insertedID))
|
||||
{
|
||||
// uh oh something went wrong
|
||||
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
|
||||
return $response->withStatus(500);
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode(array("ID" => $insertedID)));
|
||||
return $response->withStatus(200);
|
||||
}
|
||||
|
||||
if ($args["timeline"] == "work")
|
||||
{
|
||||
if (empty($data["dateFrom"]) || empty($data["companyName"]) || empty($data["area"]) || empty($data["title"]))
|
||||
{
|
||||
// uh oh sent some empty data
|
||||
$response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
|
||||
return $response->withStatus(400);
|
||||
}
|
||||
|
||||
if (empty($data["dateTo"]))
|
||||
{
|
||||
$data["dateTo"] = "";
|
||||
}
|
||||
|
||||
$insertedID = $timelineData->addWorkData($data["dateFrom"], $data["dateTo"], $data["companyName"], $data["area"], $data["title"]);
|
||||
if (!is_int($insertedID))
|
||||
{
|
||||
// uh oh something went wrong
|
||||
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
|
||||
return $response->withStatus(500);
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode(array("ID" => $insertedID)));
|
||||
return $response->withStatus(200);
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode(array("error" => "The correct data was not sent")));
|
||||
return $response->withStatus(400);
|
||||
});
|
||||
|
||||
$app->get("/projectData", function (Request $request, Response $response)
|
||||
{
|
||||
global $projectData;
|
||||
@ -373,10 +506,8 @@ $app->post("/user/changePassword", function (Request $request, Response $respons
|
||||
return $response->withStatus(500);
|
||||
});
|
||||
|
||||
$app->post("/projectData", function (Request $request, Response $response)
|
||||
{
|
||||
$response->getBody()->write(json_encode(array("test" => "test")));
|
||||
return $response;
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
$app->run();
|
||||
|
1
dist/api/middleware.php
vendored
1
dist/api/middleware.php
vendored
@ -33,6 +33,7 @@ class middleware
|
||||
*/
|
||||
function baseMiddleware(App $app): void
|
||||
{
|
||||
$app->addBodyParsingMiddleware();
|
||||
$app->addRoutingMiddleware();
|
||||
}
|
||||
|
||||
|
126
dist/api/timelineData.php
vendored
126
dist/api/timelineData.php
vendored
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace api;
|
||||
|
||||
use PDO;
|
||||
|
||||
require_once "./config.php";
|
||||
@ -17,7 +19,7 @@ class timelineData
|
||||
function getEduData(): array
|
||||
{
|
||||
$conn = dbConn();
|
||||
$stmt = $conn->prepare("SELECT DATE_FORMAT(startPeriod, '%b, %Y') as startPeriod, DATE_FORMAT(endPeriod, '%b, %Y') as endPeriod, grade, course FROM edu ORDER BY startPeriod DESC;");
|
||||
$stmt = $conn->prepare("SELECT ID, startPeriod, endPeriod, grade, course FROM edu ORDER BY startPeriod DESC;");
|
||||
$stmt->execute();
|
||||
|
||||
// set the resulting array to associative
|
||||
@ -37,7 +39,7 @@ class timelineData
|
||||
function getWorkData(): array
|
||||
{
|
||||
$conn = dbConn();
|
||||
$stmt = $conn->prepare("SELECT DATE_FORMAT(startPeriod, '%b, %Y') as startPeriod, DATE_FORMAT(endPeriod, '%b, %Y') as endPeriod, companyName, area, title FROM work ORDER BY work.startPeriod DESC;");
|
||||
$stmt = $conn->prepare("SELECT ID, startPeriod, endPeriod, companyName, area, title FROM work ORDER BY work.startPeriod DESC;");
|
||||
$stmt->execute();
|
||||
|
||||
// set the resulting array to associative
|
||||
@ -50,4 +52,124 @@ class timelineData
|
||||
return array("errorMessage" => "Error, work data not found");
|
||||
}
|
||||
|
||||
/**
|
||||
* Update education data
|
||||
* @param string $dateFrom - Start date
|
||||
* @param string $dateTo - End date
|
||||
* @param string $grade - Grade
|
||||
* @param string $course - Course
|
||||
* @param string $id - ID of the education data
|
||||
* @return bool - True if successful, false if not
|
||||
*/
|
||||
function updateEduData(string $dateFrom, string $dateTo, string $grade, string $course, string $id): bool
|
||||
{
|
||||
$conn = dbConn();
|
||||
$stmt = $conn->prepare("UPDATE edu SET startPeriod = :dateFrom, endPeriod = :dateTo, grade = :grade, course = :course WHERE ID = :id;");
|
||||
$stmt->bindParam(":dateFrom", $dateFrom);
|
||||
$stmt->bindParam(":dateTo", $dateTo);
|
||||
$stmt->bindParam(":grade", $grade);
|
||||
$stmt->bindParam(":course", $course);
|
||||
$stmt->bindParam(":id", $id);
|
||||
return $stmt->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update work data
|
||||
* @param string $dateFrom - Start date
|
||||
* @param string $dateTo - End date
|
||||
* @param string $companyName - Company name
|
||||
* @param string $area - Area
|
||||
* @param string $title - Title
|
||||
* @param string $id - ID of the work data
|
||||
* @return bool - True if successful, false if not
|
||||
*/
|
||||
public function updateWorkData(string $dateFrom, string $dateTo, string $companyName, string $area, string $title, string $id): bool
|
||||
{
|
||||
$conn = dbConn();
|
||||
$stmt = $conn->prepare("UPDATE work SET startPeriod = :dateFrom, endPeriod = :dateTo, companyName = :companyName, area = :area, title = :title WHERE ID = :id;");
|
||||
$stmt->bindParam(":dateFrom", $dateFrom);
|
||||
$stmt->bindParam(":dateTo", $dateTo);
|
||||
$stmt->bindParam(":companyName", $companyName);
|
||||
$stmt->bindParam(":area", $area);
|
||||
$stmt->bindParam(":title", $title);
|
||||
$stmt->bindParam(":id", $id);
|
||||
return $stmt->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete education data by ID
|
||||
* @param int $id
|
||||
* @return bool - True if successful, false if not
|
||||
*/
|
||||
function deleteEduData(int $id): bool
|
||||
{
|
||||
$conn = dbConn();
|
||||
$stmt = $conn->prepare("DELETE FROM edu WHERE ID = :id;");
|
||||
$stmt->bindParam(":id", $id);
|
||||
return $stmt->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete work data by ID
|
||||
* @param int $id
|
||||
* @return bool - True if successful, false if not
|
||||
*/
|
||||
function deleteWorkData(int $id): bool
|
||||
{
|
||||
$conn = dbConn();
|
||||
$stmt = $conn->prepare("DELETE FROM work WHERE ID = :id;");
|
||||
$stmt->bindParam(":id", $id);
|
||||
return $stmt->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new education data
|
||||
* @param string $dateFrom - Start date
|
||||
* @param string $dateTo - End date
|
||||
* @param string $grade - Grade
|
||||
* @param string $course - Course
|
||||
* @return bool|int - ID of the new education data or false if not successful
|
||||
*/
|
||||
function addEduData(string $dateFrom, string $dateTo, string $grade, string $course): bool|int
|
||||
{
|
||||
$conn = dbConn();
|
||||
$stmt = $conn->prepare("INSERT INTO edu (startPeriod, endPeriod, grade, course) VALUES (:dateFrom, :dateTo, :grade, :course);");
|
||||
$stmt->bindParam(":dateFrom", $dateFrom);
|
||||
$stmt->bindParam(":dateTo", $dateTo);
|
||||
$stmt->bindParam(":grade", $grade);
|
||||
$stmt->bindParam(":course", $course);
|
||||
|
||||
if($stmt->execute())
|
||||
{
|
||||
return $conn->lastInsertId();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new work data
|
||||
* @param string $dateFrom - Start date
|
||||
* @param string $dateTo - End date
|
||||
* @param string $companyName - Company name
|
||||
* @param string $area - Area
|
||||
* @param string $title - Title
|
||||
* @return bool|int - ID of the new work data if successful, false if not
|
||||
*/
|
||||
function addWorkData(string $dateFrom, string $dateTo, string $companyName, string $area, string $title): bool|int
|
||||
{
|
||||
$conn = dbConn();
|
||||
$stmt = $conn->prepare("INSERT INTO work (startPeriod, endPeriod, companyName, area, title) VALUES (:dateFrom, :dateTo, :companyName, :area, :title);");
|
||||
$stmt->bindParam(":dateFrom", $dateFrom);
|
||||
$stmt->bindParam(":dateTo", $dateTo);
|
||||
$stmt->bindParam(":companyName", $companyName);
|
||||
$stmt->bindParam(":area", $area);
|
||||
$stmt->bindParam(":title", $title);
|
||||
|
||||
if($stmt->execute())
|
||||
{
|
||||
return $conn->lastInsertId();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
2
dist/editor/css/main.css
vendored
2
dist/editor/css/main.css
vendored
File diff suppressed because one or more lines are too long
2
dist/editor/editor.html
vendored
2
dist/editor/editor.html
vendored
@ -1 +1 @@
|
||||
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Editor</title><link rel="stylesheet" href="css/main.css"></head><body><nav class="sideNav"><a href="#" class="closeBtn" id="navClose">×</a><ul><li><a href="#" class="active"><span><</span>CV<span>></span></a></li><li><a href="#"><span><</span>Projects<span>></span></a></li><li><a href="#"><span><</span>Settings<span>></span></a></li></ul></nav><main class="editor" style="margin-left: 250px;"><div class="title"><span id="navOpen">☰</span><h1>Editor</h1></div><section id="curriculumVitae"></section><section id="projects"></section><section id="settings"></section></main><script src="js/editor.js"></script></body></html>
|
||||
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Editor</title><link rel="stylesheet" href="css/main.css"><script src="https://kit.fontawesome.com/ed3c25598e.js" crossorigin="anonymous"></script></head><body><nav class="sideNav"><a href="#" class="closeBtn" id="navClose">×</a><ul><li><a href="#" class="active"><span><</span>CV<span>></span></a></li><li><a href="#"><span><</span>Projects<span>></span></a></li><li><a href="#"><span><</span>Settings<span>></span></a></li></ul></nav><main class="editor" style="margin-left: 250px;"><div class="title"><span id="navOpen">☰</span><h1>Editor</h1></div><section id="curriculumVitae"><h2>curriculum vitae</h2><div class="cvGrid"><div><h3>Education</h3><div class="editorContainer"><form action="" method="POST" id="addEdu"><div class="formControl"><label for="dateFromE">Date From</label> <input type="date" id="dateFromE" name="dateFromE"></div><div class="formControl"><label for="dateToE">Date To</label> <input type="date" id="dateToE" name="dateToE"></div><div class="formControl"><label for="grade">Grade</label> <input type="text" id="grade" name="grade"></div><div class="formControl"><label for="courseTitle">Course Title</label> <input type="text" id="courseTitle" name="courseTitle"></div><div class="error hidden" id="eduError"><button class="close" type="button">×</button><div></div></div><input type="submit" class="btn btnPrimary boxShadowIn boxShadowOut" value="Add new course"></form><div class="timeline" id="edu"></div></div></div><div><h3>Work</h3><div class="editorContainer"><form action="" method="POST" id="addWork"><div class="formControl"><label for="dateFromW">Date From</label> <input type="date" id="dateFromW" name="dateFromW"></div><div class="formControl"><label for="dateToW">Date To</label> <input type="date" id="dateToW" name="dateToW"></div><div class="formControl"><label for="company">Company</label> <input type="text" id="company" name="company"></div><div class="formControl"><label for="area">Area</label> <input type="text" id="area" name="area"></div><div class="formControl"><label for="jobTitle">Job Title</label> <input type="text" id="jobTitle" name="jobTitle"></div><div class="error hidden" id="workError"><button class="close" type="button">×</button><div></div></div><input type="submit" class="btn btnPrimary boxShadowIn boxShadowOut" value="Add new job"></form><div class="timeline" id="work"></div></div></div></div></section></main><script src="js/editor.js"></script></body></html>
|
2
dist/editor/js/editor.js
vendored
2
dist/editor/js/editor.js
vendored
File diff suppressed because one or more lines are too long
2
dist/editor/js/index.js
vendored
2
dist/editor/js/index.js
vendored
@ -1 +1 @@
|
||||
function showErrorMessage(e,t){document.querySelector(`#${t}Error`).classList.remove("hidden"),document.querySelector(`#${t}Error div`).innerText=e}function switchView(e,t){document.querySelector(e).classList.toggle("shown"),setTimeout((()=>document.querySelector(e).style.transform="translateX(150vw)"),500),setTimeout((()=>document.querySelector(e).style.display="none"),500),setTimeout((()=>document.querySelector(t).style.removeProperty("display")),200),setTimeout((()=>document.querySelector(t).classList.toggle("shown")),300),setTimeout((()=>document.querySelector(t).style.removeProperty("transform")),400)}document.addEventListener("DOMContentLoaded",(e=>{fetch("/api/user/isLoggedIn").then((e=>{e.ok&&(window.location.href="./editor.html")}))})),document.querySelector("#login form").addEventListener("submit",(e=>{e.preventDefault();let t=new FormData;if(e.target.username.value.length>0&&e.target.password.value.length>0)return t.append("username",e.target.username.value),t.append("password",e.target.password.value),void fetch("/api/user/login",{method:"POST",body:t}).then((e=>{e.ok?window.location.href="./editor.html":400!==e.status?showErrorMessage("Invalid username or password.","login"):showErrorMessage("Please type in a username and password.","login")}));showErrorMessage("Please type in a username and password.","login")})),document.querySelector("#loginError .close").addEventListener("click",(()=>document.querySelector("#loginError").classList.toggle("hidden"))),document.querySelector("#resetError .close").addEventListener("click",(()=>document.querySelector("#resetError").classList.toggle("hidden"))),document.querySelector("#changeError .close").addEventListener("click",(()=>document.querySelector("#changeError").classList.toggle("hidden"))),document.querySelectorAll("form i.fa-eye").forEach((e=>{e.addEventListener("click",(e=>{if("password"===e.target.previousElementSibling.type)return e.target.previousElementSibling.type="text",e.target.classList.remove("fa-eye"),void e.target.classList.add("fa-eye-slash");e.target.previousElementSibling.type="password",e.target.classList.remove("fa-eye-slash"),e.target.classList.add("fa-eye")}))})),document.querySelector("#resetPwd").addEventListener("click",(()=>{switchView("#login","#resetPassword")})),document.querySelector("#loginBtn").addEventListener("click",(()=>{switchView("#resetPassword","#login")})),document.querySelector("#resetPassword form").addEventListener("submit",(e=>{e.preventDefault();let t=new FormData;""!==e.target.email?(window.email=e.target.email.value,t.append("email",e.target.email.value),fetch(`/api/user/checkResetEmail/${e.target.email.value}`).then((e=>{e.ok&&switchView("#resetPassword","#checkResetCode"),showErrorMessage("Invalid email.","reset")}))):showErrorMessage("Please type in your email.","reset")})),document.querySelector("#checkResetCode form").addEventListener("submit",(e=>{e.preventDefault();let t=new FormData;""!==e.target.code.value?(t.append("code",e.target.code.value),fetch(`/api/user/checkResetCode/${e.target.code.value}`).then((e=>{e.ok&&switchView("#checkResetCode","#changePassword"),showErrorMessage("Invalid code.","resetCode")}))):showErrorMessage("Please type in your reset code.","check")})),document.querySelector("#changePassword form").addEventListener("submit",(e=>{e.preventDefault();let t=new FormData;""!==e.target.pass.value||""!==e.target.rePass.value?e.target.pass.value===e.target.rePass.value?(t.append("password",e.target.pass.value),fetch("/api/user/changePassword",{method:"POST",body:t}).then((e=>{e.ok&&switchView("#changePassword","#login"),showErrorMessage("Something went wrong.","change")}))):showErrorMessage("Passwords do not match.","change"):showErrorMessage("Please type in a new password.","change")}));
|
||||
function showErrorMessage(e,t){document.querySelector(`#${t}Error`).classList.remove("hidden"),document.querySelector(`#${t}Error div`).innerText=e}function switchView(e,t){document.querySelector(e).classList.toggle("shown"),setTimeout((()=>document.querySelector(e).style.transform="translateX(150vw)"),500),setTimeout((()=>document.querySelector(e).style.display="none"),500),setTimeout((()=>document.querySelector(t).style.removeProperty("display")),200),setTimeout((()=>document.querySelector(t).classList.toggle("shown")),300),setTimeout((()=>document.querySelector(t).style.removeProperty("transform")),400)}document.addEventListener("DOMContentLoaded",(e=>{fetch("/api/user/isLoggedIn").then((e=>{e.ok&&(window.location.href="./editor.html")}))})),document.querySelector("#login form").addEventListener("submit",(e=>{e.preventDefault();let t=new FormData;if(e.target.username.value.length>0&&e.target.password.value.length>0)return t.append("username",e.target.username.value),t.append("password",e.target.password.value),void fetch("/api/user/login",{method:"POST",body:t}).then((e=>e.json().then((t=>{if(e.ok)return localStorage.setItem("token",t.token),void(window.location.href="./editor.html");400!==e.status?showErrorMessage("Invalid username or password.","login"):showErrorMessage("Please type in a username and password.","login")}))));showErrorMessage("Please type in a username and password.","login")})),document.querySelector("#loginError .close").addEventListener("click",(()=>document.querySelector("#loginError").classList.toggle("hidden"))),document.querySelector("#resetError .close").addEventListener("click",(()=>document.querySelector("#resetError").classList.toggle("hidden"))),document.querySelector("#changeError .close").addEventListener("click",(()=>document.querySelector("#changeError").classList.toggle("hidden"))),document.querySelectorAll("form i.fa-eye").forEach((e=>{e.addEventListener("click",(e=>{if("password"===e.target.previousElementSibling.type)return e.target.previousElementSibling.type="text",e.target.classList.remove("fa-eye"),void e.target.classList.add("fa-eye-slash");e.target.previousElementSibling.type="password",e.target.classList.remove("fa-eye-slash"),e.target.classList.add("fa-eye")}))})),document.querySelector("#resetPwd").addEventListener("click",(()=>{switchView("#login","#resetPassword")})),document.querySelector("#loginBtn").addEventListener("click",(()=>{switchView("#resetPassword","#login")})),document.querySelector("#resetPassword form").addEventListener("submit",(e=>{e.preventDefault();let t=new FormData;""!==e.target.email?(window.email=e.target.email.value,t.append("email",e.target.email.value),fetch(`/api/user/checkResetEmail/${e.target.email.value}`).then((e=>{e.ok&&switchView("#resetPassword","#checkResetCode"),showErrorMessage("Invalid email.","reset")}))):showErrorMessage("Please type in your email.","reset")})),document.querySelector("#checkResetCode form").addEventListener("submit",(e=>{e.preventDefault();let t=new FormData;""!==e.target.code.value?(t.append("code",e.target.code.value),fetch(`/api/user/checkResetCode/${e.target.code.value}`).then((e=>{e.ok&&switchView("#checkResetCode","#changePassword"),showErrorMessage("Invalid code.","resetCode")}))):showErrorMessage("Please type in your reset code.","check")})),document.querySelector("#changePassword form").addEventListener("submit",(e=>{e.preventDefault();let t=new FormData;""!==e.target.pass.value||""!==e.target.rePass.value?e.target.pass.value===e.target.rePass.value?(t.append("password",e.target.pass.value),fetch("/api/user/changePassword",{method:"POST",body:t}).then((e=>{e.ok&&switchView("#changePassword","#login"),showErrorMessage("Something went wrong.","change")}))):showErrorMessage("Passwords do not match.","change"):showErrorMessage("Please type in a new password.","change")}));
|
2
dist/js/main.js
vendored
2
dist/js/main.js
vendored
File diff suppressed because one or more lines are too long
@ -59,6 +59,139 @@ $app->get("/timelineData/{timeline}", function (Request $request, Response $resp
|
||||
return $response->withStatus(404);
|
||||
});
|
||||
|
||||
$app->patch("/timelineData/{timeline}/{id}", function (Request $request, Response $response, array $args)
|
||||
{
|
||||
global $timelineData;
|
||||
$data = $request->getParsedBody();
|
||||
if ($args["timeline"] == "edu" && $args["id"] != "undefined")
|
||||
{
|
||||
if (empty($data["dateFrom"]) || empty($data["dateTo"]) || empty($data["grade"]) || empty($data["course"]))
|
||||
{
|
||||
// uh oh sent some empty data
|
||||
$response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
|
||||
return $response->withStatus(400);
|
||||
}
|
||||
|
||||
|
||||
if (!$timelineData->updateEduData($data["dateFrom"], $data["dateTo"], $data["grade"], $data["course"], $args["id"]))
|
||||
{
|
||||
// uh oh something went wrong
|
||||
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
|
||||
return $response->withStatus(500);
|
||||
}
|
||||
|
||||
return $response->withStatus(200);
|
||||
}
|
||||
|
||||
if ($args["timeline"] == "work" && $args["id"] != null)
|
||||
{
|
||||
if (empty($data["dateFrom"]) || empty($data["dateTo"]) || empty($data["companyName"]) || empty($data["area"]) || empty($data["title"]))
|
||||
{
|
||||
// uh oh sent some empty data
|
||||
$response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
|
||||
return $response->withStatus(400);
|
||||
}
|
||||
|
||||
if (!$timelineData->updateWorkData($data["dateFrom"], $data["dateTo"], $data["companyName"], $data["area"], $data["title"], $args["id"]))
|
||||
{
|
||||
// uh oh something went wrong
|
||||
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
|
||||
return $response->withStatus(500);
|
||||
}
|
||||
|
||||
return $response->withStatus(200);
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode(array("error" => "The correct data was not sent")));
|
||||
return $response->withStatus(400);
|
||||
});
|
||||
|
||||
$app->delete("/timelineData/{timeline}/{id}", function (Request $request, Response $response, array $args)
|
||||
{
|
||||
global $timelineData;
|
||||
if ($args["timeline"] == "edu" && $args["id"] != null)
|
||||
{
|
||||
if (!$timelineData->deleteEduData($args["id"]))
|
||||
{
|
||||
// uh oh something went wrong
|
||||
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
|
||||
return $response->withStatus(500);
|
||||
}
|
||||
|
||||
return $response->withStatus(200);
|
||||
}
|
||||
|
||||
if ($args["timeline"] == "work" && $args["id"] != null)
|
||||
{
|
||||
if (!$timelineData->deleteWorkData($args["id"]))
|
||||
{
|
||||
// uh oh something went wrong
|
||||
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
|
||||
return $response->withStatus(500);
|
||||
}
|
||||
|
||||
return $response->withStatus(200);
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode(array("error" => "The correct data was not sent")));
|
||||
return $response->withStatus(400);
|
||||
});
|
||||
|
||||
$app->post("/timelineData/{timeline}", function (Request $request, Response $response, array $args)
|
||||
{
|
||||
global $timelineData;
|
||||
$data = $request->getParsedBody();
|
||||
if ($args["timeline"] == "edu")
|
||||
{
|
||||
if (empty($data["dateFrom"]) || empty($data["dateTo"]) || empty($data["grade"]) || empty($data["course"]))
|
||||
{
|
||||
// uh oh sent some empty data
|
||||
$response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
|
||||
return $response->withStatus(400);
|
||||
}
|
||||
|
||||
$insertedID = $timelineData->addEduData($data["dateFrom"], $data["dateTo"], $data["grade"], $data["course"]);
|
||||
if (!is_int($insertedID))
|
||||
{
|
||||
// uh oh something went wrong
|
||||
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
|
||||
return $response->withStatus(500);
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode(array("ID" => $insertedID)));
|
||||
return $response->withStatus(200);
|
||||
}
|
||||
|
||||
if ($args["timeline"] == "work")
|
||||
{
|
||||
if (empty($data["dateFrom"]) || empty($data["companyName"]) || empty($data["area"]) || empty($data["title"]))
|
||||
{
|
||||
// uh oh sent some empty data
|
||||
$response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
|
||||
return $response->withStatus(400);
|
||||
}
|
||||
|
||||
if (empty($data["dateTo"]))
|
||||
{
|
||||
$data["dateTo"] = "";
|
||||
}
|
||||
|
||||
$insertedID = $timelineData->addWorkData($data["dateFrom"], $data["dateTo"], $data["companyName"], $data["area"], $data["title"]);
|
||||
if (!is_int($insertedID))
|
||||
{
|
||||
// uh oh something went wrong
|
||||
$response->getBody()->write(json_encode(array("error" => "Something went wrong")));
|
||||
return $response->withStatus(500);
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode(array("ID" => $insertedID)));
|
||||
return $response->withStatus(200);
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode(array("error" => "The correct data was not sent")));
|
||||
return $response->withStatus(400);
|
||||
});
|
||||
|
||||
$app->get("/projectData", function (Request $request, Response $response)
|
||||
{
|
||||
global $projectData;
|
||||
@ -373,10 +506,8 @@ $app->post("/user/changePassword", function (Request $request, Response $respons
|
||||
return $response->withStatus(500);
|
||||
});
|
||||
|
||||
$app->post("/projectData", function (Request $request, Response $response)
|
||||
{
|
||||
$response->getBody()->write(json_encode(array("test" => "test")));
|
||||
return $response;
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
$app->run();
|
||||
|
@ -33,6 +33,7 @@ class middleware
|
||||
*/
|
||||
function baseMiddleware(App $app): void
|
||||
{
|
||||
$app->addBodyParsingMiddleware();
|
||||
$app->addRoutingMiddleware();
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace api;
|
||||
|
||||
use PDO;
|
||||
|
||||
require_once "./config.php";
|
||||
@ -17,7 +19,7 @@ class timelineData
|
||||
function getEduData(): array
|
||||
{
|
||||
$conn = dbConn();
|
||||
$stmt = $conn->prepare("SELECT DATE_FORMAT(startPeriod, '%b, %Y') as startPeriod, DATE_FORMAT(endPeriod, '%b, %Y') as endPeriod, grade, course FROM edu ORDER BY startPeriod DESC;");
|
||||
$stmt = $conn->prepare("SELECT ID, startPeriod, endPeriod, grade, course FROM edu ORDER BY startPeriod DESC;");
|
||||
$stmt->execute();
|
||||
|
||||
// set the resulting array to associative
|
||||
@ -37,7 +39,7 @@ class timelineData
|
||||
function getWorkData(): array
|
||||
{
|
||||
$conn = dbConn();
|
||||
$stmt = $conn->prepare("SELECT DATE_FORMAT(startPeriod, '%b, %Y') as startPeriod, DATE_FORMAT(endPeriod, '%b, %Y') as endPeriod, companyName, area, title FROM work ORDER BY work.startPeriod DESC;");
|
||||
$stmt = $conn->prepare("SELECT ID, startPeriod, endPeriod, companyName, area, title FROM work ORDER BY work.startPeriod DESC;");
|
||||
$stmt->execute();
|
||||
|
||||
// set the resulting array to associative
|
||||
@ -50,4 +52,124 @@ class timelineData
|
||||
return array("errorMessage" => "Error, work data not found");
|
||||
}
|
||||
|
||||
/**
|
||||
* Update education data
|
||||
* @param string $dateFrom - Start date
|
||||
* @param string $dateTo - End date
|
||||
* @param string $grade - Grade
|
||||
* @param string $course - Course
|
||||
* @param string $id - ID of the education data
|
||||
* @return bool - True if successful, false if not
|
||||
*/
|
||||
function updateEduData(string $dateFrom, string $dateTo, string $grade, string $course, string $id): bool
|
||||
{
|
||||
$conn = dbConn();
|
||||
$stmt = $conn->prepare("UPDATE edu SET startPeriod = :dateFrom, endPeriod = :dateTo, grade = :grade, course = :course WHERE ID = :id;");
|
||||
$stmt->bindParam(":dateFrom", $dateFrom);
|
||||
$stmt->bindParam(":dateTo", $dateTo);
|
||||
$stmt->bindParam(":grade", $grade);
|
||||
$stmt->bindParam(":course", $course);
|
||||
$stmt->bindParam(":id", $id);
|
||||
return $stmt->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update work data
|
||||
* @param string $dateFrom - Start date
|
||||
* @param string $dateTo - End date
|
||||
* @param string $companyName - Company name
|
||||
* @param string $area - Area
|
||||
* @param string $title - Title
|
||||
* @param string $id - ID of the work data
|
||||
* @return bool - True if successful, false if not
|
||||
*/
|
||||
public function updateWorkData(string $dateFrom, string $dateTo, string $companyName, string $area, string $title, string $id): bool
|
||||
{
|
||||
$conn = dbConn();
|
||||
$stmt = $conn->prepare("UPDATE work SET startPeriod = :dateFrom, endPeriod = :dateTo, companyName = :companyName, area = :area, title = :title WHERE ID = :id;");
|
||||
$stmt->bindParam(":dateFrom", $dateFrom);
|
||||
$stmt->bindParam(":dateTo", $dateTo);
|
||||
$stmt->bindParam(":companyName", $companyName);
|
||||
$stmt->bindParam(":area", $area);
|
||||
$stmt->bindParam(":title", $title);
|
||||
$stmt->bindParam(":id", $id);
|
||||
return $stmt->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete education data by ID
|
||||
* @param int $id
|
||||
* @return bool - True if successful, false if not
|
||||
*/
|
||||
function deleteEduData(int $id): bool
|
||||
{
|
||||
$conn = dbConn();
|
||||
$stmt = $conn->prepare("DELETE FROM edu WHERE ID = :id;");
|
||||
$stmt->bindParam(":id", $id);
|
||||
return $stmt->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete work data by ID
|
||||
* @param int $id
|
||||
* @return bool - True if successful, false if not
|
||||
*/
|
||||
function deleteWorkData(int $id): bool
|
||||
{
|
||||
$conn = dbConn();
|
||||
$stmt = $conn->prepare("DELETE FROM work WHERE ID = :id;");
|
||||
$stmt->bindParam(":id", $id);
|
||||
return $stmt->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new education data
|
||||
* @param string $dateFrom - Start date
|
||||
* @param string $dateTo - End date
|
||||
* @param string $grade - Grade
|
||||
* @param string $course - Course
|
||||
* @return bool|int - ID of the new education data or false if not successful
|
||||
*/
|
||||
function addEduData(string $dateFrom, string $dateTo, string $grade, string $course): bool|int
|
||||
{
|
||||
$conn = dbConn();
|
||||
$stmt = $conn->prepare("INSERT INTO edu (startPeriod, endPeriod, grade, course) VALUES (:dateFrom, :dateTo, :grade, :course);");
|
||||
$stmt->bindParam(":dateFrom", $dateFrom);
|
||||
$stmt->bindParam(":dateTo", $dateTo);
|
||||
$stmt->bindParam(":grade", $grade);
|
||||
$stmt->bindParam(":course", $course);
|
||||
|
||||
if($stmt->execute())
|
||||
{
|
||||
return $conn->lastInsertId();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new work data
|
||||
* @param string $dateFrom - Start date
|
||||
* @param string $dateTo - End date
|
||||
* @param string $companyName - Company name
|
||||
* @param string $area - Area
|
||||
* @param string $title - Title
|
||||
* @return bool|int - ID of the new work data if successful, false if not
|
||||
*/
|
||||
function addWorkData(string $dateFrom, string $dateTo, string $companyName, string $area, string $title): bool|int
|
||||
{
|
||||
$conn = dbConn();
|
||||
$stmt = $conn->prepare("INSERT INTO work (startPeriod, endPeriod, companyName, area, title) VALUES (:dateFrom, :dateTo, :companyName, :area, :title);");
|
||||
$stmt->bindParam(":dateFrom", $dateFrom);
|
||||
$stmt->bindParam(":dateTo", $dateTo);
|
||||
$stmt->bindParam(":companyName", $companyName);
|
||||
$stmt->bindParam(":area", $area);
|
||||
$stmt->bindParam(":title", $title);
|
||||
|
||||
if($stmt->execute())
|
||||
{
|
||||
return $conn->lastInsertId();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
149
src/editor/css/editor.css
Normal file
149
src/editor/css/editor.css
Normal file
@ -0,0 +1,149 @@
|
||||
/*** Main editor styles ***/
|
||||
|
||||
section#curriculumVitae, section#projects, section#settings {
|
||||
margin: 0 2em;
|
||||
}
|
||||
|
||||
input[type="submit"] {
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
.edit, .delete {
|
||||
border: none;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
outline: none;
|
||||
background-color: var(--primaryDefault);
|
||||
color: #FFFFFF;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.timelineHeader {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
div.editorContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: baseline;
|
||||
gap: 2em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
div.editorContainer > * {
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
div.modifyBtnContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
div.dateContainer, div.companyAreaContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
section#curriculumVitae .timeline {
|
||||
position: relative;
|
||||
max-width: 30em;
|
||||
gap: 1em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
section#curriculumVitae .timelineItem {
|
||||
color: #FFFFFF;
|
||||
border: 2px solid var(--timelineItemBrdr);
|
||||
-webkit-border-radius: 10px;
|
||||
-moz-border-radius: 10px;
|
||||
border-radius: 10px;
|
||||
padding: 0 1rem;
|
||||
position: relative;
|
||||
background-color: var(--primaryHover);
|
||||
}
|
||||
|
||||
section#curriculumVitae .timelineItem.editing {
|
||||
color: #000000;
|
||||
border: 5px solid var(--primaryDefault);
|
||||
padding: 0.5em;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
form div.gradeContainer.formControl {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
section#curriculumVitae form.timelineItem:not(.editing) .edit,
|
||||
section#curriculumVitae form.timelineItem:not(.editing) .delete {
|
||||
color: var(--primaryHover);
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
section#curriculumVitae form.timelineItem:not(.editing) div.dateContainer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
section#curriculumVitae form.timelineItem.editing .timelineHeader {
|
||||
display: none;
|
||||
}
|
||||
|
||||
section#curriculumVitae form.timelineItem.editing div.gradeContainer.formControl {
|
||||
gap: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
section#curriculumVitae form.timelineItem:not(.editing) div.gradeContainer.formControl input,
|
||||
section#curriculumVitae form.timelineItem:not(.editing) div.companyAreaContainer.formControl input,
|
||||
section#curriculumVitae form.timelineItem:not(.editing) .formControl .courseText,
|
||||
section#curriculumVitae form.timelineItem:not(.editing) .formControl .jobTitleText {
|
||||
outline: none;
|
||||
border: none;
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
section#curriculumVitae form.timelineItem:not(.editing) div.gradeContainer.formControl > *,
|
||||
section#curriculumVitae form.timelineItem:not(.editing) div.companyAreaContainer.formControl > * {
|
||||
color: hsl(0, 0%, 90%);
|
||||
}
|
||||
|
||||
section#curriculumVitae form.timelineItem:not(.editing) .formControl .courseText,
|
||||
section#curriculumVitae form.timelineItem:not(.editing) .formControl .jobTitleText {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
section#curriculumVitae form.timelineItem:not(.editing) div.gradeContainer.formControl input {
|
||||
padding: 0 0.25em;
|
||||
}
|
||||
|
||||
section#curriculumVitae form.timelineItem:not(.editing) div.formControl .courseText,
|
||||
section#curriculumVitae form.timelineItem:not(.editing) .formControl .courseText {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
section#curriculumVitae form.timelineItem:not(.editing) input[type=submit] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.courseText {
|
||||
resize: none;
|
||||
}
|
||||
|
||||
section#curriculumVitae form.timelineItem:not(.editing) div.companyAreaContainer input {
|
||||
width: 30%;
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
@import "../../css/templateStyles.css";
|
||||
@import "login.css";
|
||||
@import "nav.css";
|
||||
@import "editor.css";
|
||||
|
||||
/*** Media Queries ***/
|
||||
|
||||
|
@ -71,6 +71,6 @@ main.editor {
|
||||
|
||||
#navOpen {
|
||||
visibility: hidden;
|
||||
padding-left: 0.25em;
|
||||
padding: 0.25em 0 0 0.25em;
|
||||
align-self: flex-start;
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>Editor</title>
|
||||
<link rel="stylesheet" href="css/main.css">
|
||||
<script src="https://kit.fontawesome.com/ed3c25598e.js" crossorigin="anonymous"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="sideNav">
|
||||
@ -17,8 +18,95 @@
|
||||
<main class="editor" style="margin-left: 250px;">
|
||||
<div class="title">
|
||||
<span id="navOpen">☰</span>
|
||||
|
||||
<h1>Editor</h1>
|
||||
</div>
|
||||
<section id="curriculumVitae">
|
||||
<h2>curriculum vitae</h2>
|
||||
<div class="cvGrid">
|
||||
<div>
|
||||
<h3>Education</h3>
|
||||
|
||||
<div class="editorContainer">
|
||||
<form action="" method="POST" id="addEdu">
|
||||
<div class="formControl">
|
||||
<label for="dateFromE">Date From</label>
|
||||
<input type="date" id="dateFromE" name="dateFromE">
|
||||
</div>
|
||||
|
||||
<div class="formControl">
|
||||
<label for="dateToE">Date To</label>
|
||||
<input type="date" id="dateToE" name="dateToE">
|
||||
</div>
|
||||
|
||||
<div class="formControl">
|
||||
<label for="grade">Grade</label>
|
||||
<input type="text" id="grade" name="grade">
|
||||
</div>
|
||||
|
||||
<div class="formControl">
|
||||
<label for="courseTitle">Course Title</label>
|
||||
<input type="text" id="courseTitle" name="courseTitle">
|
||||
</div>
|
||||
|
||||
<div class="error hidden" id="eduError">
|
||||
<button class="close" type="button">×</button>
|
||||
<div></div>
|
||||
</div>
|
||||
|
||||
<input type="submit" class="btn btnPrimary boxShadowIn boxShadowOut" value="Add new course">
|
||||
</form>
|
||||
|
||||
<div class="timeline" id="edu">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Work</h3>
|
||||
|
||||
<div class="editorContainer">
|
||||
<form action="" method="POST" id="addWork">
|
||||
<div class="formControl">
|
||||
<label for="dateFromW">Date From</label>
|
||||
<input type="date" id="dateFromW" name="dateFromW">
|
||||
</div>
|
||||
|
||||
<div class="formControl">
|
||||
<label for="dateToW">Date To</label>
|
||||
<input type="date" id="dateToW" name="dateToW">
|
||||
</div>
|
||||
|
||||
<div class="formControl">
|
||||
<label for="company">Company</label>
|
||||
<input type="text" id="company" name="company">
|
||||
</div>
|
||||
|
||||
<div class="formControl">
|
||||
<label for="area">Area</label>
|
||||
<input type="text" id="area" name="area">
|
||||
</div>
|
||||
|
||||
<div class="formControl">
|
||||
<label for="jobTitle">Job Title</label>
|
||||
<input type="text" id="jobTitle" name="jobTitle">
|
||||
</div>
|
||||
|
||||
<div class="error hidden" id="workError">
|
||||
<button class="close" type="button">×</button>
|
||||
<div></div>
|
||||
</div>
|
||||
|
||||
<input type="submit" class="btn btnPrimary boxShadowIn boxShadowOut" value="Add new job">
|
||||
</form>
|
||||
|
||||
<div class="timeline" id="work">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script src="js/editor.js"></script>
|
||||
|
@ -1,14 +1,51 @@
|
||||
let dateOptions = {month: 'short', year: 'numeric'};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', e =>
|
||||
document.addEventListener('DOMContentLoaded', () =>
|
||||
{
|
||||
// check if the user is logged in, if not redirect to log in
|
||||
/* fetch('/api/user/isLoggedIn').then(res =>
|
||||
fetch('/api/user/isLoggedIn').then(res =>
|
||||
{
|
||||
if (!res.ok)
|
||||
{
|
||||
window.location.href = './';
|
||||
}
|
||||
});*/
|
||||
});
|
||||
document.querySelector("#dateFromE").max = new Date().toISOString().split("T")[0];
|
||||
document.querySelector("#dateFromW").max = new Date().toISOString().split("T")[0];
|
||||
|
||||
|
||||
fetch("/api/timelineData/edu").then(res =>
|
||||
{
|
||||
res.json().then(json =>
|
||||
{
|
||||
if (res.ok)
|
||||
{
|
||||
for (let i = 0; i < json.length; i++)
|
||||
{
|
||||
addEduData(json[i].ID, json[i].startPeriod, json[i].endPeriod, json[i].grade, json[i].course);
|
||||
}
|
||||
return;
|
||||
}
|
||||
document.querySelector("#edu").innerHTML = "No education data found";
|
||||
})
|
||||
});
|
||||
|
||||
fetch("/api/timelineData/work").then(res =>
|
||||
{
|
||||
res.json().then(json =>
|
||||
{
|
||||
if (res.ok)
|
||||
{
|
||||
for (let i = 0; i < json.length; i++)
|
||||
{
|
||||
let endPeriod = json[i].endPeriod === null ? "Present" : json[i].endPeriod;
|
||||
addWorkData(json[i].ID, json[i].startPeriod, endPeriod, json[i].companyName, json[i].area, json[i].title);
|
||||
}
|
||||
return;
|
||||
}
|
||||
document.querySelector("#edu").innerHTML = "No education data found";
|
||||
})
|
||||
});
|
||||
})
|
||||
|
||||
document.querySelector("#navOpen").addEventListener("click", e =>
|
||||
@ -18,9 +55,353 @@ document.querySelector("#navOpen").addEventListener("click", e =>
|
||||
e.target.style.removeProperty("visibility");
|
||||
});
|
||||
|
||||
document.querySelector("#navClose").addEventListener("click", e =>
|
||||
document.querySelector("#navClose").addEventListener("click", () =>
|
||||
{
|
||||
document.querySelector("nav.sideNav").style.width = "0";
|
||||
document.querySelector("main.editor").style.marginLeft = "0";
|
||||
document.querySelector("#navOpen").style.visibility = "visible";
|
||||
});
|
||||
|
||||
document.querySelector("#addEdu").addEventListener("submit", e =>
|
||||
{
|
||||
e.preventDefault();
|
||||
let data = new FormData();
|
||||
data.append("dateFrom", document.querySelector("#dateFromE").value);
|
||||
data.append("dateTo", document.querySelector("#dateToE").value);
|
||||
data.append("grade", document.querySelector("#grade").value);
|
||||
data.append("course", document.querySelector("#courseTitle").value);
|
||||
|
||||
fetch("/api/timelineData/edu", {
|
||||
method: "POST",
|
||||
body: data,
|
||||
headers: {
|
||||
"Authentication": localStorage.getItem("token")
|
||||
}
|
||||
}).then(res => res.json().then(json =>
|
||||
{
|
||||
if (res.ok)
|
||||
{
|
||||
addEduData(json.ID, data.get("dateFrom"), data.get("dateTo"), data.get("grade"), data.get("course"), true);
|
||||
document.querySelector("#addEdu").reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status === 401)
|
||||
{
|
||||
window.location.href = "./";
|
||||
return;
|
||||
}
|
||||
|
||||
document.querySelector("#eduError").classList.remove("hidden");
|
||||
document.querySelector("#eduError div").innerHTML = json.error;
|
||||
}));
|
||||
});
|
||||
|
||||
document.querySelector("#addWork").addEventListener("submit", e =>
|
||||
{
|
||||
e.preventDefault();
|
||||
let data = new FormData();
|
||||
data.append("dateFrom", document.querySelector("#dateFromW").value);
|
||||
data.append("dateTo", document.querySelector("#dateToW").value);
|
||||
data.append("companyName", document.querySelector("#company").value);
|
||||
data.append("area", document.querySelector("#area").value);
|
||||
data.append("title", document.querySelector("#jobTitle").value);
|
||||
|
||||
fetch("/api/timelineData/work", {
|
||||
method: "POST",
|
||||
body: data,
|
||||
headers: {
|
||||
"Authentication": localStorage.getItem("token")
|
||||
}
|
||||
}).then(res => res.json().then(json =>
|
||||
{
|
||||
if (res.ok)
|
||||
{
|
||||
let endPeriod = data.get("dateTo") === null ? "Present" : data.get("dateTo ");
|
||||
addWorkData(json.ID, data.get("dateFrom"), endPeriod, data.get("companyName"), data.get("area"), data.get("title"), true);
|
||||
document.querySelector("#addEdu").reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status === 401)
|
||||
{
|
||||
window.location.href = "./";
|
||||
return;
|
||||
}
|
||||
|
||||
document.querySelector("#eduError").classList.remove("hidden");
|
||||
document.querySelector("#eduError div").innerHTML = json.error;
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Switches the timeline item between edit and view mode
|
||||
* @param id the id of the timeline item
|
||||
*/
|
||||
function edit(id)
|
||||
{
|
||||
document.querySelector("#timelineItem" + id).classList.toggle("editing");
|
||||
if (id.includes("e"))
|
||||
{
|
||||
document.querySelector("#grade" + id).toggleAttribute("disabled");
|
||||
document.querySelector("#course" + id).toggleAttribute("disabled");
|
||||
return;
|
||||
}
|
||||
document.querySelector("#companyName" + id).toggleAttribute("disabled");
|
||||
document.querySelector("#area" + id).toggleAttribute("disabled");
|
||||
document.querySelector("#jobTitle" + id).toggleAttribute("disabled");
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the education timeline item with the given id
|
||||
* @param ID - the id of the course timeline item
|
||||
* @param startPeriod - the start date of the course
|
||||
* @param endPeriod - the end date of the course
|
||||
* @param grade - the grade of the course
|
||||
* @param course - the name of the course
|
||||
* @param prepend - whether to prepend the timeline item to the timeline
|
||||
*/
|
||||
function addEduData(ID, startPeriod, endPeriod, grade, course, prepend=false)
|
||||
{
|
||||
let id = ID + "e";
|
||||
let timelineItem = document.createElement("form")
|
||||
timelineItem.id = "timelineItem" + id;
|
||||
timelineItem.classList.add("timelineItem");
|
||||
timelineItem.onsubmit = e => updateEduItem(ID, e);
|
||||
timelineItem.innerHTML = `
|
||||
<div class="modifyBtnContainer">
|
||||
<button class="edit" type="button" id="edit${id}" onclick="edit('${id}')"><i class="fa-solid fa-pen-to-square"></i></button>
|
||||
<button class="delete" type="button" id="delete${id}" onclick="deleteEduItem(${ID})"><i class="fa-solid fa-trash"></i></button>
|
||||
</div>
|
||||
<div class="dateContainer formControl">
|
||||
<input type="date" name="dateFrom${id}" id="dateFrom${id}" onload="this.max = new Date().toISOString().split('T')[0]" value="${startPeriod}">
|
||||
-
|
||||
<input type="date" name="dateTo${id}" id="dateTo${id}" value="${endPeriod}">
|
||||
</div>
|
||||
<h3 class="timelineHeader" id="timelineHeader${id}">${new Date(startPeriod).toLocaleString('en-gb', dateOptions)} - ${new Date(endPeriod).toLocaleString('en-gb', dateOptions)}</h3>
|
||||
<div class="gradeContainer formControl">
|
||||
<label for="grade${id}">Grade:</label>
|
||||
<input type="text" name="grade${id}" id="grade${id}" value="${grade}" disabled>
|
||||
</div>
|
||||
<div class="formControl">
|
||||
<textarea class="courseText" name="course${id}" id="course${id}" cols="10" rows="3" disabled>${course}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="error hidden" id="eduError${id}">
|
||||
<button class="close" type="button" onclick="this.parentElement.classList.toggle('hidden');">×</button>
|
||||
<div></div>
|
||||
</div>
|
||||
<input type="submit" value="Change">
|
||||
`;
|
||||
if (prepend)
|
||||
{
|
||||
document.querySelector("#edu").prepend(timelineItem);
|
||||
return;
|
||||
}
|
||||
document.getElementById("edu").appendChild(timelineItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new work timeline item to the page
|
||||
* @param ID - the id of the work timeline item
|
||||
* @param startPeriod - the start date of the job
|
||||
* @param endPeriod - the end date of the job
|
||||
* @param companyName - the name of the company
|
||||
* @param area - the area of the company
|
||||
* @param jobTitle - the job title
|
||||
* @param prepend - whether to prepend the timeline item to the timeline
|
||||
*/
|
||||
function addWorkData(ID, startPeriod, endPeriod, companyName, area, jobTitle, prepend=false)
|
||||
{
|
||||
let id = ID + "w";
|
||||
let timelineItem = document.createElement("form")
|
||||
timelineItem.id = "timelineItem" + id;
|
||||
timelineItem.classList.add("timelineItem");
|
||||
timelineItem.onsubmit = e => updateWorkItem(ID, e);
|
||||
timelineItem.innerHTML = `
|
||||
<div class="modifyBtnContainer">
|
||||
<button class="edit" type="button" id="edit${id}" onclick="edit('${id}')"><i class="fa-solid fa-pen-to-square"></i></button>
|
||||
<button class="delete" type="button" id="delete${id}" onclick="deleteWorkItem(${ID})"><i class="fa-solid fa-trash"></i></button>
|
||||
</div>
|
||||
<div class="dateContainer formControl">
|
||||
<input type="date" name="dateFrom${id}" id="dateFrom${id}" onload="this.max = new Date().toISOString().split('T')[0]" value="${startPeriod}">
|
||||
-
|
||||
<input type="date" name="dateTo${id}" id="dateTo${id}" value="${endPeriod === 'Present' ? '' : endPeriod}">
|
||||
</div>
|
||||
<h3 class="timelineHeader" id="timelineHeader${id}">${new Date(startPeriod).toLocaleString('en-gb', dateOptions)} - ${endPeriod === 'Present' ? '' : new Date(endPeriod).toLocaleString('en-gb', dateOptions)}</h3>
|
||||
<div class="companyAreaContainer formControl">
|
||||
<input type="text" name="companyName${id}" id="companyName${id}" value="${companyName}" disabled>
|
||||
-
|
||||
<input type="text" name="area${id}" id="area${id}" value="${area}" disabled>
|
||||
</div>
|
||||
<div class="formControl">
|
||||
<textarea class="jobTitleText" name="jobTitle${id}" id="jobTitle${id}" cols="10" rows="3" disabled>${jobTitle}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="error hidden" id="workError${id}">
|
||||
<button class="close" type="button" onclick="this.parentElement.classList.toggle('hidden');">×</button>
|
||||
<div></div>
|
||||
</div>
|
||||
<input type="submit" value="Change">
|
||||
`;
|
||||
if (prepend)
|
||||
{
|
||||
document.querySelector("#work").prepend(timelineItem);
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById("work").appendChild(timelineItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the edu timeline item with the given id
|
||||
* @param id the id of the edu timeline item
|
||||
* @param e the event that triggered the function
|
||||
*/
|
||||
function updateEduItem(id, e)
|
||||
{
|
||||
e.preventDefault();
|
||||
let data = {}
|
||||
data["dateFrom"] = document.querySelector(`#dateFrom${id}e`).value;
|
||||
data["dateTo"] = document.querySelector(`#dateTo${id}e`).value;
|
||||
data["grade"] = document.querySelector(`#grade${id}e`).value;
|
||||
data["course"] = document.querySelector(`#course${id}e`).value;
|
||||
|
||||
fetch("/api/timelineData/edu/" + id, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(data),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
}
|
||||
}).then(res =>
|
||||
{
|
||||
if (res.ok)
|
||||
{
|
||||
document.querySelector(`#timelineHeader${id}e`).innerHTML = new Date(document.querySelector(`#dateFrom${id}e`).value).toLocaleString('en-gb', dateOptions) + " - " + new Date(document.querySelector(`#dateTo${id}e`).value).toLocaleString('en-gb', dateOptions);
|
||||
document.querySelector(`#timelineItem${id}e`).classList.toggle("editing");
|
||||
document.querySelector(`#grade${id}e`).setAttribute("disabled", "");
|
||||
document.querySelector(`#course${id}e`).setAttribute("disabled", "");
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status === 401)
|
||||
{
|
||||
window.location.href = "./";
|
||||
return;
|
||||
}
|
||||
|
||||
res.json().then(json =>
|
||||
{
|
||||
document.querySelector(`#eduError${id}e`).classList.remove("hidden");
|
||||
document.querySelector(`#eduError${id}e div`).innerHTML = json.error;
|
||||
});
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the work timeline item with the given id
|
||||
* @param id the id of the work timeline item
|
||||
* @param e the event that triggered the function
|
||||
*/
|
||||
function updateWorkItem(id, e)
|
||||
{
|
||||
e.preventDefault();
|
||||
let data = {}
|
||||
data["dateFrom"] = document.querySelector(`#dateFrom${id}w`).value;
|
||||
data["dateTo"] = document.querySelector(`#dateTo${id}w`).value;
|
||||
data["companyName"] = document.querySelector(`#companyName${id}w`).value;
|
||||
data["area"] = document.querySelector(`#area${id}w`).value;
|
||||
data["title"] = document.querySelector(`#jobTitle${id}w`).value;
|
||||
|
||||
fetch("/api/timelineData/work/" + id, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(data),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
}
|
||||
}).then(res =>
|
||||
{
|
||||
if(res.ok)
|
||||
{
|
||||
document.querySelector(`#timelineHeader${id}w`).innerHTML = new Date(document.querySelector(`#dateFrom${id}w`).value).toLocaleString('en-gb', dateOptions) + " - " + new Date(document.querySelector(`#dateTo${id}w`).value).toLocaleString('en-gb', dateOptions);
|
||||
document.querySelector(`#timelineItem${id}w`).classList.toggle("editing");
|
||||
document.querySelector(`#companyName${id}w`).setAttribute("disabled", "");
|
||||
document.querySelector(`#area${id}w`).setAttribute("disabled", "");
|
||||
document.querySelector(`#jobTitle${id}w`).setAttribute("disabled", "");
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status === 401)
|
||||
{
|
||||
window.location.href = "./";
|
||||
return;
|
||||
}
|
||||
|
||||
res.json().then(json =>
|
||||
{
|
||||
document.querySelector(`#workError${id}w`).classList.remove("hidden");
|
||||
document.querySelector(`#workError${id}w div`).innerHTML = json.error;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the timeline item with the given id
|
||||
* @param id the id of the timeline item
|
||||
*/
|
||||
function deleteEduItem(id)
|
||||
{
|
||||
fetch("/api/timelineData/edu/" + id, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
}
|
||||
}).then(res =>
|
||||
{
|
||||
if (res.ok)
|
||||
{
|
||||
document.querySelector(`#timelineItem${id}e`).remove();
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status === 401)
|
||||
{
|
||||
window.location.href = "./";
|
||||
return;
|
||||
}
|
||||
res.json().then(json => alert(json.error));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the timeline item with the given id
|
||||
* @param id the id of the timeline item
|
||||
*/
|
||||
function deleteWorkItem(id)
|
||||
{
|
||||
fetch("/api/timelineData/work/" + id, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
}
|
||||
}).then(res =>
|
||||
{
|
||||
if (res.ok)
|
||||
{
|
||||
document.querySelector(`#timelineItem${id}w`).remove();
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status === 401)
|
||||
{
|
||||
window.location.href = "./";
|
||||
return;
|
||||
}
|
||||
res.json().then(json => alert(json.error));
|
||||
|
||||
});
|
||||
}
|
||||
|
@ -39,10 +39,11 @@ document.querySelector("#login form").addEventListener("submit", e =>
|
||||
{
|
||||
method: "POST",
|
||||
body: loginData
|
||||
}).then(res =>
|
||||
}).then(res => res.json().then(json =>
|
||||
{
|
||||
if (res.ok)
|
||||
{
|
||||
localStorage.setItem("token", json.token);
|
||||
window.location.href = "./editor.html";
|
||||
return;
|
||||
}
|
||||
@ -52,7 +53,7 @@ document.querySelector("#login form").addEventListener("submit", e =>
|
||||
return;
|
||||
}
|
||||
showErrorMessage("Invalid username or password.", "login");
|
||||
});
|
||||
}));
|
||||
return;
|
||||
}
|
||||
showErrorMessage("Please type in a username and password.", "login");
|
||||
|
@ -107,6 +107,7 @@ function StartTextAnimation(i)
|
||||
*/
|
||||
function getTimelineData()
|
||||
{
|
||||
let dateOptions = { year: "numeric", month: "short" };
|
||||
fetch("/api/timelineData/edu").then(res =>
|
||||
{
|
||||
res.json().then(json =>
|
||||
@ -118,7 +119,7 @@ function getTimelineData()
|
||||
let timelineItem = document.createElement("div")
|
||||
timelineItem.classList.add("timelineItem");
|
||||
timelineItem.innerHTML = `
|
||||
<h3 class="timelineHeader">${item["startPeriod"]} - ${item["endPeriod"]}</h3>
|
||||
<h3 class="timelineHeader">${new Date(item["startPeriod"]).toLocaleString('en-gb', dateOptions)} - ${new Date(item["endPeriod"]).toLocaleString('en-gb', dateOptions)}</h3>
|
||||
<span>Grade: ${item["grade"]}</span>
|
||||
<p class="timelineText">${item["course"]}</p>
|
||||
`;
|
||||
@ -138,8 +139,9 @@ function getTimelineData()
|
||||
{
|
||||
let timelineItem = document.createElement("div")
|
||||
timelineItem.classList.add("timelineItem");
|
||||
let endPeriod = item["endPeriod"] === null ? "Present" : new Date(item["endPeriod"]).toLocaleString('en-gb', dateOptions);
|
||||
timelineItem.innerHTML = `
|
||||
<h3 class="timelineHeader">${item["startPeriod"]} - ${item["endPeriod"]}</h3>
|
||||
<h3 class="timelineHeader">${new Date(item["startPeriod"]).toLocaleString('en-gb', dateOptions)} - ${endPeriod}</h3>
|
||||
<span>${item["companyName"]} - ${item["area"]}</span>
|
||||
<p class="timelineText">${item["title"]}</p>
|
||||
`;
|
||||
|
Loading…
Reference in New Issue
Block a user