Initial commit

This commit is contained in:
Chord 2019-12-30 09:27:49 -05:00
commit 20946fdb43
49 changed files with 2393 additions and 0 deletions

View file

@ -0,0 +1,24 @@
<script>
import { isAdmin } from '../UserState'
export let account;
</script>
<style>
.banned-account {
color: red;
text-decoration: line-through;
}
</style>
<span class="account-link">
{#if $isAdmin}
<a class:banned-account={account.inactive} href="/user/{account.id}">{account.name}</a>
{:else}
<span class:banned-account={account.inactive}>{account.name}</span>
{/if}
{#if account.admin}
<span class="badge badge-success">GM</span>
{/if}
</span>

View file

@ -0,0 +1,22 @@
<script>
import { fade } from 'svelte/transition';
export function message(e, ehtml) {
errorMessage = e ? e : "";
errorMessageHTML = ehtml ? ehtml : "";
shake = true;
setTimeout(() => {shake = false;}, 800)
}
let errorMessage = ""
let errorMessageHTML = ""
let shake = false;
</script>
{#if errorMessage || errorMessageHTML}
<div in:fade class:notification-shake={shake} class="alert alert-danger" role="alert">
{errorMessage}
{@html errorMessageHTML}
</div>
{/if}

View file

@ -0,0 +1,20 @@
<script>
import { isAdmin } from '../UserState'
export let character;
</script>
<span class="character-link">
{#if character.faction_id == 1}
<img height=24 src="/img/nc_icon.png" alt="NC" />
{:else if character.faction_id == 0}
<img height=32 src="/img/tr_icon.png" alt="TR" />
{:else if character.faction_id == 2}
<img height=32 src="/img/vs_icon.png" alt="VS" />
{/if}
{#if $isAdmin}
<a href="/character/{character.id}">{character.name}</a>
{:else}
<span>{character.name}</span>
{/if}
</span>

View file

@ -0,0 +1,20 @@
<script>
import { onMount } from 'svelte'
import axios from 'axios'
import CharacterLink from '../components/CharacterLink'
import moment from 'moment'
onMount(async () => {
axios.get("/api/user/"+userId+"/characters
});
</script>
{#if characters}
{#each characters as char, i}
<ul>
<li><CharacterLink character={char} /></li>
</ul>
{/each}
{/if}

View file

@ -0,0 +1,89 @@
<script>
import { onMount } from 'svelte';
import { tweened } from 'svelte/motion';
import { cubicOut } from 'svelte/easing';
const progress = tweened(0, {
duration: 1000,
easing: cubicOut
});
export let stats = { "TR" : 0, "NC" : 0, "VS" : 0};
let total = stats.TR + stats.NC + stats.VS;
let percentages = { "TR" : stats.TR/total,
"NC" : stats.NC/total,
"VS" : stats.VS/total}
let tr, nc, vs;
onMount(() => {
setTimeout(() => progress.set(1.0), 100);
tr.style.height = "1px";
nc.style.height = "1px";
vs.style.height = "1px";
})
progress.subscribe((v) => {
if (tr === undefined || !tr.style)
return
tr.style.height = v*percentages.TR*200 + "px";
nc.style.height = v*percentages.NC*200 + "px";
vs.style.height = v*percentages.VS*200 + "px";
})
</script>
<style>
.empire-stats {
background: black;
height: 200px;
width: 220px;
border: 1px solid white;
display: inline-block;
position: relative;
}
.empire-stat {
border: 1px solid white;
border-bottom: 0;
position: absolute;
bottom: 0;
text-align: center;
min-width: 50px;
min-height: 3em;
}
.empire-stat:nth-child(2) {
left: 10%;
}
.empire-stat:nth-child(3) {
left: 40.0%;
}
.empire-stat:nth-child(4) {
left: 70.0%;
}
.empire-stats-header {
display: inline;
position: absolute;
width: 100%;
padding-top: 3px;
padding-bottom: 3px;
background: white;
border-bottom: 3px solid black;
font-size: 1.0em;
color: black;
text-align: center;
z-index: 0;
}
</style>
<div class="empire-stats clearfix">
<div class="empire-stats-header">Empire Need</div>
<div title={stats.TR} bind:this={tr} class="empire-stat faction-tr-bg"><strong>TR</strong><br/>{Math.round(percentages.TR*100*$progress)}%</div>
<div title={stats.NC} bind:this={nc} class="empire-stat faction-nc-bg"><strong>NC</strong><br/>{Math.round(percentages.NC*100*$progress)}%</div>
<div title={stats.VS} bind:this={vs} class="empire-stat faction-vs-bg"><strong>VS</strong><br/>{Math.round(percentages.VS*100*$progress)}%</div>
</div>

View file

@ -0,0 +1,37 @@
<script>
import { onMount } from 'svelte'
import PaginatedList from './PaginatedList'
import axios from 'axios'
import moment from 'moment'
export let account_id;
async function fetch(page) {
try {
const resp = await axios.get("/api/user/" + account_id + "/logins?page="+page);
return [resp.data.logins, resp.data.page];
} catch (e) {
console.log(e)
return undefined;
}
}
onMount(async () => {
});
</script>
<PaginatedList {fetch} let:data={logins} let:pagination={pagination}>
<table slot="body" class="table table-dark table-responsive">
<thead>
<td>Login Time</td>
</thead>
<tbody>
{#each logins as login, i}
<tr>
<td>{moment(login.login_time).fromNow()}</td>
</tr>
{/each}
</tbody>
</table>
</PaginatedList>

48
app/components/Nav.svelte Normal file
View file

@ -0,0 +1,48 @@
<script>
import { get_initial_state, logout, isAdmin, loggedIn, username } from '../UserState.js';
import axios from 'axios'
export let route;
export let pageCtx;
console.log(pageCtx)
</script>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark justify-content-between">
<div class="container">
<a class="navbar-brand" href="/">
<img src="/img/logo_crop.png" height="30" class="d-inline-block align-top" alt="">
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item" class:active={route=="/"}>
<a class="nav-link" href="/">Server Status</a>
</li>
{#if $isAdmin}
<li class="nav-item" class:active={route=="/admin"}>
<a class="nav-link" style="color: red;" href="/admin">Admin Panel</a>
</li>
{/if}
</ul>
<ul class="navbar-nav">
{#if $loggedIn}
<li class="nav-item" class:active={route=="/profile"}>
<span class="navbar-text">Welcome <a href="/profile">{$username}</a>!</span>
</li>
<li class="nav-item">
<a class="nav-link" on:click|preventDefault={logout} href="/logout">Logout</a>
</li>
{:else}
<li class="nav-item" class:active={route=="/login"}>
<a class="nav-link" href="/login">Login</a>
</li>
<li class="nav-item" class:active={route=="/register"}>
<a class="nav-link" href="/register">Register</a>
</li>
{/if}
</ul>
</div>
</div>
</nav>

View file

@ -0,0 +1,57 @@
<script>
import { onMount } from 'svelte'
import axios from 'axios'
import Pagination from '../components/Pagination'
export let fetch;
let data;
let fetching = false;
let pagination = { page: 1 };
export async function refresh() {
await list_fetch(pagination.page)
}
onMount(async () => {
const url = new URL(window.location.href)
let page = url.searchParams.get('page')
if (page == undefined)
page = 1;
await list_fetch(page);
})
async function pageChange(page) {
if (pagination.page == page || fetching)
return
await list_fetch(page);
}
async function list_fetch(page) {
fetching = true;
try {
if (fetch != undefined) {
const results = await fetch(page)
if (results != undefined) {
data = results[0];
pagination = results[1]
}
}
} finally {
fetching = false;
}
}
</script>
{#if data}
<slot name="header" data={data} pagination={pagination}></slot>
<Pagination {pagination} {pageChange} />
<slot name="body" data={data} pagination={pagination}></slot>
<Pagination {pagination} {pageChange} />
<slot name="footer" data={data} pagination={pagination}></slot>
{/if}

View file

@ -0,0 +1,80 @@
<script>
export let pagination;
export let pageChange;
let numPages = 10;
let pages = []
$ : {
const new_pages = [];
let pi = 0, i;
let pg = pagination;
const pageChunk = Math.max(Math.ceil(numPages/3), 1);
const middleChunk = Math.max(Math.ceil(pageChunk/2), 1);
const leftBound = Math.min(pageChunk+1, pagination.page_count);
const rightBound = Math.max(pagination.page_count-pageChunk, 1);
// fast path: draw all pages
if (pg.page_count <= numPages || rightBound <= leftBound) {
for (i = 1; i <= pg.page_count; i++)
new_pages[pi++] = i;
} else {
let middleLeft = Math.max(pg.page-middleChunk, leftBound);
let middleRight = Math.min(pg.page+middleChunk, rightBound);
// left and middle chunks are joined
if (middleLeft == leftBound) {
middleLeft += 1;
middleRight = Math.min(middleLeft+pageChunk, rightBound);
// middle and right chunks are joined
} else if (middleRight == rightBound) {
middleRight -= 1;
middleLeft = Math.min(middleRight-middleChunk, rightBound);
}
//console.log("[1-"+leftBound+"]", "["+middleLeft+"-"+middleRight+"]", "["+rightBound+"-"+pagination.page_count+"]");
// left chunk
for (i = 1; i <= leftBound; i++) new_pages[pi++] = i;
if (leftBound+1 != middleLeft) new_pages[pi++] = -1;
// middle chunk
for (i = middleLeft; i <= middleRight; i++) new_pages[pi++] = i;
// right chunk
if (middleRight+1 != rightBound) new_pages[pi++] = -1;
for (i = rightBound; i <= pg.page_count; i++) new_pages[pi++] = i;
}
pages = new_pages
//console.log(pages);
}
</script>
<p>Displaying {(pagination.page-1)*pagination.items_per_page+1} &mdash; {Math.min(pagination.page*pagination.items_per_page, pagination.item_count)}</p>
<nav aria-label="Page navigation">
<ul class="pagination pagination-sm">
<li class="page-item" class:disabled={pagination.page<=1}>
<a class="page-link" href={"?page="+(pagination.page-1)}
on:click={(e) => pageChange(pagination.page-1)}
aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
{#each pages as page,i}
{#if page == -1}
<li class="page-item page-last-separator disabled" ><a class="page-link">...<a></li>
{:else}
<li class="page-item" class:active={page==pagination.page}><a on:click={(e) => pageChange(page)} href={"?page="+page} class="page-link">{page}</a></li>
{/if}
{/each}
<li class="page-item" class:disabled={pagination.page>=pagination.page_count}>
<a class="page-link" href={"?page="+(pagination.page+1)}
on:click={(e) => pageChange(pagination.page+1)}
aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
</ul>
</nav>