Merge pull request 'Client feature: Add Login view' (#2) from frontend/feature/login-view into feature/user-auth

Reviewed-on: https://git.tams.tech/scott/kalkutago/pulls/2
This commit is contained in:
scott 2023-06-27 10:07:18 +00:00
commit 396ed28079
3 changed files with 93 additions and 11 deletions

View file

@ -6,7 +6,8 @@ const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/', component: TableView },
{ path: '/new-track', component: NewTrackView }
{ path: '/new-track', component: NewTrackView },
{ path: '/login', component: import('./views/Login.vue') }
// for other pages:
// {path: '/', component: import('./views/TableView.vue')}
]

View file

@ -18,9 +18,19 @@ function dateQuery(date: Date): URLSearchParams {
return query
}
export const state = reactive({
tracks: new Array<Track>,
state: State.Unfetched,
interface LoggedInUser {
name: string
}
class AppState {
tracks: Array<Track>
state: State
user?: LoggedInUser
constructor() {
this.tracks = new Array<Track>
this.state = State.Unfetched
}
streamUpdatesFromServer() {
const source = new EventSource("/api/v1/updates")
source.addEventListener("open", () => console.debug("opened event source"))
@ -69,17 +79,17 @@ export const state = reactive({
window.location = window.location
})
window.addEventListener('beforeunload', () => source.close())
},
}
async repopulate() {
this.state = State.Fetching
this.tracks = await Track.fetchAll()
},
}
async populate() {
if (this.state != State.Unfetched) return
await this.repopulate()
this.streamUpdatesFromServer()
this.state = State.Fetched
},
}
async taskCompleted(track: Track, date: Date): Promise<Tick> {
const query = dateQuery(date)
const response: Response = await fetch(`/api/v1/tracks/${track.id}/ticked?${query.toString()}`, { method: "PATCH" })
@ -89,13 +99,13 @@ export const state = reactive({
throw new Error(`error setting tick for track ${track.id} ("${track.name}"): ${response.status} ${response.statusText}`)
}
return JSON.parse(body)
},
}
async taskMarkedIncomplete(track: Track, date: Date) {
const query = dateQuery(date)
const { ok, status, statusText } = await fetch(`/api/v1/tracks/${track.id}/all-ticks?${query.toString()}`, { method: 'DELETE' })
if (!ok)
error(`error deleting ticks for ${track.id}: ${statusText} (${status})`)
},
}
async addTrack(track: Track): Promise<boolean> {
const response = await fetch('/api/v1/tracks', {
method: "POST",
@ -105,9 +115,11 @@ export const state = reactive({
if (!response.ok)
error(`error submitting track: ${track}: ${response.statusText} (${response.status})`)
return response.ok
},
}
async removeTrack(trackID: number) {
const response = await fetch(`/api/v1/tracks/${trackID}`, { method: "DELETE" })
if (!response.ok) error(`error deleting track with ID ${trackID}: ${response.statusText} (${response.status})`)
}
})
}
export const state = reactive(new AppState)

View file

@ -0,0 +1,69 @@
<script setup lang="ts">
import { ref } from 'vue'
import { state } from '../state';
const name = ref("")
const password = ref("")
async function signUp() {
const $name = name.value
const result = await fetch("/api/v1/auth", {
method: 'POST',
body: JSON.stringify({ name: $name, password: password.value })
})
if (result.ok) {
state.user = { name: $name }
}
}
async function login() {
const $name = name.value
const result = await fetch("/api/v1/auth", {
method: 'PUT',
body: JSON.stringify({ name: $name, password: password.value })
})
if (result.ok) {
state.user = { name: $name }
}
}
</script>
<template>
<div class="modal is-active">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<div class="modal-card-title">Log in or Sign up</div>
</header>
<section class="modal-card-body">
<div class="field">
<label for="username">Name</label>
<div class="control">
<input type="text" name="username" class="input" v-model="name" />
</div>
</div>
<div class="field">
<label for="password" class="label">Password</label>
<div class="control">
<input type="password" name="password" class="input" v-model="password" />
</div>
</div>
</section>
<footer class="modal-card-foot">
<button class="submit button is-success" @click="login">Log in</button>
<button class="submit button is-info" @click="signUp">Sign Up</button>
</footer>
</div>
</div>
</template>
<style scoped>
.modal-card-foot {
flex-direction: row-reverse;
}
.button.submit {
margin-left: 10px;
}
</style>