0
0
Fork 0
mirror of https://github.com/verdigado/organization_folders.git synced 2024-12-06 11:22:41 +01:00

initial commit of GUI

This commit is contained in:
Jonathan Treffler 2024-11-18 18:32:34 +01:00
parent b64ae41cd0
commit f07b9953e3
20 changed files with 1416 additions and 0 deletions

View file

@ -0,0 +1,92 @@
<script setup>
import { ref } from "vue";
import NcTextField from "@nextcloud/vue/dist/Components/NcTextField.js";
import NcModal from "@nextcloud/vue/dist/Components/NcModal.js";
const props = defineProps({
title: {
type: String,
default: "Löschen",
},
loading: {
type: Boolean,
default: false,
},
matchText: {
type: String,
default: "löschen",
},
});
const open = ref(false);
const confirmText = ref("");
const openDialog = () => {
open.value = true;
}
const closeDialog = () => {
open.value = false;
}
</script>
<template>
<div>
<slot name="activator" :open="openDialog">
<button type="button" @click="openDialog">
Löschen
</button>
</slot>
<NcModal v-if="open"
class="modal"
:out-transition="true"
:has-next="false"
:has-previous="false"
@close="closeDialog">
<div class="modal__content">
<div class="modal__title">
<h1>
{{ props.title }}
</h1>
</div>
<div>
<slot name="content" />
<p>
Gib hier als Bestätigung "<span style="user-select: all;">{{ props.matchText }}</span>" ein.
</p>
<NcTextField class="confirmText"
:value.sync="confirmText"
style=" --color-border-maxcontrast: #949494;" />
<slot name="delete-button" :close="closeDialog" :disabled="confirmText !== props.matchText">
<button type="button">
Löschen
</button>
</slot>
</div>
</div>
</NcModal>
</div>
</template>
<style scoped>
.confirmText {
margin: 1rem 0 1rem 0;
}
.modal__title {
margin-bottom: 16px;
height: 50px;
}
.modal__title h1 {
text-align: center;
font-size: 1.6rem;
font-weight: bold;
}
.modal__content {
margin: 50px;
min-height: 500px;
}
</style>

View file

@ -0,0 +1,162 @@
<script setup>
import NcEmptyContent from "@nextcloud/vue/dist/Components/NcEmptyContent.js"
import NcLoadingIcon from "@nextcloud/vue/dist/Components/NcLoadingIcon.js"
import NcActions from "@nextcloud/vue/dist/Components/NcActions.js"
import NcActionButton from "@nextcloud/vue/dist/Components/NcActionButton.js"
import NcButton from "@nextcloud/vue/dist/Components/NcButton.js"
import { showError } from "@nextcloud/dialogs"
//import MemberListNewItem from "./MemberListNewItem.vue"
import MemberListItem from "./MemberListItem.vue"
import Plus from "vue-material-design-icons/Plus.vue"
import Close from "vue-material-design-icons/Close.vue"
import HelpCircle from "vue-material-design-icons/HelpCircle.vue"
import api from "../../api.js"
import { ref } from 'vue';
const props = defineProps({
members: {
type: Array,
required: true,
},
});
const loading = ref(false);
const error = ref(undefined);
const newItemComponent = ref(null);
const addMenuOpen = ref(false);
const setNewItemComponent = (name) => {
this.newItemComponent.value = name
this.addMenuOpen.value = false
};
const deleteMember = async (memberId) => {
this.loading.value = true
try {
api.deleteGroupMember(this.groupId, memberId)
//this.members.value = this.members.filter((m) => m.id !== memberId)
} catch (err) {
showError(err.message)
} finally {
this.loading.value = false
}
};
const updateMember = async (memberId, changes) => {
this.loading.value = true
try {
const member = await api.updateGroupMember(this.groupId, memberId, changes)
this.members = this.members.map((m) => m.id === member.id ? member : m)
} catch (err) {
showError(err.message)
} finally {
this.loading.value = false
}
};
const addMember = async ({ mappingId, mappingType }) => {
this.loading.value = true
try {
const _member = await api.addGroupMember(this.groupId, {
mappingType,
mappingId,
type: "member",
})
this.members.push(_member)
this.setNewItemComponent(null)
} catch (err) {
showError(err.message)
} finally {
this.loading = false
}
};
</script>
<template>
<div>
<div class="title">
<h3>Mitglieder</h3>
<!--<NcActions :disabled="!!newItemComponent" type="secondary">
<template #icon>
<Plus :size="20" />
</template>
<NcActionButton icon="icon-group" close-after-click @click="setNewItemComponent('new_item')">
Benutzer/Gruppe hinzufügen
</NcActionButton>
<NcActionButton icon="icon-group" close-after-click @click="setNewItemComponent('new_role_item')">
Organisation Rolle hinzufügen
</NcActionButton>
</NcActions>-->
</div>
<!--<div v-if="newItemComponent" class="new-item">
<NcButton type="tertiary" @click="setNewItemComponent(null)">
<template #icon>
<Close />
</template>
</NcButton>
<MemberListNewItem v-if="newItemComponent === 'new_item'" :group-id="groupId" @selected="addMember" />
</div>-->
<table>
<thead style="display: contents;">
<tr>
<th />
<th>Name</th>
<th>
<div style="display: flex; align-items: center;">
<span>Typ</span>
<HelpCircle v-tooltip="'Für Admins gelten die oben ausgewählten Ordneradministrator*innen Berechtigungen, für Mitglieder die Ordnermitglieder Berechtigungen. Admins haben auf diese Einstellungen Zugriff.'" style="margin-left: 5px;" :size="15" />
</div>
</th>
<th>Aktion</th>
</tr>
</thead>
<tbody style="display: contents">
<tr v-if="loading">
<td colspan="4" style="grid-column-start: 1; grid-column-end: 5">
<NcLoadingIcon :size="50" />
</td>
</tr>
<tr v-if="!loading && !members.length">
<td colspan="4" style="grid-column-start: 1; grid-column-end: 5">
<NcEmptyContent title="Keine Gruppenmitglieder" />
</td>
</tr>
<MemberListItem v-for="member in members"
:key="member.id"
:member="member"
@update="updateMember"
@delete="deleteMember" />
</tbody>
</table>
</div>
</template>
<style scoped>
table {
width: 100%;
margin-bottom: 14px;
display: grid;
grid-template-columns: max-content minmax(30px, auto) max-content max-content;
}
table tr {
display: contents;
}
table td, table th {
padding: 8px;
}
.title {
display: flex;
justify-content: flex-start;
align-items: center;
margin-top: 24px;
}
h3 {
font-weight: bold;
margin-right: 24px;
}
.new-item {
display: flex;
}
</style>

View file

@ -0,0 +1,95 @@
<script setup>
import Delete from "vue-material-design-icons/Delete.vue"
import NcButton from "@nextcloud/vue/dist/Components/NcButton.js"
import NcAvatar from "@nextcloud/vue/dist/Components/NcAvatar.js"
import ChevronRight from "vue-material-design-icons/ChevronRight.vue"
import { computed } from "vue"
const props = defineProps({
member: {
type: Object,
required: true,
},
});
const friendlyNameParts = computed(() => props.member.principal.split(" / "));
const emit = defineEmits(["update", "delete"]);
const typeOptions = [
{ label: "Mitglied", value: 1 },
{ label: "Manager", value: 2 },
];
const onTypeSelected = (e) => {
emit("update", props.member.id, {
type: e.target.value,
})
};
const onDeleteClicked = (e) => {
emit("delete", props.member.id)
};
</script>
<template>
<tr>
<td>
<NcAvatar :user="props.member.type === 1 ? props.member.principal : undefined"
:disabled-menu="true"
:disabled-tooltip="true"
:icon-class="props.member.type === 2 ? 'icon-group' : undefined" />
</td>
<td>
<div class="friendlyNameParts">
<div v-for="(friendlyNamePart, index) of friendlyNameParts" :key="'breadcrumb-' + friendlyNamePart" class="friendlyNamePartDiv">
<p v-tooltip="friendlyNamePart" class="friendlyNamePartP">
{{ friendlyNamePart }}
</p>
<ChevronRight v-if="index !== friendlyNameParts.length - 1" :size="20" />
</div>
</div>
</td>
<td>
<select :value="props.member.permissionLevel" @input="onTypeSelected">
<option v-for="{ label, value} in typeOptions" :key="value" :value="value">
{{ label }}
</option>
</select>
</td>
<td>
<NcButton type="tertiary-no-background" @click="onDeleteClicked">
<template #icon>
<Delete :size="20" />
</template>
</NcButton>
</td>
</tr>
</template>
<style scoped>
td {
padding: 8px;
}
.friendlyNameParts {
display: inline-flex;
max-width: 100%;
overflow-x: clip;
}
.friendlyNamePartP {
white-space: nowrap;
overflow: hidden;
}
.friendlyNamePartP:not(:last-child) {
text-overflow: ellipsis;
}
.friendlyNamePartDiv {
display: inline-flex;
min-width: 20px;
}
.friendlyNamePartDiv:last-child {
flex-shrink: 0;
}
</style>

