forked from TWS/kalkutago
UI laid out and started
This commit is contained in:
parent
1438ab6d24
commit
441d3c28ec
|
@ -1,9 +1,9 @@
|
|||
<script setup lang="ts">
|
||||
import Table from "./components/Table.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button class="button is-info">Test, hello!</button>
|
||||
<Table></Table>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
|
40
client/src/components/Table.vue
Normal file
40
client/src/components/Table.vue
Normal file
|
@ -0,0 +1,40 @@
|
|||
<template>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th>Date</th>
|
||||
<th v-for="track in tracks" :key="track.id">{{ track.icon }}</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="date in dates" :key="date.valueOf()">
|
||||
<th>{{ dateString(date) }}</th>
|
||||
<td v-for="track in tracks" :key="track.id">
|
||||
<TickComponent :isSet="track.isSetOn(date)" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { Track } from '../track'
|
||||
import TickComponent from "./TickComponent.vue";
|
||||
|
||||
const tracks = ref(new Array<Track>())
|
||||
|
||||
const today = new Date()
|
||||
const ONE_DAY_MS = 86_400_000
|
||||
// A list of the past 60 days
|
||||
const dates = [...Array(60).keys()]
|
||||
.map(n => new Date(today.valueOf() - (n * ONE_DAY_MS)))
|
||||
|
||||
console.log(dates)
|
||||
|
||||
// The date as a string like 2023-06-11
|
||||
function dateString(date: Date) {
|
||||
return date.toISOString().substring(0, 10)
|
||||
}
|
||||
|
||||
Track.fetchAll().then(result => tracks.value = result)
|
||||
|
||||
</script>
|
18
client/src/components/TableRow.vue
Normal file
18
client/src/components/TableRow.vue
Normal file
|
@ -0,0 +1,18 @@
|
|||
<template>
|
||||
<tr>
|
||||
<td>{{dateString()}}</td>
|
||||
<td v-for="tick in ticks" :key="tick.id">{{tick.name}}</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
const props = defineProps<{date: Date}>();
|
||||
|
||||
const ticks = [{id: 1, name: 'test'}]
|
||||
|
||||
// The date as a string like 2023-06-11
|
||||
function dateString() {
|
||||
return props.date.toISOString().substring(0, 10)
|
||||
}
|
||||
</script>
|
9
client/src/components/TickComponent.vue
Normal file
9
client/src/components/TickComponent.vue
Normal file
|
@ -0,0 +1,9 @@
|
|||
<template>
|
||||
<button :class="className()">{{ props.isSet ? "x" : "\u{A0}" }}</button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{ isSet: boolean }>()
|
||||
|
||||
const className = () => props.isSet ? "is-rounded is-info" : "is-rounded"
|
||||
</script>
|
6
client/src/error.ts
Normal file
6
client/src/error.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
export const error = console.error
|
||||
|
||||
export function handleError(message: string): any {
|
||||
return (...args: any) => error(message, ...args)
|
||||
}
|
65
client/src/ticks.ts
Normal file
65
client/src/ticks.ts
Normal file
|
@ -0,0 +1,65 @@
|
|||
interface ITick {
|
||||
id: number
|
||||
track_id?: number
|
||||
year?: number
|
||||
month?: number
|
||||
day?: number
|
||||
hour?: number
|
||||
minute?: number
|
||||
second?: number
|
||||
has_time_info?: number
|
||||
}
|
||||
|
||||
class Tick implements ITick {
|
||||
id: number
|
||||
track_id?: number
|
||||
year?: number
|
||||
month?: number
|
||||
day?: number
|
||||
hour?: number
|
||||
minute?: number
|
||||
second?: number
|
||||
has_time_info?: number
|
||||
|
||||
constructor(id: number,
|
||||
track_id?: number,
|
||||
year?: number,
|
||||
month?: number,
|
||||
day?: number,
|
||||
hour?: number,
|
||||
minute?: number,
|
||||
second?: number,
|
||||
has_time_info?: number,
|
||||
) {
|
||||
this.id = id
|
||||
this.track_id = track_id
|
||||
this.year = year
|
||||
this.month = month
|
||||
this.day = day
|
||||
this.hour = hour
|
||||
this.minute = minute
|
||||
this.second = second
|
||||
this.has_time_info = has_time_info
|
||||
}
|
||||
|
||||
static fromJSON(tick: ITick): Tick {
|
||||
return new Tick(
|
||||
tick.id,
|
||||
tick.track_id,
|
||||
tick.year,
|
||||
tick.month,
|
||||
tick.day,
|
||||
tick.hour,
|
||||
tick.minute,
|
||||
tick.second,
|
||||
tick.has_time_info,
|
||||
)
|
||||
}
|
||||
|
||||
date(): Date | null {
|
||||
if (this.year && this.month && this.day && this.hour && this.minute && this.second) {
|
||||
return null
|
||||
}
|
||||
return new Date(this.year!, this.month!, this.day, this.hour, this.minute, this.second)
|
||||
}
|
||||
}
|
100
client/src/track.ts
Normal file
100
client/src/track.ts
Normal file
|
@ -0,0 +1,100 @@
|
|||
import { error } from "./error"
|
||||
|
||||
export interface ITrack {
|
||||
id: number
|
||||
name: String
|
||||
description: String
|
||||
icon: String
|
||||
enabled: number
|
||||
multiple_entries_per_day?: number
|
||||
color?: number
|
||||
order?: number
|
||||
ticks?: Array<Tick>
|
||||
}
|
||||
|
||||
export class Track implements ITrack {
|
||||
id: number
|
||||
name: String
|
||||
description: String
|
||||
icon: String
|
||||
enabled: number
|
||||
multiple_entries_per_day?: number
|
||||
color?: number
|
||||
order?: number
|
||||
ticks?: Array<Tick>
|
||||
|
||||
constructor(
|
||||
id: number,
|
||||
name: String,
|
||||
description: String,
|
||||
icon: String,
|
||||
enabled: number,
|
||||
multiple_entries_per_day?: number,
|
||||
color?: number,
|
||||
order?: number,
|
||||
ticks?: Array<ITick>
|
||||
) {
|
||||
this.id = id
|
||||
this.name = name
|
||||
this.description = description
|
||||
this.icon = icon
|
||||
this.enabled = enabled
|
||||
this.multiple_entries_per_day = multiple_entries_per_day
|
||||
this.color = color
|
||||
this.order = order
|
||||
this.ticks = ticks?.map(tick => Tick.fromJSON(tick))
|
||||
this.isSetOn = this.isSetOn.bind(this)
|
||||
this.fetchTicks = this.fetchTicks.bind(this)
|
||||
}
|
||||
|
||||
static fromJSON(track: ITrack): Track {
|
||||
return new Track(track.id, track.name, track.description, track.icon, track.enabled, track.multiple_entries_per_day, track.color, track.order)
|
||||
}
|
||||
|
||||
isSetOn(date: Date): boolean {
|
||||
for (var tick of (this.ticks ?? [])) {
|
||||
if (
|
||||
date.getUTCFullYear() == tick.year &&
|
||||
date.getUTCMonth() == tick.month &&
|
||||
date.getDate() == tick.day
|
||||
) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
async fetchTicks(): Promise<Track> {
|
||||
const response = await fetch(`/api/v1/tracks/${this.id}/ticks`)
|
||||
if (response.ok) {
|
||||
this.ticks = await response.json()
|
||||
} else {
|
||||
throw new Error(`error fetching ticks: ${response.statusText} (${response.status})`)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
static async fetchAll(): Promise<Array<Track>> {
|
||||
const result = await fetch('/api/v1/tracks')
|
||||
if (result.ok) {
|
||||
try {
|
||||
const body = await result.text();
|
||||
try {
|
||||
const tracks = Array.prototype.map.call(JSON.parse(body), Track.fromJSON) as Array<Track>
|
||||
return Promise.all(tracks.map((track: Track) => track.fetchTicks()))
|
||||
} catch (e) {
|
||||
console.error('error parsing body from JSON')
|
||||
console.error(e)
|
||||
console.debug(body)
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
result.text()
|
||||
.then(console.debug)
|
||||
.catch(console.error)
|
||||
}
|
||||
} else {
|
||||
error(`error fetching tracks: ${result.statusText} (${result.status})`)
|
||||
}
|
||||
return []
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue