Add Vue component for configuring sailline CSV imports.

Sailline CSV imports are related to issue #264. Not yet implemented
server-side.
This commit is contained in:
D. Berge
2023-11-08 19:22:19 +01:00
parent f82f2c78c7
commit 0137bd84d5

View File

@@ -0,0 +1,295 @@
<template>
<v-card flat elevation="0">
<v-card-title v-if="title">{{ title }}</v-card-title>
<v-card-subtitle v-if="subtitle">{{ subtitle }}</v-card-subtitle>
<v-card-text>
<v-tabs v-model="viewTab">
<v-tab>Text</v-tab>
<v-tab>Parsed</v-tab>
</v-tabs>
<v-tabs-items v-model="viewTab">
<v-tab-item>
<v-simple-table dense class="text">
<template v-slot:default>
<colgroup v-if="showLineNumbers">
<col class="line_no"/>
</colgroup>
<thead>
<tr>
<th class="line_no">
<v-simple-checkbox
v-ripple
off-icon="mdi-format-list-numbered"
title="Show line numbers"
v-model="showLineNumbers"
>
</v-simple-checkbox>
</th>
<th v-for="(header, idx) in headers" :key="idx"
:style="`color:${header.colour};`"
>
{{ header.text }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, ridx) in rows" :key="ridx">
<td class="line_no"">
<small v-if="showLineNumbers">
{{ ridx + (typeof numberedLines == "number" ? numberedLines : 0)+1 }}
</small>
</td>
<td v-for="(cell, cidx) in row" :key="cidx"
:style="`background-color:${cell.colour};`"
>
{{ cell.text }}
</td>
</tr>
</tbody>
</template>
</v-simple-table>
</v-tab-item>
<v-tab-item>
<!-- Parsed view -->
<v-simple-table dense class="parsed">
<template v-slot:default>
<thead>
<tr>
<th
title="The line along which the vessel will nominally sail"
>Sail line</th>
<th
title="Whether the line will be acquired in the incrementing or decrementing shot points direction"
>Direction</th>
<th
title="Whether the line is planned to be acquired. Some lines may be in the preplot but not intended to be shot in a particular campaign"
>Acquire?</th>
<th
title="The source lines that will be shot from this vessel line. Typically there is one source line per source array."
>Source lines</th>
<th
title="Any general remarks concerning this sail line (supports Markdown)"
>Remarks</th>
</tr>
</thead>
<tbody>
<tr v-for="(line, line_no) in saillines" :key="line_no">
<td>{{ line_no }}</td>
<td v-if="line.incr" title="Incrementing"></td>
<td v-else title="Decrementing"></td>
<td v-if="line.ntba" title="Not to be acquired" class="ko"></td>
<td v-else title="Line acquisition planned" class="ok"></td>
<td v-html="line.source_line.join('<br/>')"></td>
<td v-if="line['meta.colour']"
:style="`background-color:${line['meta.colour']};`"
v-html="$options.filters.markdown(line.remarks)"></td>
<td v-else
v-html="$options.filters.markdown(line.remarks)"></td>
</tr>
</tbody>
</template>
</v-simple-table>
</v-tab-item>
</v-tabs-items>
</v-card-text>
<v-card-actions>
</v-card-actions>
</v-card>
</template>
<style scoped>
/*.v-data-table table tbody tr td*/
.text th {
border: 1px solid hsl(0, 0%, 33.3%);
}
.text td {
border-inline: 1px solid hsl(0, 0%, 33.3%);
}
.parsed td {
vertical-align: top;
}
.line_no {
text-align: right;
width: 4ex;
border: none !important;
}
.ok {
color: green;
}
.ko {
color: red;
}
</style>
<script>
import { parse } from 'csv-parse/sync'
import { getHSLColourFor } from '@/lib/hsl'
import truncateText from '@/lib/truncate-text'
export default {
name: "DougalSaillinesStringDecoder",
components: {
},
props: {
text: String,
fields: Object,
//delimiter: String,
headerRow: { type: [ Boolean, Number ], default: false},
numberedLines: [ Boolean, Number ],
maxHeight: String,
editableFieldList: { type: Boolean, default: true },
readonly: Boolean,
title: String,
subtitle: String
},
data () {
return {
delimiter: ",",
showLineNumbers: null,
text_: "",
viewTab: null
}
},
computed: {
cells () {
return parse(this.text_, {delimiter: this.delimiter, trim: true});
},
headers () {
return this.cells[0]?.map(cell => ({
text: cell,
colour: this.getHSLColourFor(cell),
backgroundColour: this.getHSLColourFor(cell, 0.2),
})) ?? [];
},
rows () {
return [...this.cells].slice(1).map(r =>
r.map( (c, ι) => ({
text: truncateText(c),
colour: this.headers[ι]?.backgroundColour
})));
},
/*
* A saillines object looks like:
*
* {
* [sail_line]: {
* incr: true, // or false
* ntba: true, // or false
* remarks: "",
* source_line: [ 1000, 1001, …],
* "meta.colour": ""
* },
* …
* }
*/
saillines () {
// Return an array of the column numbers
// corresponding to `key`.
// This file accepts duplicate column numbers,
// notably for `source_line`.
const key_indices = (key) =>
this.headers.reduce( (acc, cur, ι) => {
if (cur.text == key) {
acc.push(ι)
}
return acc;
}, []);
// Properties of the sailline object
const keys = [ "incr", "ntba", "remarks", "source_line", "meta.colour" ];
// To transform the input text into the required format for each field
const transformer = (key) => {
const transformers = {
incr: (v) => Boolean(Number(v)),
ntba: (v) => Boolean(Number(v)),
remarks: String,
source_line: Number,
};
return transformers[key] ?? String;
};
// This is the saillines object
const lines = {};
// The column numbers for each property
const columns = keys.map( k => [ k, key_indices(k) ] );
// The column number for the sail_line property, which
// we use as a key.
const sail_line_idx = key_indices("sail_line")[0];
// Transform each line in the input file into a
// sailline object (just for display purposes,
// this is not exactly how the server will do it).
for (const row of this.rows) {
const sail_line = row[sail_line_idx]?.text;
const values = columns.map(i => [
i[0],
i[0] == "source_line"
? i[1].map(idx => transformer(i[0])(row[idx]?.text))
: transformer(i[0])(row[i[1][0]]?.text)
]);
lines[sail_line] = Object.fromEntries(values);
}
return lines;
}
},
watch: {
text () {
if (this.text != this.text_) {
this.reset();
}
},
numberedLines (cur, prev) {
if (cur != prev) {
this.showLineNumbers = typeof cur == "number" || cur;
}
}
},
methods: {
getHSLColourFor: getHSLColourFor.bind(this),
numberLine (number, line) {
return `<span class="line-number">${number}</span>${line}`;
},
reset () {
this.text_ = this.text.replaceAll("\r", "");
}
},
mounted () {
this.reset();
}
}
</script>