View file

@ -0,0 +1 @@
export { default } from "./MemberList.vue"

View file

@ -0,0 +1,93 @@
<script setup>
import { computed } from "vue";
import PermissionsInputRow from "./PermissionsInputRow.vue";
const props = defineProps({
resource: {
type: Object,
required: true,
},
})
const emit = defineEmits(["permissionUpdated"]);
const permissionGroups = computed(() => {
return [
{
field: "managersAclPermission",
label: "Resourcenadministrator*innen",
value: props.resource.managersAclPermission,
mask: 31,
},
{
field: "membersAclPermission",
label: "Resourcenmitglieder",
value: props.resource.membersAclPermission,
mask: 31,
},
{
field: "inheritedAclPermission",
label: "Vererbte Berechtigungen",
value: props.resource.inheritedAclPermission,
mask: 31,
},
]
});
const permissionUpdated = async (field, value) => {
emit("permissionUpdated", { field, value });
}
</script>
<template>
<table>
<thead>
<tr>
<th />
<th v-tooltip="t('groupfolders', 'Read')" class="state-column">
{{ t('groupfolders', 'Read') }}
</th>
<th v-tooltip="t('groupfolders', 'Write')" class="state-column">
{{ t('groupfolders', 'Write') }}
</th>
<th v-tooltip="t('groupfolders', 'Create')" class="state-column">
{{ t('groupfolders', 'Create') }}
</th>
<th v-tooltip="t('groupfolders', 'Delete')" class="state-column">
{{ t('groupfolders', 'Delete') }}
</th>
<th v-tooltip="t('groupfolders', 'Share')" class="state-column">
{{ t('groupfolders', 'Share') }}
</th>
</tr>
</thead>
<tbody>
<PermissionsInputRow v-for="{ field, label, mask, value} in permissionGroups"
:key="field"
:label="label"
:mask="mask"
:value="value"
@change="(val) => permissionUpdated(field, val)" />
</tbody>
</table>
</template>
<style scoped>
table {
width: 100%;
margin-bottom: 14px;
}
table td, table th {
padding: 0
}
.state-column {
text-align: center;
width: 44px !important;
padding: 3px;
}
thead .state-column {
text-overflow: ellipsis;
overflow: hidden;
}
</style>

View file

@ -0,0 +1,82 @@
<script setup>
import { computed } from "vue";
import { calcBits, toggleBit } from "../../helpers/permission-helpers.js";
const props = defineProps({
label: {
type: String,
default: "",
},
mask: {
type: Number,
default: 31,
},
value: {
type: Number,
default: 0,
},
})
const emit = defineEmits(["change"]);
const calcBitButtonProps = (bitName, bitState) => {
const states = {
INHERIT_DENY: {
tooltipText: t("groupfolders", "Denied (Inherited permission)"),
className: "icon-deny inherited",
},
INHERIT_ALLOW: {
tooltipText: t("groupfolders", "Allowed (Inherited permission)"),
className: "icon-checkmark inherited",
},
SELF_DENY: {
tooltipText: t("groupfolders", "Denied"),
className: "icon-deny",
},
SELF_ALLOW: {
tooltipText: t("groupfolders", "Allowed"),
className: "icon-checkmark",
},
}
return {
...states[bitState],
bitName,
}
};
const bitButtonProps = computed(() => Object.entries(calcBits(props.value, props.mask)).map(([bitName, { state }]) => calcBitButtonProps(bitName, state)));
const onClick = (bitName) => emit("change", toggleBit(props.value, bitName));
</script>
<template>
<tr>
<td v-tooltip="props.label">
{{ props.label }}
</td>
<td v-for="({ bitName, className, tooltipText }) in bitButtonProps" :key="bitName">
<button v-tooltip="tooltipText"
:class="className"
@click="() => onClick(bitName)" />
</td>
</tr>
</template>
<style scoped>
button {
height: 24px;
border-color: transparent;
}
button:hover {
height: 24px;
border-color: var(--color-primary, #0082c9);
}
.icon-deny {
background-image: url('../../../img/deny.svg');
}
.inherited {
opacity: 0.5;
}
</style>

View file

@ -0,0 +1 @@
export { default } from "./Permissions.vue"