Completed first revision of front end. Enabled CORS in spring. Allowed path variable to be optional.
This commit is contained in:
parent
c160703048
commit
e5a84268cc
11 changed files with 456 additions and 328 deletions
3
backend/.gitignore
vendored
3
backend/.gitignore
vendored
|
@ -19,3 +19,6 @@ gradlew.bat
|
||||||
|
|
||||||
# Local property file
|
# Local property file
|
||||||
src/main/resources/application-*.properties
|
src/main/resources/application-*.properties
|
||||||
|
|
||||||
|
# Server Keystore
|
||||||
|
src/main/resources/achievements-ssl-key.p12
|
||||||
|
|
|
@ -3,6 +3,9 @@ package achievements;
|
||||||
import achievements.services.DbConnectionService;
|
import achievements.services.DbConnectionService;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class Application {
|
public class Application {
|
||||||
|
@ -16,4 +19,16 @@ public class Application {
|
||||||
SpringApplication.exit(context, () -> 0);
|
SpringApplication.exit(context, () -> 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public WebMvcConfigurer configurer() {
|
||||||
|
return new WebMvcConfigurer() {
|
||||||
|
@Override
|
||||||
|
public void addCorsMappings(CorsRegistry registry) {
|
||||||
|
registry
|
||||||
|
.addMapping("/*")
|
||||||
|
.allowedOrigins("*");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -21,8 +21,8 @@ public class Controller {
|
||||||
|
|
||||||
public Controller() {}
|
public Controller() {}
|
||||||
|
|
||||||
@RequestMapping(value = "/achievements/{Name}", method = GET, produces = "application/json")
|
@RequestMapping(value = { "/achievements", "/achievements/{Name}" }, method = GET, produces = "application/json")
|
||||||
public ResponseEntity<String> fetchAchievements(@PathVariable("Name") String getName) {
|
public ResponseEntity<String> fetchAchievements(@PathVariable(value = "Name", required = false) String getName) {
|
||||||
var achievements = (Achievements) null;
|
var achievements = (Achievements) null;
|
||||||
if (getName == null) {
|
if (getName == null) {
|
||||||
achievements = db.getAchievements("%");
|
achievements = db.getAchievements("%");
|
||||||
|
@ -42,8 +42,8 @@ public class Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "/games/{Name}", method = GET, produces = "text/html")
|
@RequestMapping(value = { "/games", "/games/{Name}" }, method = GET, produces = "application/json")
|
||||||
public ResponseEntity<String> fetchGames(@PathVariable("Name") String getName) {
|
public ResponseEntity<String> fetchGames(@PathVariable(value = "Name", required = false) String getName) {
|
||||||
var games = (Games) null;
|
var games = (Games) null;
|
||||||
if (getName == null) {
|
if (getName == null) {
|
||||||
games = db.getGames("%");
|
games = db.getGames("%");
|
||||||
|
|
|
@ -1,58 +1,16 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const morgan = require('morgan' );
|
const morgan = require('morgan' );
|
||||||
const fs = require('fs' );
|
const fs = require('fs' );
|
||||||
const http = require('http' );
|
const https = require('https' );
|
||||||
|
|
||||||
const config = require('./config.js').load(process.argv[2]);
|
const config = require('./config.js').load(process.argv[2]);
|
||||||
|
|
||||||
|
if (config.build === 'debug') {
|
||||||
|
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use("/", morgan("dev"));
|
app.use("/", morgan("dev"));
|
||||||
app.use("/", (request, response, next) => {
|
|
||||||
/*if (request.headers['accept'].split(',')[0] === 'text/html') {
|
|
||||||
let path = request.url;
|
|
||||||
if (path === '/') {
|
|
||||||
path = "/index.html";
|
|
||||||
}
|
|
||||||
path = 'webpage' + path;
|
|
||||||
fs.readFile(path, 'utf8', (err, fileData) => {
|
|
||||||
if (err) {
|
|
||||||
response.statusCode = 500;
|
|
||||||
response.statusMessage = 'Unkown file read error';
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path === 'webpage/index.html') {
|
|
||||||
console.log("ummmm");
|
|
||||||
dbRequest = http.get("http://localhost:4730/achievements", dbResponse => {
|
|
||||||
console.log("Yesting?");
|
|
||||||
dbResponse.on('data', achievementData => {
|
|
||||||
console.log("Hello?");
|
|
||||||
achievementData = JSON.parse(achievementData);
|
|
||||||
let list = "";
|
|
||||||
for (const achievement of achievementData.achievements) {
|
|
||||||
list = list +
|
|
||||||
`<div class="list-page-list-entry">
|
|
||||||
<img class="achievement-list-page-entry-icon" src="res/dummy_achievement.png" alt="Achievement Icon.png" />
|
|
||||||
<p class="achievement-list-page-entry-name">${achievement.name}</p>
|
|
||||||
<p class="achievement-list-page-entry-description">${achievement.description}</p>
|
|
||||||
<p class="achievement-list-page-entry-game">${achievement.gameID}</p>
|
|
||||||
</div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
response.statusCode = 200;
|
|
||||||
response.statusMessage = 'OK';
|
|
||||||
response.write(Buffer.from(fileDate.replace("\${list}", list), 'utf8'));
|
|
||||||
response.end();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}*/
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
app.use("/", express.static("webpage"));
|
app.use("/", express.static("webpage"));
|
||||||
|
|
||||||
app.listen(config.port);
|
app.listen(config.port);
|
|
@ -1,272 +1,35 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Achievements Project</title>
|
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
<title>Achievements Project</title>
|
||||||
|
|
||||||
<!-- Load Stylesheets -->
|
|
||||||
<link rel="stylesheet" href="styles/index.css" />
|
<link rel="stylesheet" href="styles/index.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="navbar">
|
<div id="navbar">
|
||||||
<div id="navbar-left">
|
<template data-template="navbar: list">
|
||||||
<div id="navbar-item-games" class="navbar-item" data-page-name="games">
|
<div id="navbar-section-${navbar[section]}" class="navbar-section">
|
||||||
Games
|
<template data-template="navbar-section-${navbar[section]}: list">
|
||||||
</diV>
|
<div id="navbar-item-${navbar-section-${navbar[section]}[item]}" class="navbar-item" data-page-name="${navbar-section-${navbar[section]}[item]}">
|
||||||
<div id="navbar-item-achievement" class="navbar-item" data-page-name="achievements">
|
${navbar-section-${navbar[section]}[title]}
|
||||||
Achievements
|
</div>
|
||||||
</diV>
|
</template>
|
||||||
</div>
|
|
||||||
<div id="navbar-right">
|
|
||||||
<div id="navbar-item-profile" class="navbar-item" data-page-name="profile">
|
|
||||||
Profile
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div id="content-body">
|
<div id="content-body">
|
||||||
<div id="games-page" class="page">
|
<template data-template="content-body: list">
|
||||||
<div class="page-header">
|
<div id="${content-body[page]}-page" class="page">
|
||||||
<p class="page-header-text">Games</p>
|
<div class="page-header">
|
||||||
<div class="page-header-separator"></div>
|
<p class="page-header-text">${content-body[title]}</p>
|
||||||
<div class="list-page-search">
|
<div class="page-header-separator"></div>
|
||||||
<label for="game-search">Search</label>
|
|
||||||
<input id="game-search" type="text" placeholder="Name, Keyword, etc..." name="game-search" />
|
|
||||||
</div>
|
|
||||||
<div class="list-page-partitions">
|
|
||||||
<div class="list-page-filter-partition">
|
|
||||||
<p class="page-subheader-text">Filters</p>
|
|
||||||
<div class="page-subheader-separator"></div>
|
|
||||||
<div id="games-owned-filter" class="list-page-filter">
|
|
||||||
<div class="list-page-filter-checkbox"></div>
|
|
||||||
<p class="list-page-filter-name">Games Owned</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="list-page-list-partition">
|
|
||||||
<div class="list-page-list">
|
|
||||||
<div class="list-page-list-header">
|
|
||||||
<p class="game-list-page-entry-icon"></p>
|
|
||||||
<p class="game-list-page-entry-name">Name</p>
|
|
||||||
<p class="game-list-page-entry-description">Description</p>
|
|
||||||
</div>
|
|
||||||
<div class="list-page-list-entry">
|
|
||||||
<img class="game-list-page-entry-icon" src="res/dummy_game.png" alt="Achievement Icon.png" />
|
|
||||||
<p class="game-list-page-entry-name">Latin</p>
|
|
||||||
<p class="game-list-page-entry-description">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
|
||||||
</div>
|
|
||||||
<div class="list-page-list-entry">
|
|
||||||
<img class="game-list-page-entry-icon" src="res/dummy_game.png" alt="Achievement Icon.png" />
|
|
||||||
<p class="game-list-page-entry-name">Latin</p>
|
|
||||||
<p class="game-list-page-entry-description">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
|
||||||
</div>
|
|
||||||
<div class="list-page-list-entry">
|
|
||||||
<img class="game-list-page-entry-icon" src="res/dummy_game.png" alt="Achievement Icon.png" />
|
|
||||||
<p class="game-list-page-entry-name">Latin</p>
|
|
||||||
<p class="game-list-page-entry-description">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
|
||||||
</div>
|
|
||||||
<div class="list-page-list-entry">
|
|
||||||
<img class="game-list-page-entry-icon" src="res/dummy_game.png" alt="Achievement Icon.png" />
|
|
||||||
<p class="game-list-page-entry-name">Latin</p>
|
|
||||||
<p class="game-list-page-entry-description">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
|
||||||
</div>
|
|
||||||
<div class="list-page-list-entry">
|
|
||||||
<img class="game-list-page-entry-icon" src="res/dummy_game.png" alt="Achievement Icon.png" />
|
|
||||||
<p class="game-list-page-entry-name">Latin</p>
|
|
||||||
<p class="game-list-page-entry-description">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
|
||||||
</div>
|
|
||||||
<div class="list-page-list-entry">
|
|
||||||
<img class="game-list-page-entry-icon" src="res/dummy_game.png" alt="Achievement Icon.png" />
|
|
||||||
<p class="game-list-page-entry-name">Latin</p>
|
|
||||||
<p class="game-list-page-entry-description">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<template data-template="fetch-${content-body[page]}-page: fetch"></template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
<div id="achievements-page" class="page">
|
|
||||||
<div class="page-header">
|
|
||||||
<p class="page-header-text">Achievements</p>
|
|
||||||
<div class="page-header-separator"></div>
|
|
||||||
<div class="list-page-search">
|
|
||||||
<label for="achievement-search">Search</label>
|
|
||||||
<input id="achievement-search" type="text" placeholder="Name, Keyword, etc..." name="achievement-search" />
|
|
||||||
</div>
|
|
||||||
<div class="list-page-partitions">
|
|
||||||
<div class="list-page-filter-partition">
|
|
||||||
<p class="page-subheader-text">Filters</p>
|
|
||||||
<div class="page-subheader-separator"></div>
|
|
||||||
<div id="games-owned-filter" class="list-page-filter">
|
|
||||||
<div class="list-page-filter-checkbox"></div>
|
|
||||||
<p class="list-page-filter-name">From Games Owned</p>
|
|
||||||
</div>
|
|
||||||
<div id="games-owned-filter" class="list-page-filter">
|
|
||||||
<div class="list-page-filter-checkbox"></div>
|
|
||||||
<p class="list-page-filter-name">In Progress</p>
|
|
||||||
</div>
|
|
||||||
<div id="games-owned-filter" class="list-page-filter">
|
|
||||||
<div class="list-page-filter-checkbox"></div>
|
|
||||||
<p class="list-page-filter-name">Completed</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="list-page-list-partition">
|
|
||||||
<div class="list-page-list">
|
|
||||||
<div class="list-page-list-header">
|
|
||||||
<p class="achievement-list-page-entry-icon"></p>
|
|
||||||
<p class="achievement-list-page-entry-name">Name</p>
|
|
||||||
<p class="achievement-list-page-entry-description">Description</p>
|
|
||||||
<p class="achievement-list-page-entry-game">Game</p>
|
|
||||||
</div>
|
|
||||||
${list}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="profile-page" class="page">
|
|
||||||
<div class="page-header">
|
|
||||||
<p class="page-header-text">Profile</p>
|
|
||||||
<div class="page-header-separator"></div>
|
|
||||||
</div>
|
|
||||||
<div id="profile-section-1">
|
|
||||||
<div id="profile-info">
|
|
||||||
<img id="profile-info-pfp" src="res/temp_pfp.png" alt="User's Profile Pictuer" />
|
|
||||||
<p id="profile-info-name">Jane Doe</p>
|
|
||||||
</div>
|
|
||||||
<div id="profile-platforms">
|
|
||||||
<p class="page-subheader-text">Platforms</p>
|
|
||||||
<div class="page-subheader-separator"></div>
|
|
||||||
<div class="profile-platform-entry connected">
|
|
||||||
<div class="profile-platform-entry-left">
|
|
||||||
<img class="profile-platform-icon" src="res/steam.png" alt="Steam Logo" />
|
|
||||||
<p class="profile-platform-name">Steam</p>
|
|
||||||
</div>
|
|
||||||
<div class="profile-platform-entry-right">
|
|
||||||
<p class="profile-platform-connected">Connected</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="profile-platform-entry">
|
|
||||||
<div class="profile-platform-entry-left">
|
|
||||||
<img class="profile-platform-icon" src="res/xbox.png" alt="Xbox Logo" />
|
|
||||||
<p class="profile-platform-name">Xbox Live</p>
|
|
||||||
</div>
|
|
||||||
<div class="profile-platform-entry-right">
|
|
||||||
<p class="profile-platform-connected">Connected</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="profile-platform-entry">
|
|
||||||
<div class="profile-platform-entry-left">
|
|
||||||
<img class="profile-platform-icon" src="res/psn.png" alt="PSN Logo" />
|
|
||||||
<p class="profile-platform-name">PSN</p>
|
|
||||||
</div>
|
|
||||||
<div class="profile-platform-entry-right">
|
|
||||||
<p class="profile-platform-connected">Connected</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="profile-section-2">
|
|
||||||
<div id="profile-games">
|
|
||||||
<p class="page-subheader-text">Games</p>
|
|
||||||
<div class="page-subheader-separator"></div>
|
|
||||||
<div class="profile-game-entry">
|
|
||||||
<img class="profile-game-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
|
||||||
<p class="profile-game-entry-name">Latin</p>
|
|
||||||
</div>
|
|
||||||
<div class="profile-game-entry">
|
|
||||||
<img class="profile-game-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
|
||||||
<p class="profile-game-entry-name">Latin</p>
|
|
||||||
</div>
|
|
||||||
<div class="profile-game-entry">
|
|
||||||
<img class="profile-game-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
|
||||||
<p class="profile-game-entry-name">Latin</p>
|
|
||||||
</div>
|
|
||||||
<div class="profile-game-entry">
|
|
||||||
<img class="profile-game-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
|
||||||
<p class="profile-game-entry-name">Latin</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="profile-achievements">
|
|
||||||
<p class="page-subheader-text">Achievements</p>
|
|
||||||
<div class="page-subheader-separator"></div>
|
|
||||||
<div class="profile-achievement-entry completed">
|
|
||||||
<div class="profile-achievement-entry-left">
|
|
||||||
<img class="profile-achievement-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
|
||||||
<p class="profile-achievement-entry-name">Lorem Ipsum</p>
|
|
||||||
</div>
|
|
||||||
<div class="profile-achievement-entry-right">
|
|
||||||
<p class="profile-achievement-completed">Completed</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="profile-achievement-entry completed">
|
|
||||||
<div class="profile-achievement-entry-left">
|
|
||||||
<img class="profile-achievement-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
|
||||||
<p class="profile-achievement-entry-name">Lorem Ipsum</p>
|
|
||||||
</div>
|
|
||||||
<div class="profile-achievement-entry-right">
|
|
||||||
<p class="profile-achievement-completed">Completed</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="profile-achievement-entry completed">
|
|
||||||
<div class="profile-achievement-entry-left">
|
|
||||||
<img class="profile-achievement-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
|
||||||
<p class="profile-achievement-entry-name">Lorem Ipsum</p>
|
|
||||||
</div>
|
|
||||||
<div class="profile-achievement-entry-right">
|
|
||||||
<p class="profile-achievement-completed">Completed</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="profile-achievement-entry">
|
|
||||||
<div class="profile-achievement-entry-left">
|
|
||||||
<img class="profile-achievement-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
|
||||||
<p class="profile-achievement-entry-name">Lorem Ipsum</p>
|
|
||||||
</div>
|
|
||||||
<div class="profile-achievement-entry-right">
|
|
||||||
<p class="profile-achievement-completed">Completed</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="profile-achievement-entry">
|
|
||||||
<div class="profile-achievement-entry-left">
|
|
||||||
<img class="profile-achievement-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
|
||||||
<p class="profile-achievement-entry-name">Lorem Ipsum</p>
|
|
||||||
</div>
|
|
||||||
<div class="profile-achievement-entry-right">
|
|
||||||
<p class="profile-achievement-completed">Completed</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="profile-achievement-entry">
|
|
||||||
<div class="profile-achievement-entry-left">
|
|
||||||
<img class="profile-achievement-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
|
||||||
<p class="profile-achievement-entry-name">Lorem Ipsum</p>
|
|
||||||
</div>
|
|
||||||
<div class="profile-achievement-entry-right">
|
|
||||||
<p class="profile-achievement-completed">Completed</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="profile-achievement-entry">
|
|
||||||
<div class="profile-achievement-entry-left">
|
|
||||||
<img class="profile-achievement-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
|
||||||
<p class="profile-achievement-entry-name">Lorem Ipsum</p>
|
|
||||||
</div>
|
|
||||||
<div class="profile-achievement-entry-right">
|
|
||||||
<p class="profile-achievement-completed">Completed</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="profile-achievement-entry">
|
|
||||||
<div class="profile-achievement-entry-left">
|
|
||||||
<img class="profile-achievement-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
|
||||||
<p class="profile-achievement-entry-name">Lorem Ipsum</p>
|
|
||||||
</div>
|
|
||||||
<div class="profile-achievement-entry-right">
|
|
||||||
<p class="profile-achievement-completed">Completed</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<script src="scripts/template.js"></script>
|
||||||
<!-- Load javascript -->
|
|
||||||
<script src="scripts/index.js"></script>
|
<script src="scripts/index.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,15 +1,40 @@
|
||||||
|
const expandTemplates = async () => {
|
||||||
|
template.register("navbar", [
|
||||||
|
{ section: "left" },
|
||||||
|
{ section: "right" }
|
||||||
|
]);
|
||||||
|
template.register("navbar-section-left", [
|
||||||
|
{ item: "games", title: "Games" },
|
||||||
|
{ item: "achievements", title: "Achievements" }
|
||||||
|
]);
|
||||||
|
template.register("navbar-section-right", [
|
||||||
|
{ item: "profile", title: "Profile" }
|
||||||
|
]);
|
||||||
|
template.register("content-body", [
|
||||||
|
{ page: "games", title: "Games" },
|
||||||
|
{ page: "achievements", title: "Achievements" },
|
||||||
|
{ page: "profile", title: "Profile" }
|
||||||
|
]);
|
||||||
|
template.register("fetch-games-page", "games_page" );
|
||||||
|
template.register("fetch-achievements-page", "achievements_page");
|
||||||
|
template.register("fetch-profile-page", "profile_page" );
|
||||||
|
template.registerFetch("achievements-page-list", "Achievements", "https://localhost:4730/achievements", { method: 'GET', mode: 'cors' });
|
||||||
|
|
||||||
|
await template.expand();
|
||||||
|
};
|
||||||
|
|
||||||
let pages = null;
|
let pages = null;
|
||||||
const loadPages = () => {
|
const loadPages = () => {
|
||||||
pages = document.querySelectorAll(".page");
|
pages = document.querySelectorAll(".page");
|
||||||
}
|
}
|
||||||
|
|
||||||
const connectNavbar = () => {
|
const connectNavbar = () => {
|
||||||
const navItems = document.querySelectorAll(".navbar-item");
|
const navItems = document.querySelectorAll(".navbar-item");
|
||||||
|
|
||||||
for (let item of navItems) {
|
for (const item of navItems) {
|
||||||
item.addEventListener("click", (clickEvent) => {
|
item.addEventListener("click", (clickEvent) => {
|
||||||
const navItemPageId = item.dataset.pageName + "-page"
|
const navItemPageId = item.dataset.pageName + "-page"
|
||||||
for (page of pages) {
|
for (const page of pages) {
|
||||||
if (page.id === navItemPageId) {
|
if (page.id === navItemPageId) {
|
||||||
page.style.display = "block";
|
page.style.display = "block";
|
||||||
} else {
|
} else {
|
||||||
|
@ -25,7 +50,7 @@ const connectProfile = () => {
|
||||||
const achievements = document.querySelector("#profile-achievements");
|
const achievements = document.querySelector("#profile-achievements");
|
||||||
|
|
||||||
games.children[0].addEventListener("click", (clickEvent) => {
|
games.children[0].addEventListener("click", (clickEvent) => {
|
||||||
for (page of pages) {
|
for (const page of pages) {
|
||||||
if (page.id === "games-page") {
|
if (page.id === "games-page") {
|
||||||
page.style.display = "block";
|
page.style.display = "block";
|
||||||
} else {
|
} else {
|
||||||
|
@ -34,7 +59,6 @@ const connectProfile = () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(achievements.firstElement);
|
|
||||||
achievements.children[0].addEventListener("click", (clickEvent) => {
|
achievements.children[0].addEventListener("click", (clickEvent) => {
|
||||||
for (page of pages) {
|
for (page of pages) {
|
||||||
if (page.id === "achievements-page") {
|
if (page.id === "achievements-page") {
|
||||||
|
@ -59,7 +83,9 @@ const loadFilters = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("load", (loadEvent) => {
|
window.addEventListener("load", async (loadEvent) => {
|
||||||
|
await expandTemplates();
|
||||||
|
|
||||||
loadPages();
|
loadPages();
|
||||||
|
|
||||||
connectNavbar();
|
connectNavbar();
|
||||||
|
|
145
frontend/webpage/scripts/template.js
Normal file
145
frontend/webpage/scripts/template.js
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
var template = template || {};
|
||||||
|
|
||||||
|
template.type = {};
|
||||||
|
template.type._entryMap = new Map();
|
||||||
|
template.type.register = (type, callback) => {
|
||||||
|
const asyncCallback = async function() {
|
||||||
|
await callback.apply(null, Array.from(arguments));
|
||||||
|
}
|
||||||
|
if (typeof type !== 'string') {
|
||||||
|
console.error(`'type' must be a string, recieved: `, type);
|
||||||
|
} else {
|
||||||
|
if (type === "") {
|
||||||
|
console.error(`'type' may not be empty`);
|
||||||
|
} else if (template.type._entryMap.get(type) === undefined) {
|
||||||
|
template.type._entryMap.set(type, asyncCallback);
|
||||||
|
} else {
|
||||||
|
console.error(`${type} is already a registered template!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Intrinsic Templates */
|
||||||
|
|
||||||
|
// Basic - Simple search and replace
|
||||||
|
template.type.register('basic', (template, map) => {
|
||||||
|
let html = template.element.innerHTML;
|
||||||
|
for (const key in map) {
|
||||||
|
html = html.replace(new RegExp(`(?:(?<!(?<!\\\\)\\\\)\\\${${key}})`, 'gm'), map[key]);
|
||||||
|
}
|
||||||
|
template.element.outerHTML = html.trim();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fetch - Retrieve template from webserver
|
||||||
|
template.type.register('fetch', (template, name) => {
|
||||||
|
return fetch(`templates/${name}.html.template`, {
|
||||||
|
method: 'GET',
|
||||||
|
mode: 'no-cors',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'text/plain'
|
||||||
|
}
|
||||||
|
}).then(response => response.text().then((data) => {
|
||||||
|
template.element.outerHTML = data;
|
||||||
|
})).catch(error => {
|
||||||
|
console.error(`failed to retrieve template '${name}': `, error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// List - Iterate over list and emit copy of child for each iteration
|
||||||
|
template.type.register('list', (template, arrayMap) => {
|
||||||
|
let cumulative = "";
|
||||||
|
for (const map of arrayMap) {
|
||||||
|
let html = template.element.innerHTML;
|
||||||
|
for (const key in map) {
|
||||||
|
html = html.replace(new RegExp(`(?:(?<!(?<!\\\\)\\\\)\\\${${template.id}\\[${key}\\]})`, 'gm'), map[key]);
|
||||||
|
}
|
||||||
|
cumulative = cumulative + html.trim();
|
||||||
|
}
|
||||||
|
template.element.outerHTML = cumulative;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
template._entryMap = new Map();
|
||||||
|
template.register = function(pattern/*, arguments */) {
|
||||||
|
if (typeof pattern !== 'string') {
|
||||||
|
console.error('pattern must be a string, received: ', pattern);
|
||||||
|
} else {
|
||||||
|
const arrayArguments = Array.from(arguments);
|
||||||
|
arrayArguments.splice(0, 1);
|
||||||
|
template._entryMap.set(RegExp("^" + pattern + "$"), async () => arrayArguments);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template.registerFetch = function(pattern, path, url, fetchOptions) {
|
||||||
|
if (typeof pattern !== 'string') {
|
||||||
|
console.error('pattern must be a string, received: ', pattern);
|
||||||
|
} else {
|
||||||
|
let args = null;
|
||||||
|
template._entryMap.set(RegExp("^" + pattern + "$"), async () => {
|
||||||
|
if (args !== null) {
|
||||||
|
return Promise.resolve(args);
|
||||||
|
} else {
|
||||||
|
return fetch(url, fetchOptions)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
args = data;
|
||||||
|
path = path.split('\\.');
|
||||||
|
for (const id of path) {
|
||||||
|
args = args[id];
|
||||||
|
}
|
||||||
|
return args = [ args ];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
const findTemplates = (element) =>
|
||||||
|
Array
|
||||||
|
.from(element.querySelectorAll("template"))
|
||||||
|
.filter(child => Boolean(child.dataset.template))
|
||||||
|
.map(child => {
|
||||||
|
const data = child.dataset.template.split(/\s*:\s*/);
|
||||||
|
return {
|
||||||
|
id: data[0],
|
||||||
|
type: data.length > 1 ? data[1] : 'basic',
|
||||||
|
element: child
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const expand = async (element) => {
|
||||||
|
let children = findTemplates(element);
|
||||||
|
let promises = [];
|
||||||
|
let parents = new Set();
|
||||||
|
for (const child of children) {
|
||||||
|
for (const [pattern, argCallbacks] of template._entryMap) {
|
||||||
|
await argCallbacks().then(args => {
|
||||||
|
if (pattern.test(child.id)) {
|
||||||
|
const callback = template.type._entryMap.get(child.type);
|
||||||
|
if (typeof callback !== 'function') {
|
||||||
|
console.error(`'${child.type}' is not a registered template type`);
|
||||||
|
} else {
|
||||||
|
let params = Array.from(args)
|
||||||
|
params.splice(0, 0, child);
|
||||||
|
let parent = child.element.parentElement;
|
||||||
|
if (!parents.has(parent)) {
|
||||||
|
parents.add(parent);
|
||||||
|
}
|
||||||
|
promises.push(callback.apply(null, params));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('failed to retrieve arguments: ', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Promise.all(promises);
|
||||||
|
promises = [];
|
||||||
|
for (const parent of parents) {
|
||||||
|
promises.push(expand(parent));
|
||||||
|
}
|
||||||
|
await Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
template.expand = async () => expand(document.children[0]);
|
||||||
|
})();
|
|
@ -52,17 +52,7 @@ html, body {
|
||||||
box-shadow: 0px 0px 5px 10px rgba(0, 0, 0, 0.5);
|
box-shadow: 0px 0px 5px 10px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
#navbar-left {
|
.navbar-section {
|
||||||
width: max-content;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#navbar-right {
|
|
||||||
width: max-content;
|
width: max-content;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
|
@ -363,7 +353,7 @@ html, body {
|
||||||
}
|
}
|
||||||
|
|
||||||
#games-page {
|
#games-page {
|
||||||
max-width: 2560px;
|
max-width: 1920px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.game-list-page-entry-icon {
|
.game-list-page-entry-icon {
|
||||||
|
@ -404,7 +394,7 @@ html, body {
|
||||||
}
|
}
|
||||||
|
|
||||||
#achievements-page {
|
#achievements-page {
|
||||||
max-width: 2560px;
|
max-width: 1920px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.achievement-list-page-entry-icon {
|
.achievement-list-page-entry-icon {
|
||||||
|
@ -425,7 +415,7 @@ html, body {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
flex-grow: 1;
|
flex-grow: 4;
|
||||||
flex-basis: 0px;
|
flex-basis: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -440,11 +430,11 @@ html, body {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
flex-grow: 2;
|
flex-grow: 8;
|
||||||
flex-basis: 0px;
|
flex-basis: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.achievement-list-page-entry-game {
|
.achievement-list-page-entry-stages {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
40
frontend/webpage/templates/achievements_page.html.template
Normal file
40
frontend/webpage/templates/achievements_page.html.template
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<div class="list-page-search">
|
||||||
|
<label for="achievement-search">Search</label>
|
||||||
|
<input id="achievement-search" type="text" placeholder="Name, Keyword, etc..." name="achievement-search" />
|
||||||
|
</div>
|
||||||
|
<div class="list-page-partitions">
|
||||||
|
<div class="list-page-filter-partition">
|
||||||
|
<p class="page-subheader-text">Filters</p>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
<div id="games-owned-filter" class="list-page-filter">
|
||||||
|
<div class="list-page-filter-checkbox"></div>
|
||||||
|
<p class="list-page-filter-name">From Games Owned</p>
|
||||||
|
</div>
|
||||||
|
<div id="games-owned-filter" class="list-page-filter">
|
||||||
|
<div class="list-page-filter-checkbox"></div>
|
||||||
|
<p class="list-page-filter-name">In Progress</p>
|
||||||
|
</div>
|
||||||
|
<div id="games-owned-filter" class="list-page-filter">
|
||||||
|
<div class="list-page-filter-checkbox"></div>
|
||||||
|
<p class="list-page-filter-name">Completed</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-list-partition">
|
||||||
|
<div class="list-page-list">
|
||||||
|
<div class="list-page-list-header">
|
||||||
|
<p class="achievement-list-page-entry-icon"></p>
|
||||||
|
<p class="achievement-list-page-entry-name">Name</p>
|
||||||
|
<p class="achievement-list-page-entry-description">Description</p>
|
||||||
|
<p class="achievement-list-page-entry-stages">Stages</p>
|
||||||
|
</div>
|
||||||
|
<template data-template="achievements-page-list: list">
|
||||||
|
<div class="list-page-list-entry">
|
||||||
|
<img class="achievement-list-page-entry-icon" src="res/dummy_achievement.png" alt="Achievement Thumbnail"></img>
|
||||||
|
<p class="achievement-list-page-entry-name">${achievements-page-list[Name]}</p>
|
||||||
|
<p class="achievement-list-page-entry-description">${achievements-page-list[Description]}</p>
|
||||||
|
<p class="achievement-list-page-entry-stages">${achievements-page-list[Stages]}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
53
frontend/webpage/templates/games_page.html.template
Normal file
53
frontend/webpage/templates/games_page.html.template
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<div class="list-page-search">
|
||||||
|
<label for="game-search">Search</label>
|
||||||
|
<input id="game-search" type="text" placeholder="Name, Keyword, etc..." name="game-search" />
|
||||||
|
</div>
|
||||||
|
<div class="list-page-partitions">
|
||||||
|
<div class="list-page-filter-partition">
|
||||||
|
<p class="page-subheader-text">Filters</p>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
<div id="games-owned-filter" class="list-page-filter">
|
||||||
|
<div class="list-page-filter-checkbox"></div>
|
||||||
|
<p class="list-page-filter-name">Games Owned</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-list-partition">
|
||||||
|
<div class="list-page-list">
|
||||||
|
<div class="list-page-list-header">
|
||||||
|
<p class="game-list-page-entry-icon"></p>
|
||||||
|
<p class="game-list-page-entry-name">Name</p>
|
||||||
|
<p class="game-list-page-entry-description">Description</p>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-list-entry">
|
||||||
|
<img class="game-list-page-entry-icon" src="res/dummy_game.png" alt="Achievement Icon.png" />
|
||||||
|
<p class="game-list-page-entry-name">Latin</p>
|
||||||
|
<p class="game-list-page-entry-description">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-list-entry">
|
||||||
|
<img class="game-list-page-entry-icon" src="res/dummy_game.png" alt="Achievement Icon.png" />
|
||||||
|
<p class="game-list-page-entry-name">Latin</p>
|
||||||
|
<p class="game-list-page-entry-description">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-list-entry">
|
||||||
|
<img class="game-list-page-entry-icon" src="res/dummy_game.png" alt="Achievement Icon.png" />
|
||||||
|
<p class="game-list-page-entry-name">Latin</p>
|
||||||
|
<p class="game-list-page-entry-description">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-list-entry">
|
||||||
|
<img class="game-list-page-entry-icon" src="res/dummy_game.png" alt="Achievement Icon.png" />
|
||||||
|
<p class="game-list-page-entry-name">Latin</p>
|
||||||
|
<p class="game-list-page-entry-description">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-list-entry">
|
||||||
|
<img class="game-list-page-entry-icon" src="res/dummy_game.png" alt="Achievement Icon.png" />
|
||||||
|
<p class="game-list-page-entry-name">Latin</p>
|
||||||
|
<p class="game-list-page-entry-description">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-list-entry">
|
||||||
|
<img class="game-list-page-entry-icon" src="res/dummy_game.png" alt="Achievement Icon.png" />
|
||||||
|
<p class="game-list-page-entry-name">Latin</p>
|
||||||
|
<p class="game-list-page-entry-description">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
135
frontend/webpage/templates/profile_page.html.template
Normal file
135
frontend/webpage/templates/profile_page.html.template
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
<div id="profile-section-1">
|
||||||
|
<div id="profile-info">
|
||||||
|
<img id="profile-info-pfp" src="res/temp_pfp.png" alt="User's Profile Pictuer" />
|
||||||
|
<p id="profile-info-name">Jane Doe</p>
|
||||||
|
</div>
|
||||||
|
<div id="profile-platforms">
|
||||||
|
<p class="page-subheader-text">Platforms</p>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
<div class="profile-platform-entry connected">
|
||||||
|
<div class="profile-platform-entry-left">
|
||||||
|
<img class="profile-platform-icon" src="res/steam.png" alt="Steam Logo" />
|
||||||
|
<p class="profile-platform-name">Steam</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-platform-entry-right">
|
||||||
|
<p class="profile-platform-connected">Connected</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="profile-platform-entry">
|
||||||
|
<div class="profile-platform-entry-left">
|
||||||
|
<img class="profile-platform-icon" src="res/xbox.png" alt="Xbox Logo" />
|
||||||
|
<p class="profile-platform-name">Xbox Live</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-platform-entry-right">
|
||||||
|
<p class="profile-platform-connected">Connected</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="profile-platform-entry">
|
||||||
|
<div class="profile-platform-entry-left">
|
||||||
|
<img class="profile-platform-icon" src="res/psn.png" alt="PSN Logo" />
|
||||||
|
<p class="profile-platform-name">PSN</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-platform-entry-right">
|
||||||
|
<p class="profile-platform-connected">Connected</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="profile-section-2">
|
||||||
|
<div id="profile-games">
|
||||||
|
<p class="page-subheader-text">Games</p>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
<div class="profile-game-entry">
|
||||||
|
<img class="profile-game-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
||||||
|
<p class="profile-game-entry-name">Latin</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-game-entry">
|
||||||
|
<img class="profile-game-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
||||||
|
<p class="profile-game-entry-name">Latin</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-game-entry">
|
||||||
|
<img class="profile-game-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
||||||
|
<p class="profile-game-entry-name">Latin</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-game-entry">
|
||||||
|
<img class="profile-game-entry-icon" src="res/dummy_game.png" alt="Some Game Icon" />
|
||||||
|
<p class="profile-game-entry-name">Latin</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="profile-achievements">
|
||||||
|
<p class="page-subheader-text">Achievements</p>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
<div class="profile-achievement-entry completed">
|
||||||
|
<div class="profile-achievement-entry-left">
|
||||||
|
<img class="profile-achievement-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
||||||
|
<p class="profile-achievement-entry-name">Lorem Ipsum</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-achievement-entry-right">
|
||||||
|
<p class="profile-achievement-completed">Completed</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="profile-achievement-entry completed">
|
||||||
|
<div class="profile-achievement-entry-left">
|
||||||
|
<img class="profile-achievement-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
||||||
|
<p class="profile-achievement-entry-name">Lorem Ipsum</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-achievement-entry-right">
|
||||||
|
<p class="profile-achievement-completed">Completed</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="profile-achievement-entry completed">
|
||||||
|
<div class="profile-achievement-entry-left">
|
||||||
|
<img class="profile-achievement-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
||||||
|
<p class="profile-achievement-entry-name">Lorem Ipsum</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-achievement-entry-right">
|
||||||
|
<p class="profile-achievement-completed">Completed</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="profile-achievement-entry">
|
||||||
|
<div class="profile-achievement-entry-left">
|
||||||
|
<img class="profile-achievement-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
||||||
|
<p class="profile-achievement-entry-name">Lorem Ipsum</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-achievement-entry-right">
|
||||||
|
<p class="profile-achievement-completed">Completed</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="profile-achievement-entry">
|
||||||
|
<div class="profile-achievement-entry-left">
|
||||||
|
<img class="profile-achievement-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
||||||
|
<p class="profile-achievement-entry-name">Lorem Ipsum</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-achievement-entry-right">
|
||||||
|
<p class="profile-achievement-completed">Completed</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="profile-achievement-entry">
|
||||||
|
<div class="profile-achievement-entry-left">
|
||||||
|
<img class="profile-achievement-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
||||||
|
<p class="profile-achievement-entry-name">Lorem Ipsum</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-achievement-entry-right">
|
||||||
|
<p class="profile-achievement-completed">Completed</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="profile-achievement-entry">
|
||||||
|
<div class="profile-achievement-entry-left">
|
||||||
|
<img class="profile-achievement-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
||||||
|
<p class="profile-achievement-entry-name">Lorem Ipsum</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-achievement-entry-right">
|
||||||
|
<p class="profile-achievement-completed">Completed</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="profile-achievement-entry">
|
||||||
|
<div class="profile-achievement-entry-left">
|
||||||
|
<img class="profile-achievement-entry-icon" src="res/dummy_achievement.png" alt="Some Achievement Icon" />
|
||||||
|
<p class="profile-achievement-entry-name">Lorem Ipsum</p>
|
||||||
|
</div>
|
||||||
|
<div class="profile-achievement-entry-right">
|
||||||
|
<p class="profile-achievement-completed">Completed</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
Loading…
Add table
Reference in a new issue