aboutsummaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
Diffstat (limited to 'web')
-rw-r--r--web/pw-server/src/lib/components/Leaderboard.svelte14
-rw-r--r--web/pw-server/src/lib/components/navbar/UserControls.svelte4
-rw-r--r--web/pw-server/src/routes/bots/[bot_id].svelte74
-rw-r--r--web/pw-server/src/routes/bots/[bot_name].svelte139
-rw-r--r--web/pw-server/src/routes/users/[user_name].svelte25
5 files changed, 160 insertions, 96 deletions
diff --git a/web/pw-server/src/lib/components/Leaderboard.svelte b/web/pw-server/src/lib/components/Leaderboard.svelte
index 8582198..d29d5d6 100644
--- a/web/pw-server/src/lib/components/Leaderboard.svelte
+++ b/web/pw-server/src/lib/components/Leaderboard.svelte
@@ -41,11 +41,17 @@
<td class="leaderboard-rating">
{formatRating(entry)}
</td>
- <td class="leaderboard-bot">{entry["bot"]["name"]}</td>
+ <td class="leaderboard-bot">
+ <a class="leaderboard-href" href="/bots/{entry['bot']['name']}"
+ >{entry["bot"]["name"]}
+ </a></td
+ >
<td class="leaderboard-author">
{#if entry["author"]}
- <!-- TODO: remove duplication -->
- <a href="/users/{entry["author"]["username"]}">{entry["author"]["username"]}</a>
+ <!-- TODO: remove duplication -->
+ <a class="leaderboard-href" href="/users/{entry['author']['username']}"
+ >{entry["author"]["username"]}</a
+ >
{/if}
</td>
</tr>
@@ -71,7 +77,7 @@
color: #333;
}
- .leaderboard-author a{
+ .leaderboard-href {
text-decoration: none;
color: black;
}
diff --git a/web/pw-server/src/lib/components/navbar/UserControls.svelte b/web/pw-server/src/lib/components/navbar/UserControls.svelte
index a9bd87b..5646982 100644
--- a/web/pw-server/src/lib/components/navbar/UserControls.svelte
+++ b/web/pw-server/src/lib/components/navbar/UserControls.svelte
@@ -36,8 +36,8 @@
<div class="user-controls">
{#if $currentUser}
- <a class="current-user-name" href="/users/{$currentUser["username"]}">
- {$currentUser["username"]}
+ <a class="current-user-name" href="/users/{$currentUser['username']}">
+ {$currentUser["username"]}
</a>
<div class="sign-out" on:click={signOut}>Sign out</div>
{:else}
diff --git a/web/pw-server/src/routes/bots/[bot_id].svelte b/web/pw-server/src/routes/bots/[bot_id].svelte
deleted file mode 100644
index 3eece10..0000000
--- a/web/pw-server/src/routes/bots/[bot_id].svelte
+++ /dev/null
@@ -1,74 +0,0 @@
-<script lang="ts" context="module">
- import { get_session_token } from "$lib/auth";
-
- export async function load({ page }) {
- const token = get_session_token();
- const res = await fetch(`/api/bots/${page.params["bot_id"]}`, {
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${token}`,
- },
- });
-
- if (res.ok) {
- const data = await res.json();
- return {
- props: {
- bot: data["bot"],
- bundles: data["bundles"],
- },
- };
- }
-
- return {
- status: res.status,
- error: new Error("Could not load bot"),
- };
- }
-</script>
-
-<script lang="ts">
- import dayjs from "dayjs";
-
- export let bot: object;
- export let bundles: object[];
-
- let files;
-
- async function submitCode() {
- console.log("click");
- const token = get_session_token();
-
- const formData = new FormData();
- formData.append("File", files[0]);
-
- const res = await fetch(`/api/bots/${bot["id"]}/upload`, {
- method: "POST",
- headers: {
- // the content type header will be set by the browser
- Authorization: `Bearer ${token}`,
- },
- body: formData,
- });
-
- console.log(res.statusText);
- }
-</script>
-
-<div>
- {bot["name"]}
-</div>
-
-<div>Upload code</div>
-<form on:submit|preventDefault={submitCode}>
- <input type="file" bind:files />
- <button type="submit">Submit</button>
-</form>
-
-<ul>
- {#each bundles as bundle}
- <li>
- bundle created at {dayjs(bundle["created_at"]).format("YYYY-MM-DD HH:mm")}
- </li>
- {/each}
-</ul>
diff --git a/web/pw-server/src/routes/bots/[bot_name].svelte b/web/pw-server/src/routes/bots/[bot_name].svelte
new file mode 100644
index 0000000..9e9f016
--- /dev/null
+++ b/web/pw-server/src/routes/bots/[bot_name].svelte
@@ -0,0 +1,139 @@
+<script lang="ts" context="module">
+ import { get_session_token } from "$lib/auth";
+
+ export async function load({ params, fetch }) {
+ const token = get_session_token();
+ const res = await fetch(`/api/bots/${params["bot_name"]}`, {
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${token}`,
+ },
+ });
+
+ if (res.ok) {
+ const { bot, owner, versions } = await res.json();
+ // sort most recent first
+ versions.sort((a: string, b: string) =>
+ dayjs(a["created_at"]).isAfter(b["created_at"]) ? -1 : 1
+ );
+ return {
+ props: {
+ bot,
+ owner,
+ versions,
+ },
+ };
+ }
+
+ return {
+ status: res.status,
+ error: new Error("Could not find bot"),
+ };
+ }
+</script>
+
+<script lang="ts">
+ import dayjs from "dayjs";
+
+ import { currentUser } from "$lib/stores/current_user";
+
+ export let bot: object;
+ export let owner: object;
+ export let versions: object[];
+
+ // function last_updated() {
+ // versions.sort()
+ // }
+
+ // let files;
+
+ // async function submitCode() {
+ // console.log("click");
+ // const token = get_session_token();
+
+ // const formData = new FormData();
+ // formData.append("File", files[0]);
+
+ // const res = await fetch(`/api/bots/${bot["id"]}/upload`, {
+ // method: "POST",
+ // headers: {
+ // // the content type header will be set by the browser
+ // Authorization: `Bearer ${token}`,
+ // },
+ // body: formData,
+ // });
+
+ // console.log(res.statusText);
+ // }
+</script>
+
+<!--
+<div>Upload code</div>
+<form on:submit|preventDefault={submitCode}>
+ <input type="file" bind:files />
+ <button type="submit">Submit</button>
+</form> -->
+
+<div class="container">
+ <div class="header">
+ <h1 class="bot-name">{bot["name"]}</h1>
+ {#if owner}
+ <a class="owner-name" href="/users/{owner['username']}">
+ {owner["username"]}
+ </a>
+ {/if}
+ </div>
+
+ {#if $currentUser && $currentUser["user_id"] === bot["owner_id"]}
+ <div>
+ <!-- TODO: can we avoid hardcoding the url? -->
+ Publish a new version by pushing a docker container to
+ <code>registry.planetwars.dev/{bot["name"]}:latest</code>, or using the web editor.
+ </div>
+ {/if}
+
+ <div class="versions">
+ <h4>Versions</h4>
+ <ul class="version-list">
+ {#each versions as version}
+ <li>
+ {dayjs(version["created_at"]).format("YYYY-MM-DD HH:mm")}
+ </li>
+ {/each}
+ </ul>
+ </div>
+</div>
+
+<style lang="scss">
+ .container {
+ width: 800px;
+ max-width: 80%;
+ margin: 50px auto;
+ }
+
+ .header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-end;
+ margin-bottom: 60px;
+ border-bottom: 1px solid black;
+ }
+
+ $header-space-above-line: 12px;
+
+ .bot-name {
+ font-size: 24pt;
+ margin-bottom: $header-space-above-line;
+ }
+
+ .owner-name {
+ font-size: 14pt;
+ text-decoration: none;
+ color: #333;
+ margin-bottom: $header-space-above-line;
+ }
+
+ .versions {
+ margin: 30px 0;
+ }
+</style>
diff --git a/web/pw-server/src/routes/users/[user_name].svelte b/web/pw-server/src/routes/users/[user_name].svelte
index fab3a96..a1801f4 100644
--- a/web/pw-server/src/routes/users/[user_name].svelte
+++ b/web/pw-server/src/routes/users/[user_name].svelte
@@ -1,12 +1,4 @@
<script lang="ts" context="module">
- function fetchJson(url: string): Promise<Response> {
- return fetch(url, {
- headers: {
- "Content-Type": "application/json",
- },
- });
- }
-
export async function load({ params, fetch }) {
const userName = params["user_name"];
const userBotsResponse = await fetch(`/api/users/${userName}/bots`);
@@ -36,17 +28,17 @@
<h2>Bots</h2>
<ul class="bot-list">
{#each bots as bot}
- <li class="bot">
- <span class="bot-name">{bot['name']}</span>
- </li>
+ <li class="bot">
+ <a class="bot-name" href="/bots/{bot['name']}">{bot["name"]}</a>
+ </li>
{/each}
</ul>
</div>
<style lang="scss">
.container {
- min-width: 600px;
- max-width: 800px;
+ width: 800px;
+ max-width: 80%;
margin: 50px auto;
}
@@ -56,7 +48,7 @@
}
.user-name {
- margin-bottom: .5em;
+ margin-bottom: 0.5em;
}
.bot-list {
@@ -75,10 +67,11 @@
.bot-name {
font-size: 20px;
font-weight: 400;
+ text-decoration: none;
+ color: black;
}
.bot:first-child {
border-top: 1px solid $border-color;
}
-
-</style> \ No newline at end of file
+</style>