mirror of
https://gitlab.com/wgp/dougal/software.git
synced 2025-12-06 12:37:08 +00:00
Add ProjectSettings view
This commit is contained in:
607
lib/www/client/source/src/views/ProjectSettings.vue
Normal file
607
lib/www/client/source/src/views/ProjectSettings.vue
Normal file
@@ -0,0 +1,607 @@
|
||||
<template>
|
||||
<v-container fluid>
|
||||
|
||||
<v-overlay :value="loading && !configuration" absolute>
|
||||
<v-progress-circular
|
||||
indeterminate
|
||||
size="64"
|
||||
></v-progress-circular>
|
||||
</v-overlay>
|
||||
|
||||
<v-overlay :value="!configuration && !loading" absolute opacity="0.8">
|
||||
<v-row justify="center">
|
||||
<v-alert
|
||||
type="error"
|
||||
>
|
||||
The configuration could not be loaded.
|
||||
</v-alert>
|
||||
</v-row>
|
||||
<v-row justify="center">
|
||||
<v-btn color="primary" @click="getConfiguration">Retry</v-btn>
|
||||
</v-row>
|
||||
</v-overlay>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-dialog
|
||||
max-width="600px"
|
||||
:value="dialog"
|
||||
@input="closeDialog"
|
||||
>
|
||||
<template v-slot:activator="{ on, attrs }">
|
||||
<v-btn v-if="writeaccess"
|
||||
small
|
||||
color="teal" dark
|
||||
v-bind="attrs"
|
||||
v-on="on"
|
||||
title="Create a new survey based on this one by changing only a few settings"
|
||||
>Clone survey</v-btn>
|
||||
</template>
|
||||
<v-card>
|
||||
<v-card-title>Create a survey based on this one</v-card-title>
|
||||
<v-card-text>
|
||||
<p>Only a subset of fields will be changed.</p>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="4">
|
||||
<v-toolbar
|
||||
dense
|
||||
flat
|
||||
>
|
||||
<v-toolbar-title>
|
||||
Survey configuration
|
||||
</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
|
||||
<v-switch
|
||||
dense
|
||||
label="Active"
|
||||
title="Inactive surveys do not take new data, but changes to the logs can still be made."
|
||||
:disabled="!configuration"
|
||||
v-model="surveyState"
|
||||
></v-switch>
|
||||
|
||||
<v-treeview
|
||||
dense
|
||||
activatable
|
||||
hoverable
|
||||
:multiple-active="false"
|
||||
:active.sync="active"
|
||||
:open.sync="open"
|
||||
:items="treeview"
|
||||
style="cursor:pointer;"
|
||||
>
|
||||
</v-treeview>
|
||||
|
||||
</v-col>
|
||||
|
||||
<v-col cols="8" v-if="activeComponent">
|
||||
<component :is="activeComponent" :value="activeValues" @input="save" @close="deselect"></component>
|
||||
</v-col>
|
||||
<v-col cols="8" v-else>
|
||||
<v-card>
|
||||
<v-card-text>
|
||||
<p>Select a configuration section to change its settings. When clicking <v-btn small color="primary">Save</v-btn> the changes are immediately saved to the server and start to take effect.</p>
|
||||
|
||||
<v-divider class="my-5"></v-divider>
|
||||
|
||||
<v-alert border="left" type="warning">
|
||||
Be careful when changing configuration settings! It is rather easy to break things.
|
||||
</v-alert>
|
||||
|
||||
<v-alert border="left" type="info">
|
||||
On the first save, the survey will be switched to <em>inactive</em> if not already so. This means that no new data will be read. Remember to switch back to <strong>active</strong> when satisfied with your changes.
|
||||
</v-alert>
|
||||
|
||||
<v-divider class="my-5"></v-divider>
|
||||
|
||||
<p>It is recommended that you download a backup of your configuration before making any changes.</p>
|
||||
|
||||
<v-divider class="my-5"></v-divider>
|
||||
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn outlined
|
||||
color="primary"
|
||||
title="Any unsaved changes you might have will be lost."
|
||||
@click="getConfiguration"
|
||||
>
|
||||
<v-icon small left>mdi-cloud-refresh</v-icon>
|
||||
Refresh
|
||||
</v-btn>
|
||||
|
||||
<v-spacer></v-spacer>
|
||||
|
||||
<v-btn
|
||||
class="ml-2"
|
||||
color="primary"
|
||||
:disabled="!configuration"
|
||||
:href="`/api/project/${this.$route.params.project}/configuration?mime=application/yaml`"
|
||||
:download="`${this.$route.params.project}.yaml`"
|
||||
>
|
||||
<v-icon small left>mdi-cloud-download</v-icon>
|
||||
Download
|
||||
</v-btn>
|
||||
<v-dialog
|
||||
max-width="400px"
|
||||
>
|
||||
<template v-slot:activator="{ on, attrs }">
|
||||
<v-btn
|
||||
class="ml-2"
|
||||
color="warning"
|
||||
v-bind="attrs"
|
||||
v-on="on"
|
||||
>
|
||||
<v-icon small left>mdi-cloud-upload</v-icon>
|
||||
Upload
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-card flat>
|
||||
<v-card-text class="pt-5">
|
||||
<v-file-input
|
||||
class="mt-4"
|
||||
show-size
|
||||
accept="application/json"
|
||||
label="Select configuration file"
|
||||
append-outer-icon="mdi-cloud-upload"
|
||||
@click-append-outer="upload"
|
||||
></v-file-input>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex';
|
||||
|
||||
import DougalProjectSettingsNameId from '@/components/project-settings/name-id';
|
||||
import DougalProjectSettingsGroups from '@/components/project-settings/groups';
|
||||
import DougalProjectSettingsGeodetics from '@/components/project-settings/geodetics';
|
||||
import DougalProjectSettingsBinning from '@/components/project-settings/binning';
|
||||
import DougalProjectSettingsFilePath from '@/components/project-settings/file-path';
|
||||
import DougalProjectSettingsPreplots from '@/components/project-settings/preplots';
|
||||
import DougalProjectSettingsRawP111 from '@/components/project-settings/input-raw-p111';
|
||||
import DougalProjectSettingsFinalP111 from '@/components/project-settings/input-final-p111';
|
||||
import DougalProjectSettingsRawNTBP from '@/components/project-settings/input-raw-ntbp';
|
||||
import DougalProjectSettingsFinalPending from '@/components/project-settings/input-final-pending';
|
||||
import DougalProjectSettingsSmartsource from '@/components/project-settings/input-smartsource';
|
||||
import DougalProjectSettingsPlanner from '@/components/project-settings/planner';
|
||||
import DougalProjectSettingsOnlineLineNameFormat from '@/components/project-settings/online-line-name-format';
|
||||
import DougalProjectSettingsASAQC from '@/components/project-settings/asaqc';
|
||||
import DougalProjectSettingsProduction from '@/components/project-settings/production';
|
||||
// Temporary placeholder component
|
||||
import DougalProjectSettingsNotImplemented from '@/components/project-settings/not-implemented';
|
||||
|
||||
const components = {
|
||||
name_id: DougalProjectSettingsNameId,
|
||||
groups: DougalProjectSettingsGroups,
|
||||
geodetics: DougalProjectSettingsGeodetics,
|
||||
binning: DougalProjectSettingsBinning,
|
||||
input_files: DougalProjectSettingsFilePath,
|
||||
preplots: DougalProjectSettingsPreplots,
|
||||
//raw_data: DougalProjectSettingsNotImplemented,
|
||||
raw_data_p111: DougalProjectSettingsRawP111,
|
||||
raw_data_smartsource: DougalProjectSettingsSmartsource,
|
||||
raw_data_ntbp: DougalProjectSettingsRawNTBP,
|
||||
//final_data: DougalProjectSettingsNotImplemented,
|
||||
final_data_p111: DougalProjectSettingsFinalP111,
|
||||
final_data_pending: DougalProjectSettingsFinalPending,
|
||||
line_name_format: DougalProjectSettingsOnlineLineNameFormat,
|
||||
planner_settings: DougalProjectSettingsPlanner,
|
||||
logging: DougalProjectSettingsNotImplemented,
|
||||
logging_preset_comments: DougalProjectSettingsNotImplemented,
|
||||
logging_labels: DougalProjectSettingsNotImplemented,
|
||||
asaqc: DougalProjectSettingsASAQC,
|
||||
production: DougalProjectSettingsProduction
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "DougalProjectSettings",
|
||||
|
||||
data () {
|
||||
return {
|
||||
configuration: null,
|
||||
active: [],
|
||||
open: [],
|
||||
treeview: [
|
||||
/*
|
||||
{
|
||||
id: 0,
|
||||
name: "Archive",
|
||||
values: (cfg) => cfg.archived,
|
||||
save: (data, cfg) => {
|
||||
cfg.archived = data.archived;
|
||||
return cfg;
|
||||
}
|
||||
},
|
||||
*/
|
||||
{
|
||||
id: "name_id",
|
||||
name: "Name and ID",
|
||||
values: (obj) => ({
|
||||
id: obj?.id,
|
||||
name: obj?.name
|
||||
}),
|
||||
save: async (data, cfg) => {
|
||||
await this.patch({
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "groups",
|
||||
name: "Groups",
|
||||
values: (obj) => ({
|
||||
groups: obj?.groups
|
||||
}),
|
||||
save: async (data, cfg) => {
|
||||
await this.patch({
|
||||
groups: data.groups,
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "geodetics",
|
||||
name: "Geodetics",
|
||||
values: (obj) => ({
|
||||
epsg: obj?.epsg
|
||||
}),
|
||||
save: async (data, cfg) => {
|
||||
await this.patch({
|
||||
epsg: data.epsg
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "binning",
|
||||
name: "Binning",
|
||||
values: (obj) => {
|
||||
const data = {...obj.binning};
|
||||
data.origin = {...obj.binning.origin};
|
||||
return data;
|
||||
},
|
||||
save: async (data, cfg) => {
|
||||
await this.patch({binning: {...data}});
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "input_files",
|
||||
name: "Input files",
|
||||
values: obj => ({ path: obj.rootPath}),
|
||||
save: async (data, cfg) => {
|
||||
await this.patch({rootPath: data.path});
|
||||
},
|
||||
children: [
|
||||
{
|
||||
id: "preplots",
|
||||
name: "Preplots",
|
||||
values: (obj) => ({
|
||||
preplots: obj.preplots,
|
||||
rootPath: obj.rootPath
|
||||
}),
|
||||
save: async (data, cfg) => {
|
||||
await this.patch({preplots: {...data.preplots}})
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "raw_data",
|
||||
name: "Raw data",
|
||||
children: [
|
||||
{
|
||||
id: "raw_data_p111",
|
||||
name: "P1/11",
|
||||
values: (obj) => ({
|
||||
rootPath: obj.rootPath,
|
||||
globs: [...obj.raw.p111.globs],
|
||||
paths: [...obj.raw.p111.paths],
|
||||
pattern: structuredClone(obj.raw.p111.pattern)
|
||||
}),
|
||||
save: async (data, cfg) => {
|
||||
await this.patch({raw: {p111: {...data}}});
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "raw_data_smartsource",
|
||||
name: "Smartsource",
|
||||
values: (obj) => ({
|
||||
rootPath: obj.rootPath,
|
||||
globs: [...obj.raw.smsrc.globs],
|
||||
paths: [...obj.raw.smsrc.paths],
|
||||
pattern: structuredClone(obj.raw.smsrc.pattern)
|
||||
}),
|
||||
save: async (data, cfg) => {
|
||||
await this.patch({raw: {smsrc: {...data}}});
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "raw_data_ntbp",
|
||||
name: "NTBP detection",
|
||||
values: (obj) => ({
|
||||
regex: obj.raw.ntbp?.pattern?.regex,
|
||||
flags: obj.raw.ntbp?.pattern?.flags
|
||||
}),
|
||||
save: async (data, cfg) => {
|
||||
await this.patch({
|
||||
raw:{
|
||||
ntbp: {
|
||||
pattern: {
|
||||
regex: data.regex,
|
||||
flags: data.flags
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "final_data",
|
||||
name: "Final data",
|
||||
children: [
|
||||
{
|
||||
id: "final_data_p111",
|
||||
name: "P1/11",
|
||||
values: (obj) => ({
|
||||
rootPath: obj.rootPath,
|
||||
globs: [...obj.final.p111.globs],
|
||||
paths: [...obj.final.p111.paths],
|
||||
pattern: structuredClone(obj.final.p111.pattern)
|
||||
}),
|
||||
save: async (data, cfg) => {
|
||||
await this.patch({final: {p111: {...data}}});
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "final_data_pending",
|
||||
name: "Pending line detection",
|
||||
values: (obj) => ({
|
||||
regex: obj.final.pending?.pattern?.regex,
|
||||
flags: obj.final.pending?.pattern?.flags
|
||||
}),
|
||||
save: async (data, cfg) => {
|
||||
await this.patch({
|
||||
final:{
|
||||
pending: {
|
||||
pattern: {
|
||||
regex: data.regex,
|
||||
flags: data.flags
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "line_name_format",
|
||||
name: "Line name format",
|
||||
values: (obj) => ({...obj.online?.line?.pattern}),
|
||||
save: async (data, cfg) => {
|
||||
await this.patch({
|
||||
online: {
|
||||
line: {
|
||||
pattern: {
|
||||
...data
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "planner_settings",
|
||||
name: "Planner settings",
|
||||
values: (obj) => ({...obj.planner}),
|
||||
save: async (data, cfg) => {
|
||||
await this.patch({
|
||||
planner: {...data}
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "production",
|
||||
name: "Production settings",
|
||||
values: (obj) => ({...obj.production}),
|
||||
save: async (data, cfg) => {
|
||||
await this.patch({
|
||||
production: {...data}
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "logging",
|
||||
name: "Logging",
|
||||
children: [
|
||||
{
|
||||
id: "logging_preset_comments",
|
||||
name: "Preset comments"
|
||||
},
|
||||
{
|
||||
id: "logging_labels",
|
||||
name: "Labels"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "cloud_apis",
|
||||
name: "Cloud APIs",
|
||||
children: [
|
||||
{
|
||||
id: "asaqc",
|
||||
name: "ASAQC",
|
||||
values: (obj) => ({...obj.asaqc}),
|
||||
save: async (data, cfg) => {
|
||||
await this.patch({asaqc: {...cfg}});
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
dialog: false
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
active (cur, prev) {
|
||||
if (cur == prev) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Current", cur);
|
||||
console.log("Item", this.activeItem);
|
||||
console.log("Component", this.activeComponent);
|
||||
if (!this.activeComponent && this.activeItem?.children?.length) {
|
||||
// Automatically expand this item
|
||||
if (!this.open.includes(cur)) {
|
||||
this.open.push(cur);
|
||||
}
|
||||
this.$nextTick( () => {
|
||||
const idx = this.active.findIndex(i => i == cur);
|
||||
if (idx != -1) {
|
||||
this.active.push(this.activeItem.children[0].id);
|
||||
this.active.splice(cur, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
||||
activeComponent () {
|
||||
return components[this.active[0]];
|
||||
},
|
||||
|
||||
activeItem () {
|
||||
function walk (tree) {
|
||||
const list = [];
|
||||
for (const leaf of tree) {
|
||||
if (leaf.children) {
|
||||
list.push(...walk(leaf.children));
|
||||
}
|
||||
list.push(leaf);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
return walk(this.treeview).find(i => i.id === this.active[0]);
|
||||
},
|
||||
|
||||
activeValues () {
|
||||
return this.activeItem?.values &&
|
||||
this.activeItem.values(this.configuration);
|
||||
},
|
||||
|
||||
surveyState: {
|
||||
get () {
|
||||
return this.configuration && !this.configuration.archived;
|
||||
},
|
||||
|
||||
async set (value) {
|
||||
if (this.configuration) {
|
||||
await this.patch({archived: !value});
|
||||
// this.configuration.archived = !value;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
...mapGetters(['user', 'writeaccess', 'loading', 'serverEvent'])
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
async getConfiguration () {
|
||||
this.configuration = null;
|
||||
const url = `/project/${this.$route.params.project}/configuration/`;
|
||||
this.configuration = await this.api([url]);
|
||||
},
|
||||
|
||||
makeTree (obj, id=0) {
|
||||
const isObject = typeof obj === "object" && !Array.isArray(obj) && obj !== null;
|
||||
return isObject
|
||||
? Object.keys(obj).map(k => {
|
||||
const children = this.makeTree(obj[k], id); //.filter(i => i !== null);
|
||||
return {
|
||||
id: id++,
|
||||
name: k,
|
||||
children
|
||||
}
|
||||
})
|
||||
: null;
|
||||
},
|
||||
|
||||
deselect () {
|
||||
this.active.pop();
|
||||
},
|
||||
|
||||
async save (data) {
|
||||
console.log("SAVING", data);
|
||||
if (this.activeItem?.save) {
|
||||
await this.activeItem.save(data, this.configuration);
|
||||
/*
|
||||
const cfg = this.activeItem.save(data, this.configuration);
|
||||
this.configuration = {...cfg};
|
||||
|
||||
this.surveyState = false;
|
||||
*/
|
||||
// TODO And now push to the server
|
||||
}
|
||||
},
|
||||
|
||||
async patch (data) {
|
||||
const url = `/project/${this.$route.params.project}/configuration/`;
|
||||
const init = {
|
||||
method: "PATCH",
|
||||
body: data
|
||||
};
|
||||
const callback = async (err, res) => {
|
||||
if (!err && res.ok) {
|
||||
this.showSnack(["Configuration saved", "success"]);
|
||||
}
|
||||
};
|
||||
|
||||
const refreshedConfiguration = await this.api([url, init, callback]);
|
||||
if (refreshedConfiguration) {
|
||||
this.configuration == refreshedConfiguration;
|
||||
}
|
||||
},
|
||||
|
||||
upload () {
|
||||
console.log("UPLOAD");
|
||||
},
|
||||
|
||||
closeDialog () {
|
||||
},
|
||||
|
||||
...mapActions(["api", "showSnack"])
|
||||
|
||||
},
|
||||
|
||||
async mounted () {
|
||||
this.getConfiguration();
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user