Add components for editing organisations settings

This commit is contained in:
D. Berge
2025-07-24 20:35:17 +02:00
parent ec41d26a7a
commit f8e5b74c1a
2 changed files with 308 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
<template>
<v-row dense no-gutters>
<v-col>
<slot name="prepend"></slot>
</v-col>
<v-col cols="6">
<v-text-field
class="mr-5"
dense
label="Name"
:value="name"
:readonly="true"
></v-text-field>
</v-col>
<v-col>
<v-checkbox
class="mr-3"
label="Read"
v-model="operations.read"
:readonly="readonly"
></v-checkbox>
</v-col>
<v-col>
<v-checkbox
class="mr-3"
label="Write"
v-model="operations.write"
:readonly="readonly"
></v-checkbox>
</v-col>
<v-col>
<v-checkbox
class="mr-3"
label="Edit"
v-model="operations.edit"
:readonly="readonly"
></v-checkbox>
</v-col>
<v-col>
<!-- Just to fill the twelve-column grid -->
<!--
NOTE: this column could also be used for
a popdown menu with additional operations
if needed.
-->
</v-col>
<v-col>
<slot name="append"></slot>
</v-col>
</v-row>
</template>
<style scoped>
</style>
<script>
import { Organisations } from '@dougal/organisations';
export default {
name: "DougalOrganisationsItem",
props: {
name: String,
value: Object,
readonly: Boolean,
},
data () {
return {
operations: {...this.value}
}
},
watch: {
value: {
handler (newValue) {
this.operations = {...this.value};
},
deep: true,
},
operations: {
handler (newValue) {
if (["read", "write", "edit"].some( k => newValue[k] != this.value[k] )) {
// Only emit if a value has actually changed
this.$emit("input", {...newValue});
}
},
deep: true,
},
},
methods: {
reset () {
}
},
mounted () {
this.reset();
}
}
</script>

View File

@@ -0,0 +1,196 @@
<template>
<v-card>
<v-card-title>Organisations</v-card-title>
<v-card-subtitle>Organisation access</v-card-subtitle>
<v-card-text>
<v-form>
<v-container>
<dougal-organisations-item v-for="organisation in localOrganisations.names()"
:key="organisation"
:name="organisation"
:value="localOrganisations.get(organisation)"
@input="setOrganisation(organisation, $event)"
>
<template v-slot:append v-if="!readonly">
<v-btn
class="ml-3"
fab
text
small
title="Remove this organisation"
>
<v-icon
color="error"
@click="removeOrganisation(organisation)"
>mdi-minus</v-icon>
</v-btn>
</template>
</dougal-organisations-item>
<v-row no-gutters class="mb-2" v-if="!readonly">
<h4>Add organisation</h4>
</v-row>
<v-row no-gutters class="mb-2" v-if="!readonly">
<v-combobox v-if="canCreateOrganisations"
label="Organisation"
:items="remainingOrganisations"
v-model="organisationName"
@input.native="organisationName = $event.srcElement.value"
@keyup.enter="addOrganisation()"
></v-combobox>
<v-select v-else
label="Organisation"
:items="remainingOrganisations"
v-model="organisationName"
></v-select>
<v-btn
class="ml-3"
fab
text
small
title="Add organisation"
:disabled="!(organisationName && organisationName.length)"
@click="addOrganisation()"
>
<v-icon
color="primary"
>mdi-plus</v-icon>
</v-btn>
</v-row>
</v-container>
</v-form>
</v-card-text>
<v-card-actions>
<!--
<v-spacer></v-spacer>
<v-btn
color="secondary"
@click="back"
>Back</v-btn>
-->
</v-card-actions>
</v-card>
</template>
<script>
import { Organisations } from '@dougal/organisations';
import DougalOrganisationsItem from './organisations-item';
export default {
name: "DougalOrganisations",
components: {
DougalOrganisationsItem
},
props: {
self: Object,
organisations: Object,
readonly: Boolean
},
data () {
return {
organisationName: "",
localOrganisations: this.setLocalOrganisations(this.organisations)
}
},
computed: {
availableOrganisations () {
return this.self.organisations.names();
},
// Organisations available to add.
// These are the organisations in `availableOrganisations`
// minus any that have already been added.
// The special value "*" (meaning "every organisation")
// is not included.
remainingOrganisations () {
const orgs = [];
for (const org of this.availableOrganisations) {
if (org != "*" && !this.localOrganisations.has(org)) {
orgs.push(org);
}
}
return orgs;
},
canCreateOrganisations () {
return this.self.organisations.value("*")?.edit ?? false;
},
validationErrors () {
const errors = [];
// Check if there is at least one organisation
if (this.localOrganisations.length) {
errors.push("ERR_NO_ORGS");
}
// Check if at least one organisation has edit rights
},
},
watch: {
organisations (newValue) {
this.localOrganisations = this.setLocalOrganisations(newValue);
},
},
methods: {
setLocalOrganisations (value) {
return new Organisations(this.organisations);
},
setOrganisation(name, value) {
this.localOrganisations.set(name, value);
this.$emit("update:organisations", new Organisations(this.localOrganisations));
},
addOrganisation () {
const key = this.organisationName;
if (!this.localOrganisations.has(key)) {
this.localOrganisations.set(key);
this.$emit("update:organisations", this.localOrganisations);
}
this.organisationName = "";
},
removeOrganisation (key) {
if (this.localOrganisations.has(key)) {
this.localOrganisations.remove(key);
}
this.$emit("update:organisations", this.localOrganisations);
},
reset () {
},
save () {
},
back () {
this.$emit('close');
}
},
mounted () {
this.reset();
}
}
</script>