mirror of
https://gitlab.com/wgp/dougal/software.git
synced 2025-12-06 13:07:08 +00:00
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:
@@ -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>
|
||||||
Reference in New Issue
Block a user