parent
							
								
									af2527dd47
								
							
						
					
					
						commit
						6c9edb976f
					
				@ -0,0 +1,9 @@ | 
				
			||||
<template> | 
				
			||||
  <router-view></router-view> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
  export default { | 
				
			||||
      name: 'UserLayout' | 
				
			||||
  } | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,9 @@ | 
				
			||||
<template> | 
				
			||||
  <router-view></router-view> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
  export default { | 
				
			||||
      name: 'UserGroupLayout' | 
				
			||||
  } | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,9 @@ | 
				
			||||
<template> | 
				
			||||
  <router-view></router-view> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
  export default { | 
				
			||||
      name: 'UserRelUserLayout' | 
				
			||||
  } | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,15 @@ | 
				
			||||
export default { | 
				
			||||
  path: '/resources/users', | 
				
			||||
  meta: { requiresAuth: true }, | 
				
			||||
  name: 'users', | 
				
			||||
  component: () => import('../components/user/Layout.vue'), | 
				
			||||
  //redirect: { name: 'UserGroupList' },
 | 
				
			||||
  children: [ | 
				
			||||
    { | 
				
			||||
      name: 'UserGroupShow', | 
				
			||||
      //path: ':id',
 | 
				
			||||
      path: 'show', | 
				
			||||
      component: () => import('../views/usergroup/Show.vue') | 
				
			||||
    } | 
				
			||||
  ] | 
				
			||||
}; | 
				
			||||
@ -0,0 +1,20 @@ | 
				
			||||
export default { | 
				
			||||
  path: '/resources/usergroups', | 
				
			||||
  meta: { requiresAuth: true }, | 
				
			||||
  name: 'usergroups', | 
				
			||||
  component: () => import('../components/usergroup/Layout.vue'), | 
				
			||||
  redirect: { name: 'UserGroupList' }, | 
				
			||||
  children: [ | 
				
			||||
    { | 
				
			||||
      name: 'UserGroupList', | 
				
			||||
      path: '', | 
				
			||||
      component: () => import('../views/usergroup/List.vue') | 
				
			||||
    }, | 
				
			||||
    { | 
				
			||||
      name: 'UserGroupShow', | 
				
			||||
      //path: ':id',
 | 
				
			||||
      path: 'show', | 
				
			||||
      component: () => import('../views/usergroup/Show.vue') | 
				
			||||
    } | 
				
			||||
  ] | 
				
			||||
}; | 
				
			||||
@ -0,0 +1,20 @@ | 
				
			||||
export default { | 
				
			||||
  path: '/resources/friends', | 
				
			||||
  meta: { requiresAuth: true }, | 
				
			||||
  name: 'friends', | 
				
			||||
  component: () => import('../components/userreluser/Layout.vue'), | 
				
			||||
  redirect: { name: 'UserGroupList' }, | 
				
			||||
  children: [ | 
				
			||||
    { | 
				
			||||
      name: 'UserRelUserList', | 
				
			||||
      path: '', | 
				
			||||
      component: () => import('../views/userreluser/List.vue') | 
				
			||||
    }, | 
				
			||||
    { | 
				
			||||
      name: 'UserRelUserAdd', | 
				
			||||
      //path: ':id',
 | 
				
			||||
      path: 'add', | 
				
			||||
      component: () => import('../views/userreluser/Add.vue') | 
				
			||||
    } | 
				
			||||
  ] | 
				
			||||
}; | 
				
			||||
@ -0,0 +1,3 @@ | 
				
			||||
import makeService from './api'; | 
				
			||||
 | 
				
			||||
export default makeService('user'); | 
				
			||||
@ -0,0 +1,3 @@ | 
				
			||||
import makeService from './api'; | 
				
			||||
 | 
				
			||||
export default makeService('usergroups'); | 
				
			||||
@ -0,0 +1,3 @@ | 
				
			||||
import makeService from './api'; | 
				
			||||
 | 
				
			||||
export default makeService('user_rel_users'); | 
				
			||||
@ -0,0 +1,563 @@ | 
				
			||||
<template> | 
				
			||||
  <Toolbar | 
				
			||||
  > | 
				
			||||
    <template v-slot:right> | 
				
			||||
        <v-btn | 
				
			||||
            tile | 
				
			||||
            icon | 
				
			||||
            @click="composeHandler"> | 
				
			||||
          <v-icon icon="mdi-email-plus-outline" /> | 
				
			||||
        </v-btn> | 
				
			||||
 | 
				
			||||
        <v-btn | 
				
			||||
            tile | 
				
			||||
            icon | 
				
			||||
            :loading="isLoading" | 
				
			||||
            @click="reloadHandler"> | 
				
			||||
          <v-icon icon="mdi-refresh" /> | 
				
			||||
        </v-btn> | 
				
			||||
 | 
				
			||||
         <v-btn | 
				
			||||
            tile | 
				
			||||
            icon | 
				
			||||
            @click="confirmDeleteMultiple" | 
				
			||||
            :class="[ !selectedItems || !selectedItems.length ? 'hidden': '']" | 
				
			||||
         > | 
				
			||||
          <v-icon icon="mdi-delete" /> | 
				
			||||
        </v-btn> | 
				
			||||
<!--        :disabled="!selectedItems || !selectedItems.length"--> | 
				
			||||
        <v-btn | 
				
			||||
            icon | 
				
			||||
            tile | 
				
			||||
            @click="markAsUnReadMultiple" | 
				
			||||
            :class="[ !selectedItems || !selectedItems.length ? 'hidden': '']" | 
				
			||||
        > | 
				
			||||
          <v-icon icon="mdi-email" /> | 
				
			||||
        </v-btn> | 
				
			||||
 | 
				
			||||
        <v-btn | 
				
			||||
            tile | 
				
			||||
            icon | 
				
			||||
            :class="[ !selectedItems || !selectedItems.length ? 'hidden': '']" | 
				
			||||
        > | 
				
			||||
          <v-icon icon="mdi-email-open" /> | 
				
			||||
        </v-btn> | 
				
			||||
 | 
				
			||||
    </template> | 
				
			||||
  </Toolbar> | 
				
			||||
 | 
				
			||||
  <div class="flex flex-row pt-2"> | 
				
			||||
    <div class="w-1/5 "> | 
				
			||||
      <v-card | 
				
			||||
        max-width="300" | 
				
			||||
        tile | 
				
			||||
      > | 
				
			||||
      <v-list dense> | 
				
			||||
  <!--      v-model="selectedItem"--> | 
				
			||||
        <v-list-item-group | 
				
			||||
            color="primary" | 
				
			||||
        > | 
				
			||||
          <v-list-item @click="goToInbox"> | 
				
			||||
            <v-list-item-icon> | 
				
			||||
              <v-icon icon="mdi-inbox"></v-icon> | 
				
			||||
            </v-list-item-icon> | 
				
			||||
            <v-list-item-content> | 
				
			||||
              <v-list-item-title>Inbox</v-list-item-title> | 
				
			||||
            </v-list-item-content> | 
				
			||||
          </v-list-item> | 
				
			||||
 | 
				
			||||
          <v-list-item @click="goToSent"> | 
				
			||||
            <v-list-item-icon> | 
				
			||||
              <v-icon icon="mdi-send-outline"></v-icon> | 
				
			||||
            </v-list-item-icon> | 
				
			||||
            <v-list-item-content> | 
				
			||||
              <v-list-item-title>Sent</v-list-item-title> | 
				
			||||
            </v-list-item-content> | 
				
			||||
          </v-list-item> | 
				
			||||
 | 
				
			||||
          <v-list-item @click="goToUnread"> | 
				
			||||
            <v-list-item-icon> | 
				
			||||
              <v-icon icon="mdi-email-outline"></v-icon> | 
				
			||||
            </v-list-item-icon> | 
				
			||||
            <v-list-item-content> | 
				
			||||
              <v-list-item-title>Unread</v-list-item-title> | 
				
			||||
            </v-list-item-content> | 
				
			||||
          </v-list-item> | 
				
			||||
 | 
				
			||||
 | 
				
			||||
          <v-list-item | 
				
			||||
              v-for="(tag, i) in tags" | 
				
			||||
              :key="i" | 
				
			||||
              @click="goToTag(tag)" | 
				
			||||
          > | 
				
			||||
            <v-list-item-icon> | 
				
			||||
              <v-icon icon="mdi-label-outline"></v-icon> | 
				
			||||
            </v-list-item-icon> | 
				
			||||
            <v-list-item-content> | 
				
			||||
              <v-list-item-title v-text="tag.tag"></v-list-item-title> | 
				
			||||
            </v-list-item-content> | 
				
			||||
          </v-list-item> | 
				
			||||
        </v-list-item-group> | 
				
			||||
      </v-list> | 
				
			||||
    </v-card> | 
				
			||||
    </div> | 
				
			||||
    <div class="w-4/5 pl-4"> | 
				
			||||
      <div class="text-h4 q-mb-md">{{ title }}</div> | 
				
			||||
      <DataTable | 
				
			||||
        class="p-datatable-sm" | 
				
			||||
        :value="items" | 
				
			||||
        v-model:selection="selectedItems" | 
				
			||||
        dataKey="id" | 
				
			||||
        v-model:filters="filters" | 
				
			||||
        filterDisplay="menu" | 
				
			||||
        sortBy="sendDate" | 
				
			||||
        sortOrder="asc" | 
				
			||||
        :lazy="true" | 
				
			||||
        :paginator="true" | 
				
			||||
        :rows="10" | 
				
			||||
        :totalRecords="totalItems" | 
				
			||||
        :loading="isLoading" | 
				
			||||
        @page="onPage($event)" | 
				
			||||
        @sort="sortingChanged($event)" | 
				
			||||
        paginatorTemplate="CurrentPageReport FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown" | 
				
			||||
        :rowsPerPageOptions="[5, 10, 20, 50]" | 
				
			||||
        responsiveLayout="scroll" | 
				
			||||
        currentPageReportTemplate="Showing {first} to {last} of {totalRecords}" | 
				
			||||
        :globalFilterFields="['title', 'sendDate']"> | 
				
			||||
 | 
				
			||||
      <Column selectionMode="multiple" style="width: 3rem" :exportable="false"></Column> | 
				
			||||
 | 
				
			||||
      <Column field="userSender" :header="$t('From')" :sortable="false"> | 
				
			||||
        <template #body="slotProps"> | 
				
			||||
          <q-avatar size="40px"> | 
				
			||||
            <img :src="slotProps.data.userSender.illustrationUrl + '?w=80&h=80&fit=crop'" /> | 
				
			||||
          </q-avatar> | 
				
			||||
 | 
				
			||||
          <a | 
				
			||||
              v-if="slotProps.data" | 
				
			||||
              @click="showHandler(slotProps.data)" | 
				
			||||
              class="cursor-pointer" | 
				
			||||
              :class="[ true === slotProps.data.read ? 'font-normal': 'font-semibold']" | 
				
			||||
          > | 
				
			||||
            {{ slotProps.data.userSender.username }} | 
				
			||||
          </a> | 
				
			||||
        </template> | 
				
			||||
      </Column> | 
				
			||||
 | 
				
			||||
 | 
				
			||||
      <Column field="title" :header="$t('Title')" :sortable="false"> | 
				
			||||
        <template #body="slotProps"> | 
				
			||||
          <a | 
				
			||||
              v-if="slotProps.data" | 
				
			||||
              @click="showHandler(slotProps.data)" | 
				
			||||
              class="cursor-pointer" | 
				
			||||
              v-bind:class="{ 'font-semibold': !slotProps.data.read }" | 
				
			||||
          > | 
				
			||||
            {{ slotProps.data.title }} | 
				
			||||
          </a> | 
				
			||||
 | 
				
			||||
          <div class="flex flex-row"> | 
				
			||||
            <v-chip v-for="tag in slotProps.data.tags" > | 
				
			||||
              {{ tag.tag }} | 
				
			||||
            </v-chip> | 
				
			||||
          </div> | 
				
			||||
        </template> | 
				
			||||
 | 
				
			||||
        <!--         <template #filter="{filterModel}">--> | 
				
			||||
        <!--           <InputText type="text" v-model="filterModel.value" class="p-column-filter" placeholder="Search by name"/>--> | 
				
			||||
        <!--         </template>--> | 
				
			||||
        <!--         --> | 
				
			||||
 | 
				
			||||
        <!--      <template #filter="{filterModel}">--> | 
				
			||||
        <!--        <InputText type="text" v-model="filterModel.value" class="p-column-filter" placeholder="Search by title"/>--> | 
				
			||||
        <!--      </template>--> | 
				
			||||
        <!--      <template #filterclear="{filterCallback}">--> | 
				
			||||
        <!--        <Button type="button" icon="pi pi-times" @click="filterCallback()" class="p-button-secondary"></Button>--> | 
				
			||||
        <!--      </template>--> | 
				
			||||
        <!--      <template #filterapply="{filterCallback}">--> | 
				
			||||
        <!--        <Button type="button" icon="pi pi-check" @click="filterCallback()" class="p-button-success"></Button>--> | 
				
			||||
        <!--      </template>--> | 
				
			||||
      </Column> | 
				
			||||
 | 
				
			||||
      <Column field="sendDate" :header="$t('Send date')" :sortable="true"> | 
				
			||||
        <template #body="slotProps"> | 
				
			||||
          {{$luxonDateTime.fromISO(slotProps.data.sendDate).toRelative() }} | 
				
			||||
        </template> | 
				
			||||
      </Column> | 
				
			||||
 | 
				
			||||
      <Column :exportable="false"> | 
				
			||||
        <template #body="slotProps"> | 
				
			||||
          <div class="flex flex-row gap-2"> | 
				
			||||
            <v-btn | 
				
			||||
                tile | 
				
			||||
                icon | 
				
			||||
                @click="confirmDeleteItem(slotProps.data)" > | 
				
			||||
              <v-icon icon="mdi-delete" /> | 
				
			||||
            </v-btn> | 
				
			||||
          </div> | 
				
			||||
        </template> | 
				
			||||
      </Column> | 
				
			||||
    </DataTable> | 
				
			||||
    </div> | 
				
			||||
  </div> | 
				
			||||
 | 
				
			||||
<!--  Dialogs--> | 
				
			||||
 | 
				
			||||
  <Dialog v-model:visible="itemDialog" :style="{width: '450px'}" :header="$t('New folder')" :modal="true" class="p-fluid"> | 
				
			||||
    <div class="p-field"> | 
				
			||||
      <label for="name">{{ $t('Name') }}</label> | 
				
			||||
      <InputText | 
				
			||||
          autocomplete="off" | 
				
			||||
          id="title" | 
				
			||||
          v-model.trim="item.title" | 
				
			||||
          required="true" | 
				
			||||
          autofocus | 
				
			||||
          :class="{'p-invalid': submitted && !item.title}" | 
				
			||||
      /> | 
				
			||||
      <small class="p-error" v-if="submitted && !item.title">$t('Title is required')</small> | 
				
			||||
    </div> | 
				
			||||
 | 
				
			||||
    <template #footer> | 
				
			||||
      <Button label="Cancel" icon="pi pi-times" class="p-button-text" @click="hideDialog"/> | 
				
			||||
      <Button label="Save" icon="pi pi-check" class="p-button-text" @click="saveItem" /> | 
				
			||||
    </template> | 
				
			||||
  </Dialog> | 
				
			||||
 | 
				
			||||
  <Dialog v-model:visible="deleteItemDialog" :style="{width: '450px'}" header="Confirm" :modal="true"> | 
				
			||||
    <div class="confirmation-content"> | 
				
			||||
      <i class="pi pi-exclamation-triangle p-mr-3" style="font-size: 2rem" /> | 
				
			||||
      <span v-if="item">Are you sure you want to delete <b>{{item.title}}</b>?</span> | 
				
			||||
    </div> | 
				
			||||
    <template #footer> | 
				
			||||
      <Button label="No" icon="pi pi-times" class="p-button-text" @click="deleteItemDialog = false"/> | 
				
			||||
      <Button label="Yes" icon="pi pi-check" class="p-button-text" @click="deleteItemButton" /> | 
				
			||||
    </template> | 
				
			||||
  </Dialog> | 
				
			||||
 | 
				
			||||
  <Dialog v-model:visible="deleteMultipleDialog" :style="{width: '450px'}" header="Confirm" :modal="true"> | 
				
			||||
    <div class="confirmation-content"> | 
				
			||||
      <i class="pi pi-exclamation-triangle p-mr-3" style="font-size: 2rem" /> | 
				
			||||
      <span v-if="item">Are you sure you want to delete the selected items?</span> | 
				
			||||
    </div> | 
				
			||||
    <template #footer> | 
				
			||||
      <Button label="No" icon="pi pi-times" class="p-button-text" @click="deleteMultipleDialog = false"/> | 
				
			||||
      <Button label="Yes" icon="pi pi-check" class="p-button-text" @click="deleteMultipleItems" /> | 
				
			||||
    </template> | 
				
			||||
  </Dialog> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import { mapActions, mapGetters } from 'vuex'; | 
				
			||||
import { mapFields } from 'vuex-map-fields'; | 
				
			||||
import ListMixin from '../../mixins/ListMixin'; | 
				
			||||
import ActionCell from '../../components/ActionCell.vue'; | 
				
			||||
import Toolbar from '../../components/Toolbar.vue'; | 
				
			||||
import ResourceFileIcon from '../../components/documents/ResourceFileIcon.vue'; | 
				
			||||
import ResourceFileLink from '../../components/documents/ResourceFileLink.vue'; | 
				
			||||
 | 
				
			||||
import {useRoute, useRouter} from 'vue-router' | 
				
			||||
import DataFilter from '../../components/DataFilter'; | 
				
			||||
import DocumentsFilterForm from '../../components/documents/Filter'; | 
				
			||||
import { ref, reactive, onMounted, computed } from 'vue'; | 
				
			||||
import { useStore } from 'vuex'; | 
				
			||||
import isEmpty from 'lodash/isEmpty'; | 
				
			||||
import moment from "moment"; | 
				
			||||
import toInteger from "lodash/toInteger"; | 
				
			||||
import useState from "../../hooks/useState"; | 
				
			||||
import axios from "axios"; | 
				
			||||
import {ENTRYPOINT} from "../../config/entrypoint"; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'UserGroupList', | 
				
			||||
  servicePrefix: 'usergroups', | 
				
			||||
  components: { | 
				
			||||
    Toolbar, | 
				
			||||
    ActionCell, | 
				
			||||
    ResourceFileIcon, | 
				
			||||
    ResourceFileLink, | 
				
			||||
    DocumentsFilterForm, | 
				
			||||
    DataFilter | 
				
			||||
  }, | 
				
			||||
  mixins: [ListMixin], | 
				
			||||
  setup() { | 
				
			||||
    const store = useStore(); | 
				
			||||
    const filters = ref([]); | 
				
			||||
    const filtersSent = ref([]); | 
				
			||||
    const user = store.getters["security/getUser"]; | 
				
			||||
    const tags = ref([]); | 
				
			||||
    const title = ref('Inbox'); | 
				
			||||
 | 
				
			||||
    filtersSent.value = { | 
				
			||||
      msgType: 2, | 
				
			||||
      userSender: user.id | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    // inbox | 
				
			||||
    filters.value = { | 
				
			||||
      msgType: 1, | 
				
			||||
      userReceiver: user.id | 
				
			||||
    }; | 
				
			||||
 | 
				
			||||
    // Get user tags. | 
				
			||||
    axios.get(ENTRYPOINT + 'message_tags', { | 
				
			||||
      params: { | 
				
			||||
        user: user['@id'] | 
				
			||||
      } | 
				
			||||
    }).then(response => { | 
				
			||||
      let data = response.data; | 
				
			||||
      tags.value = data['hydra:member']; | 
				
			||||
    }); | 
				
			||||
 | 
				
			||||
    function goToInbox() { | 
				
			||||
      title.value = 'Inbox'; | 
				
			||||
      filters.value = { | 
				
			||||
        msgType: 1, | 
				
			||||
        userReceiver: user.id, | 
				
			||||
      }; | 
				
			||||
      store.dispatch('message/resetList'); | 
				
			||||
      store.dispatch('message/fetchAll', filters.value); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    function goToUnread() { | 
				
			||||
      title.value = 'Unread'; | 
				
			||||
      filters.value = { | 
				
			||||
        msgType: 1, | 
				
			||||
        userReceiver: user.id, | 
				
			||||
        read: false | 
				
			||||
      }; | 
				
			||||
      store.dispatch('message/resetList'); | 
				
			||||
      store.dispatch('message/fetchAll', filters.value); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    function goToSent() { | 
				
			||||
      title.value = 'Sent'; | 
				
			||||
      filters.value = { | 
				
			||||
        msgType: 2, | 
				
			||||
        userSender: user.id | 
				
			||||
      }; | 
				
			||||
      store.dispatch('message/resetList'); | 
				
			||||
      store.dispatch('message/fetchAll', filters.value); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    function goToTag(tag) { | 
				
			||||
      title.value = tag.tag; | 
				
			||||
      filters.value = { | 
				
			||||
        msgType: 1, | 
				
			||||
        userReceiver: user.id, | 
				
			||||
        tags: [tag] | 
				
			||||
      }; | 
				
			||||
      store.dispatch('message/resetList'); | 
				
			||||
      store.dispatch('message/fetchAll', filters.value); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    return { | 
				
			||||
      goToInbox, | 
				
			||||
      goToSent, | 
				
			||||
      goToTag, | 
				
			||||
      goToUnread, | 
				
			||||
      tags, | 
				
			||||
      filters, | 
				
			||||
      title, | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  data() { | 
				
			||||
    return { | 
				
			||||
      columns: [ | 
				
			||||
        { label: this.$i18n.t('Title'), field: 'title', name: 'title', sortable: true}, | 
				
			||||
        { label: this.$i18n.t('Sender'), field: 'userSender', name: 'userSender', sortable: true}, | 
				
			||||
        { label: this.$i18n.t('Modified'), field: 'sendDate', name: 'updatedAt', sortable: true}, | 
				
			||||
        { label: this.$i18n.t('Actions'), name: 'action', sortable: false} | 
				
			||||
      ], | 
				
			||||
      pageOptions: [10, 20, 50, this.$i18n.t('All')], | 
				
			||||
      selected: [], | 
				
			||||
      isBusy: false, | 
				
			||||
      options: { | 
				
			||||
        sortBy: 'sendDate', | 
				
			||||
        sortDesc: 'asc', | 
				
			||||
      }, | 
				
			||||
      selectedItems: [], | 
				
			||||
      // prime vue | 
				
			||||
      itemDialog: false, | 
				
			||||
      deleteItemDialog: false, | 
				
			||||
      deleteMultipleDialog: false, | 
				
			||||
      item: {}, | 
				
			||||
      submitted: false, | 
				
			||||
    }; | 
				
			||||
  }, | 
				
			||||
  mounted() { | 
				
			||||
    this.onUpdateOptions(this.options); | 
				
			||||
  }, | 
				
			||||
  computed: { | 
				
			||||
    // From crud.js list function | 
				
			||||
    ...mapGetters('resourcenode', { | 
				
			||||
      resourceNode: 'getResourceNode' | 
				
			||||
    }), | 
				
			||||
    ...mapGetters({ | 
				
			||||
      'isAuthenticated': 'security/isAuthenticated', | 
				
			||||
      'isAdmin': 'security/isAdmin', | 
				
			||||
      'currentUser': 'security/getUser', | 
				
			||||
    }), | 
				
			||||
 | 
				
			||||
    ...mapGetters('message', { | 
				
			||||
      items: 'list', | 
				
			||||
    }), | 
				
			||||
 | 
				
			||||
    //...getters | 
				
			||||
 | 
				
			||||
    // From ListMixin | 
				
			||||
    ...mapFields('message', { | 
				
			||||
      deletedItem: 'deleted', | 
				
			||||
      error: 'error', | 
				
			||||
      isLoading: 'isLoading', | 
				
			||||
      resetList: 'resetList', | 
				
			||||
      totalItems: 'totalItems', | 
				
			||||
      view: 'view' | 
				
			||||
    }), | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    composeHandler() { | 
				
			||||
      let folderParams = this.$route.query; | 
				
			||||
      this.$router.push({ name: `${this.$options.servicePrefix}Create` , query: folderParams}); | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    // prime | 
				
			||||
    onPage(event) { | 
				
			||||
      this.options.itemsPerPage = event.rows; | 
				
			||||
      this.options.page = event.page + 1; | 
				
			||||
      this.options.sortBy = event.sortField; | 
				
			||||
      this.options.sortDesc = event.sortOrder === -1; | 
				
			||||
 | 
				
			||||
      this.onUpdateOptions(this.options); | 
				
			||||
    }, | 
				
			||||
    sortingChanged(event) { | 
				
			||||
      console.log('sortingChanged'); | 
				
			||||
      console.log(event); | 
				
			||||
      this.options.sortBy = event.sortField; | 
				
			||||
      this.options.sortDesc = event.sortOrder === -1; | 
				
			||||
 | 
				
			||||
      this.onUpdateOptions(this.options); | 
				
			||||
      // ctx.sortBy   ==> Field key for sorting by (or null for no sorting) | 
				
			||||
      // ctx.sortDesc ==> true if sorting descending, false otherwise | 
				
			||||
    }, | 
				
			||||
    openNew() { | 
				
			||||
      this.item = {}; | 
				
			||||
      this.submitted = false; | 
				
			||||
      this.itemDialog = true; | 
				
			||||
    }, | 
				
			||||
    hideDialog() { | 
				
			||||
      this.itemDialog = false; | 
				
			||||
      this.submitted = false; | 
				
			||||
    }, | 
				
			||||
    saveItem() { | 
				
			||||
      this.submitted = true; | 
				
			||||
 | 
				
			||||
      if (this.item.title.trim()) { | 
				
			||||
        if (this.item.id) { | 
				
			||||
        } else { | 
				
			||||
          //this.products.push(this.product); | 
				
			||||
          this.item.filetype = 'folder'; | 
				
			||||
          this.item.parentResourceNodeId = this.$route.params.node; | 
				
			||||
          this.item.resourceLinkList = JSON.stringify([{ | 
				
			||||
            gid: this.$route.query.gid, | 
				
			||||
            sid: this.$route.query.sid, | 
				
			||||
            cid: this.$route.query.cid, | 
				
			||||
            visibility: 2, // visible by default | 
				
			||||
          }]); | 
				
			||||
 | 
				
			||||
          this.create(this.item); | 
				
			||||
          this.showMessage('Saved'); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        this.itemDialog = false; | 
				
			||||
        this.item = {}; | 
				
			||||
      } | 
				
			||||
    }, | 
				
			||||
    editItem(item) { | 
				
			||||
      this.item = {...item}; | 
				
			||||
      this.itemDialog = true; | 
				
			||||
    }, | 
				
			||||
    confirmDeleteItem(item) { | 
				
			||||
      this.item = item; | 
				
			||||
      this.deleteItemDialog = true; | 
				
			||||
    }, | 
				
			||||
    confirmDeleteMultiple() { | 
				
			||||
      this.deleteMultipleDialog = true; | 
				
			||||
    }, | 
				
			||||
    markAsReadMultiple(){ | 
				
			||||
      console.log('markAsReadMultiple'); | 
				
			||||
      this.selectedItems.forEach(message => { | 
				
			||||
        message.read = true; | 
				
			||||
        this.update(message); | 
				
			||||
      }); | 
				
			||||
      this.selectedItems = null; | 
				
			||||
      this.resetList = true; | 
				
			||||
    }, | 
				
			||||
    reloadHandler() { | 
				
			||||
      this.onUpdateOptions(this.options); | 
				
			||||
    }, | 
				
			||||
    markAsUnReadMultiple(){ | 
				
			||||
      console.log('markAsUnReadMultiple'); | 
				
			||||
      this.selectedItems.forEach(message => { | 
				
			||||
        message.read = false; | 
				
			||||
        this.update(message); | 
				
			||||
      }); | 
				
			||||
      this.selectedItems = null; | 
				
			||||
      this.resetList = true; | 
				
			||||
      //this.onUpdateOptions(this.options); | 
				
			||||
    }, | 
				
			||||
    deleteMultipleItems() { | 
				
			||||
      console.log('deleteMultipleItems'); | 
				
			||||
      console.log(this.selectedItems); | 
				
			||||
      this.deleteMultipleAction(this.selectedItems); | 
				
			||||
      this.onRequest({ | 
				
			||||
        pagination: this.pagination, | 
				
			||||
      }); | 
				
			||||
      this.deleteMultipleDialog = false; | 
				
			||||
      this.selectedItems = null; | 
				
			||||
      //this.onUpdateOptions(this.options); | 
				
			||||
    }, | 
				
			||||
    deleteItemButton() { | 
				
			||||
      console.log('deleteItem'); | 
				
			||||
      this.deleteItem(this.item); | 
				
			||||
      //this.items = this.items.filter(val => val.iid !== this.item.iid); | 
				
			||||
      this.deleteItemDialog = false; | 
				
			||||
      this.item = {}; | 
				
			||||
      this.onUpdateOptions(this.options); | 
				
			||||
    }, | 
				
			||||
    onRowSelected(items) { | 
				
			||||
      this.selected = items | 
				
			||||
    }, | 
				
			||||
    selectAllRows() { | 
				
			||||
      this.$refs.selectableTable.selectAllRows() | 
				
			||||
    }, | 
				
			||||
    clearSelected() { | 
				
			||||
      this.$refs.selectableTable.clearSelected() | 
				
			||||
    }, | 
				
			||||
    async deleteSelected() { | 
				
			||||
      console.log('deleteSelected'); | 
				
			||||
      /*for (let i = 0; i < this.selected.length; i++) { | 
				
			||||
        let item = this.selected[i]; | 
				
			||||
        //this.deleteHandler(item); | 
				
			||||
        this.deleteItem(item); | 
				
			||||
      }*/ | 
				
			||||
 | 
				
			||||
      this.deleteMultipleAction(this.selected); | 
				
			||||
      this.onRequest({ | 
				
			||||
        pagination: this.pagination, | 
				
			||||
      }); | 
				
			||||
    }, | 
				
			||||
    //...actions, | 
				
			||||
    // From ListMixin | 
				
			||||
    ...mapActions('message', { | 
				
			||||
      getPage: 'fetchAll', | 
				
			||||
      create: 'create', | 
				
			||||
      update: 'update', | 
				
			||||
      deleteItem: 'del', | 
				
			||||
      deleteMultipleAction: 'delMultiple' | 
				
			||||
    }), | 
				
			||||
    ...mapActions('resourcenode', { | 
				
			||||
      findResourceNode: 'findResourceNode', | 
				
			||||
    }), | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,242 @@ | 
				
			||||
<template> | 
				
			||||
  <div v-if="item"> | 
				
			||||
    <Toolbar | 
				
			||||
      :handle-delete="del" | 
				
			||||
    > | 
				
			||||
      <template v-slot:right> | 
				
			||||
<!--        <v-toolbar-title v-if="item">--> | 
				
			||||
<!--          {{--> | 
				
			||||
<!--            `${$options.servicePrefix} ${item['@id']}`--> | 
				
			||||
<!--          }}--> | 
				
			||||
<!--        </v-toolbar-title>--> | 
				
			||||
 | 
				
			||||
        <v-btn | 
				
			||||
            :loading="isLoading" | 
				
			||||
            tile | 
				
			||||
            icon | 
				
			||||
            @click="reply" | 
				
			||||
        > | 
				
			||||
          <v-icon icon="mdi-reply" /> | 
				
			||||
        </v-btn> | 
				
			||||
      </template> | 
				
			||||
    </Toolbar> | 
				
			||||
 | 
				
			||||
    <VueMultiselect | 
				
			||||
        placeholder="Tags" | 
				
			||||
        v-model="item.tags" | 
				
			||||
        :loading="isLoadingSelect" | 
				
			||||
        tag-placeholder="Add this as new tag" | 
				
			||||
        :options="tags" | 
				
			||||
        :multiple="true" | 
				
			||||
        :searchable="true" | 
				
			||||
        :internal-search="false" | 
				
			||||
        @search-change="asyncFind" | 
				
			||||
 | 
				
			||||
        @select="addTagToMessage" | 
				
			||||
        @remove="removeTagFromMessage" | 
				
			||||
 | 
				
			||||
        :taggable="true" | 
				
			||||
        @tag="addTag" | 
				
			||||
        label="tag" | 
				
			||||
        track-by="id" | 
				
			||||
    /> | 
				
			||||
 | 
				
			||||
    <p class="text-lg"> | 
				
			||||
      From: | 
				
			||||
      <q-avatar size="32px"> | 
				
			||||
        <img :src="item['userSender']['illustrationUrl'] + '?w=80&h=80&fit=crop'" /> | 
				
			||||
        <!--              <q-icon name="person" ></q-icon>--> | 
				
			||||
      </q-avatar> | 
				
			||||
      {{ item['userSender']['username'] }} | 
				
			||||
    </p> | 
				
			||||
 | 
				
			||||
    <p class="text-lg"> | 
				
			||||
      {{ $luxonDateTime.fromISO(item['sendDate']).toRelative() }} | 
				
			||||
    </p> | 
				
			||||
 | 
				
			||||
    <p class="text-lg"> | 
				
			||||
      <h3>{{ item.title }}</h3> | 
				
			||||
    </p> | 
				
			||||
 | 
				
			||||
    <div class="flex flex-row"> | 
				
			||||
      <div class="w-full"> | 
				
			||||
        <p v-html="item.content" /> | 
				
			||||
      </div> | 
				
			||||
    </div> | 
				
			||||
    <Loading :visible="isLoading" /> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<style src="vue-multiselect/dist/vue-multiselect.css"></style> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import {mapActions, mapGetters, useStore} from 'vuex'; | 
				
			||||
import { mapFields } from 'vuex-map-fields'; | 
				
			||||
import Loading from '../../components/Loading.vue'; | 
				
			||||
import ShowMixin from '../../mixins/ShowMixin'; | 
				
			||||
import Toolbar from '../../components/Toolbar.vue'; | 
				
			||||
import VueMultiselect from 'vue-multiselect' | 
				
			||||
import {computed, ref} from "vue"; | 
				
			||||
import isEmpty from "lodash/isEmpty"; | 
				
			||||
import axios from "axios"; | 
				
			||||
import {ENTRYPOINT} from "../../config/entrypoint"; | 
				
			||||
import useVuelidate from "@vuelidate/core"; | 
				
			||||
import {useRoute, useRouter} from "vue-router"; | 
				
			||||
import NotificationMixin from "../../mixins/NotificationMixin"; | 
				
			||||
 | 
				
			||||
const servicePrefix = 'usergroups'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'UserGroupShow', | 
				
			||||
  components: { | 
				
			||||
      Loading, | 
				
			||||
      Toolbar, | 
				
			||||
      VueMultiselect | 
				
			||||
  }, | 
				
			||||
  setup () { | 
				
			||||
    const tags = ref([]); | 
				
			||||
    const isLoadingSelect = ref(false); | 
				
			||||
    const store = useStore(); | 
				
			||||
    const user = store.getters["security/getUser"]; | 
				
			||||
    const find = store.getters["message/find"]; | 
				
			||||
    const route = useRoute(); | 
				
			||||
    const router = useRouter(); | 
				
			||||
 | 
				
			||||
    let id = route.params.id; | 
				
			||||
    if (isEmpty(id)) { | 
				
			||||
      id = route.query.id; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    console.log(id); | 
				
			||||
    console.log(decodeURIComponent(id)); | 
				
			||||
 | 
				
			||||
    let item = find(decodeURIComponent(id)); | 
				
			||||
 | 
				
			||||
    // Change to read | 
				
			||||
    if (false === item.read) { | 
				
			||||
      axios.put(ENTRYPOINT + 'messages/' + item.id, { | 
				
			||||
        read: true, | 
				
			||||
      }).then(response => { | 
				
			||||
        console.log(response); | 
				
			||||
      }).catch(function (error) { | 
				
			||||
        console.log(error); | 
				
			||||
      }); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    function addTag(newTag) { | 
				
			||||
      axios.post(ENTRYPOINT + 'message_tags', { | 
				
			||||
        user: user['@id'], | 
				
			||||
        tag: newTag, | 
				
			||||
      }).then(response => { | 
				
			||||
        addTagToMessage(response.data); | 
				
			||||
        //this.showMessage('Added'); | 
				
			||||
        item.tags.push(response.data); | 
				
			||||
        console.log(response); | 
				
			||||
        isLoadingSelect.value = false; | 
				
			||||
      }).catch(function (error) { | 
				
			||||
        isLoadingSelect.value = false; | 
				
			||||
        console.log(error); | 
				
			||||
      }); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    function addTagToMessage(newTag) { | 
				
			||||
      console.log('addTagToMessage'); | 
				
			||||
      let tagsToUpdate = []; | 
				
			||||
      item.tags.forEach(tagItem => { | 
				
			||||
        tagsToUpdate.push(tagItem['@id']); | 
				
			||||
      }); | 
				
			||||
      tagsToUpdate.push(newTag['@id']); | 
				
			||||
      console.log(tagsToUpdate); | 
				
			||||
 | 
				
			||||
      axios.put(ENTRYPOINT + 'messages/' + item.id, { | 
				
			||||
        tags: tagsToUpdate, | 
				
			||||
      }).then(response => { | 
				
			||||
        //this.showMessage('Added'); | 
				
			||||
        console.log(response); | 
				
			||||
        isLoadingSelect.value = false; | 
				
			||||
      }).catch(function (error) { | 
				
			||||
        isLoadingSelect.value = false; | 
				
			||||
        console.log(error); | 
				
			||||
      }); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    function removeTagFromMessage() { | 
				
			||||
      let tagsToUpdate = []; | 
				
			||||
      item.tags.forEach(tagItem => { | 
				
			||||
        tagsToUpdate.push(tagItem['@id']); | 
				
			||||
      }); | 
				
			||||
 | 
				
			||||
      axios.put(ENTRYPOINT + 'messages/' + item.id, { | 
				
			||||
        tags: tagsToUpdate, | 
				
			||||
      }).then(response => { | 
				
			||||
        console.log(response); | 
				
			||||
        isLoadingSelect.value = false; | 
				
			||||
      }).catch(function (error) { | 
				
			||||
        isLoadingSelect.value = false; | 
				
			||||
        console.log(error); | 
				
			||||
      }); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    axios.get(ENTRYPOINT + 'message_tags', { | 
				
			||||
      params: { | 
				
			||||
        user: user['@id'] | 
				
			||||
      } | 
				
			||||
    }).then(response => { | 
				
			||||
      isLoadingSelect.value = false; | 
				
			||||
      let data = response.data; | 
				
			||||
      tags.value = data['hydra:member']; | 
				
			||||
    }); | 
				
			||||
 | 
				
			||||
    function reply() { | 
				
			||||
      let params = route.query; | 
				
			||||
      router.push({name: `${servicePrefix}Reply`, query: params}); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    function asyncFind (query) { | 
				
			||||
      if (query.toString().length < 3) { | 
				
			||||
        return; | 
				
			||||
      } | 
				
			||||
 | 
				
			||||
      isLoadingSelect.value = true; | 
				
			||||
      axios.get(ENTRYPOINT + 'message_tags', { | 
				
			||||
        params: { | 
				
			||||
          user: user['@id'] | 
				
			||||
        } | 
				
			||||
      }).then(response => { | 
				
			||||
        isLoadingSelect.value = false; | 
				
			||||
        let data = response.data; | 
				
			||||
        tags.value = data['hydra:member']; | 
				
			||||
 | 
				
			||||
      }).catch(function (error) { | 
				
			||||
        isLoadingSelect.value = false; | 
				
			||||
        console.log(error); | 
				
			||||
      }); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    return { | 
				
			||||
      v$: useVuelidate(), tags, isLoadingSelect, item, | 
				
			||||
      addTag, addTagToMessage, removeTagFromMessage, asyncFind, reply | 
				
			||||
    }; | 
				
			||||
  }, | 
				
			||||
  mixins: [ShowMixin, NotificationMixin], | 
				
			||||
  computed: { | 
				
			||||
    ...mapFields('message', { | 
				
			||||
      isLoading: 'isLoading' | 
				
			||||
    }), | 
				
			||||
    ...mapGetters('message', ['find']), | 
				
			||||
    ...mapGetters({ | 
				
			||||
      'isAuthenticated': 'security/isAuthenticated', | 
				
			||||
      'isAdmin': 'security/isAdmin', | 
				
			||||
      'currentUser': 'security/getUser', | 
				
			||||
    }), | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    ...mapActions('message', { | 
				
			||||
      deleteItem: 'del', | 
				
			||||
      reset: 'resetShow', | 
				
			||||
      retrieve: 'loadWithQuery' | 
				
			||||
    }), | 
				
			||||
  }, | 
				
			||||
  servicePrefix | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,190 @@ | 
				
			||||
<template> | 
				
			||||
  <Toolbar  > | 
				
			||||
    <template v-slot:right> | 
				
			||||
      <v-btn | 
				
			||||
          tile | 
				
			||||
          icon | 
				
			||||
          :loading="isLoading" | 
				
			||||
          @click="reloadHandler"> | 
				
			||||
        <v-icon icon="mdi-account-plus-outline" /> | 
				
			||||
      </v-btn> | 
				
			||||
    </template> | 
				
			||||
  </Toolbar> | 
				
			||||
 | 
				
			||||
  <div class="flex flex-row pt-2"> | 
				
			||||
    <div class="w-full"> | 
				
			||||
      <div class="text-h4 q-mb-md">Search</div> | 
				
			||||
 | 
				
			||||
      <VueMultiselect | 
				
			||||
          placeholder="Add" | 
				
			||||
 | 
				
			||||
          :loading="isLoadingSelect" | 
				
			||||
          :options="users" | 
				
			||||
          :multiple="true" | 
				
			||||
          :searchable="true" | 
				
			||||
          :internal-search="false" | 
				
			||||
          @search-change="asyncFind" | 
				
			||||
 | 
				
			||||
          @select="addFriend" | 
				
			||||
          limit-text="3" | 
				
			||||
          limit="3" | 
				
			||||
          label="username" | 
				
			||||
          track-by="id" | 
				
			||||
      /> | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    </div> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<style src="vue-multiselect/dist/vue-multiselect.css"></style> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import { mapActions, mapGetters } from 'vuex'; | 
				
			||||
import { mapFields } from 'vuex-map-fields'; | 
				
			||||
import ListMixin from '../../mixins/ListMixin'; | 
				
			||||
import Toolbar from '../../components/Toolbar.vue'; | 
				
			||||
 | 
				
			||||
import VueMultiselect from 'vue-multiselect' | 
				
			||||
import { ref, reactive, onMounted, computed } from 'vue'; | 
				
			||||
import { useStore } from 'vuex'; | 
				
			||||
import axios from "axios"; | 
				
			||||
import {ENTRYPOINT} from "../../config/entrypoint"; | 
				
			||||
import useVuelidate from "@vuelidate/core"; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'UserRelUserAdd', | 
				
			||||
  servicePrefix: 'userreluser', | 
				
			||||
  components: { | 
				
			||||
    Toolbar, | 
				
			||||
    VueMultiselect | 
				
			||||
  }, | 
				
			||||
  mixins: [ListMixin], | 
				
			||||
  setup() { | 
				
			||||
    const users = ref([]); | 
				
			||||
    const isLoadingSelect = ref(false); | 
				
			||||
    const store = useStore(); | 
				
			||||
    const user = store.getters["security/getUser"]; | 
				
			||||
 | 
				
			||||
    function asyncFind (query) { | 
				
			||||
      if (query.toString().length < 3) { | 
				
			||||
        return; | 
				
			||||
      } | 
				
			||||
 | 
				
			||||
      isLoadingSelect.value = true; | 
				
			||||
      axios.get(ENTRYPOINT + 'users', { | 
				
			||||
        params: { | 
				
			||||
          username: query | 
				
			||||
        } | 
				
			||||
      }).then(response => { | 
				
			||||
        isLoadingSelect.value = false; | 
				
			||||
        let data = response.data; | 
				
			||||
        users.value = data['hydra:member']; | 
				
			||||
      }).catch(function (error) { | 
				
			||||
        isLoadingSelect.value = false; | 
				
			||||
        console.log(error); | 
				
			||||
      }); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    function addFriend(friend) { | 
				
			||||
      axios.post(ENTRYPOINT + 'user_rel_users', { | 
				
			||||
        user: user['@id'], | 
				
			||||
        friend: friend['@id'], | 
				
			||||
        relationType: 10, | 
				
			||||
      }).then(response => { | 
				
			||||
        console.log(response); | 
				
			||||
        isLoadingSelect.value = false; | 
				
			||||
      }).catch(function (error) { | 
				
			||||
        isLoadingSelect.value = false; | 
				
			||||
        console.log(error); | 
				
			||||
      }); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    return {v$: useVuelidate(), users, asyncFind, addFriend, isLoadingSelect}; | 
				
			||||
  }, | 
				
			||||
  data() { | 
				
			||||
    return { | 
				
			||||
      selected: [], | 
				
			||||
      isBusy: false, | 
				
			||||
      options: { | 
				
			||||
        sortBy: 'sendDate', | 
				
			||||
        sortDesc: 'asc', | 
				
			||||
      }, | 
				
			||||
      selectedItems: [], | 
				
			||||
      // prime vue | 
				
			||||
      itemDialog: false, | 
				
			||||
      deleteItemDialog: false, | 
				
			||||
      deleteMultipleDialog: false, | 
				
			||||
      item: {}, | 
				
			||||
      submitted: false, | 
				
			||||
    }; | 
				
			||||
  }, | 
				
			||||
  mounted() { | 
				
			||||
    this.onUpdateOptions(this.options); | 
				
			||||
  }, | 
				
			||||
  computed: { | 
				
			||||
    // From crud.js list function | 
				
			||||
    ...mapGetters('resourcenode', { | 
				
			||||
      resourceNode: 'getResourceNode' | 
				
			||||
    }), | 
				
			||||
    ...mapGetters({ | 
				
			||||
      'isAuthenticated': 'security/isAuthenticated', | 
				
			||||
      'isAdmin': 'security/isAdmin', | 
				
			||||
      'currentUser': 'security/getUser', | 
				
			||||
    }), | 
				
			||||
 | 
				
			||||
    ...mapGetters('message', { | 
				
			||||
      items: 'list', | 
				
			||||
    }), | 
				
			||||
 | 
				
			||||
    //...getters | 
				
			||||
 | 
				
			||||
    // From ListMixin | 
				
			||||
    ...mapFields('message', { | 
				
			||||
      deletedItem: 'deleted', | 
				
			||||
      error: 'error', | 
				
			||||
      isLoading: 'isLoading', | 
				
			||||
      resetList: 'resetList', | 
				
			||||
      totalItems: 'totalItems', | 
				
			||||
      view: 'view' | 
				
			||||
    }), | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    deleteItemButton() { | 
				
			||||
      console.log('deleteItem'); | 
				
			||||
      this.deleteItem(this.item); | 
				
			||||
      //this.items = this.items.filter(val => val.iid !== this.item.iid); | 
				
			||||
      this.deleteItemDialog = false; | 
				
			||||
      this.item = {}; | 
				
			||||
      this.onUpdateOptions(this.options); | 
				
			||||
    }, | 
				
			||||
    onRowSelected(items) { | 
				
			||||
      this.selected = items | 
				
			||||
    }, | 
				
			||||
    selectAllRows() { | 
				
			||||
      this.$refs.selectableTable.selectAllRows() | 
				
			||||
    }, | 
				
			||||
    clearSelected() { | 
				
			||||
      this.$refs.selectableTable.clearSelected() | 
				
			||||
    }, | 
				
			||||
    async deleteSelected() { | 
				
			||||
      this.deleteMultipleAction(this.selected); | 
				
			||||
      this.onRequest({ | 
				
			||||
        pagination: this.pagination, | 
				
			||||
      }); | 
				
			||||
    }, | 
				
			||||
    //...actions, | 
				
			||||
    // From ListMixin | 
				
			||||
    ...mapActions('userreluser', { | 
				
			||||
      getPage: 'fetchAll', | 
				
			||||
      create: 'create', | 
				
			||||
      update: 'update', | 
				
			||||
      deleteItem: 'del', | 
				
			||||
      deleteMultipleAction: 'delMultiple' | 
				
			||||
    }), | 
				
			||||
    ...mapActions('resourcenode', { | 
				
			||||
      findResourceNode: 'findResourceNode', | 
				
			||||
    }), | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,381 @@ | 
				
			||||
<template> | 
				
			||||
  <Toolbar  > | 
				
			||||
    <template v-slot:right> | 
				
			||||
 | 
				
			||||
      <v-btn | 
				
			||||
          tile | 
				
			||||
          icon | 
				
			||||
          :loading="isLoading" | 
				
			||||
          :to="'/resources/friends/add'" | 
				
			||||
      > | 
				
			||||
        <v-icon icon="mdi-account-plus-outline" /> | 
				
			||||
      </v-btn> | 
				
			||||
 | 
				
			||||
        <v-btn | 
				
			||||
            tile | 
				
			||||
            icon | 
				
			||||
            :loading="isLoading" | 
				
			||||
            @click="reloadHandler" | 
				
			||||
        > | 
				
			||||
          <v-icon icon="mdi-refresh" /> | 
				
			||||
        </v-btn> | 
				
			||||
 | 
				
			||||
         <v-btn | 
				
			||||
            tile | 
				
			||||
            icon | 
				
			||||
            @click="confirmDeleteMultiple" | 
				
			||||
            :class="[ !selectedItems || !selectedItems.length ? 'hidden': '']" | 
				
			||||
         > | 
				
			||||
          <v-icon icon="mdi-delete" /> | 
				
			||||
        </v-btn> | 
				
			||||
<!--        :disabled="!selectedItems || !selectedItems.length"--> | 
				
			||||
 | 
				
			||||
    </template> | 
				
			||||
  </Toolbar> | 
				
			||||
 | 
				
			||||
  <div class="flex flex-row pt-2"> | 
				
			||||
    <div class="w-full"> | 
				
			||||
      <div class="text-h4 q-mb-md">Friends</div> | 
				
			||||
      <DataTable | 
				
			||||
        class="p-datatable-sm" | 
				
			||||
        :value="items" | 
				
			||||
        v-model:selection="selectedItems" | 
				
			||||
        dataKey="id" | 
				
			||||
        v-model:filters="filters" | 
				
			||||
        filterDisplay="menu" | 
				
			||||
        sortBy="sendDate" | 
				
			||||
        sortOrder="asc" | 
				
			||||
        :lazy="true" | 
				
			||||
        :paginator="false" | 
				
			||||
        :totalRecords="totalItems" | 
				
			||||
        :loading="isLoading" | 
				
			||||
        @page="onPage($event)" | 
				
			||||
        @sort="sortingChanged($event)" | 
				
			||||
        paginatorTemplate="CurrentPageReport FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown" | 
				
			||||
        :rowsPerPageOptions="[5, 10, 20, 50]" | 
				
			||||
        responsiveLayout="scroll" | 
				
			||||
        currentPageReportTemplate="Showing {first} to {last} of {totalRecords}" | 
				
			||||
      > | 
				
			||||
 | 
				
			||||
      <Column selectionMode="multiple" style="width: 3rem" :exportable="false"></Column> | 
				
			||||
 | 
				
			||||
      <Column field="userSender" :header="$t('User')" :sortable="false"> | 
				
			||||
        <template #body="slotProps"> | 
				
			||||
          <q-avatar size="40px"> | 
				
			||||
            <img :src="slotProps.data.friend.illustrationUrl + '?w=80&h=80&fit=crop'" /> | 
				
			||||
         </q-avatar> | 
				
			||||
            {{ slotProps.data.friend.username }} | 
				
			||||
        </template> | 
				
			||||
      </Column> | 
				
			||||
 | 
				
			||||
      <Column field="createdAt" :header="$t('Sent date')" :sortable="true"> | 
				
			||||
        <template #body="slotProps"> | 
				
			||||
          {{$luxonDateTime.fromISO(slotProps.data.createdAt).toRelative() }} | 
				
			||||
        </template> | 
				
			||||
      </Column> | 
				
			||||
 | 
				
			||||
      <Column :exportable="false"> | 
				
			||||
 | 
				
			||||
        <template #body="slotProps"> | 
				
			||||
<!--          class="flex flex-row gap-2"--> | 
				
			||||
 | 
				
			||||
              <v-icon   v-if="slotProps.data.relationType == 3" icon="mdi-check" /> | 
				
			||||
 | 
				
			||||
 | 
				
			||||
            <v-btn | 
				
			||||
                v-if="slotProps.data.relationType == 10" | 
				
			||||
                tile | 
				
			||||
                icon | 
				
			||||
                @click="addFriend(slotProps.data)" > | 
				
			||||
              <v-icon icon="mdi-plus" /> | 
				
			||||
            </v-btn> | 
				
			||||
 | 
				
			||||
            <v-btn | 
				
			||||
                tile | 
				
			||||
                icon | 
				
			||||
                @click="confirmDeleteItem(slotProps.data)" > | 
				
			||||
              <v-icon icon="mdi-delete" /> | 
				
			||||
            </v-btn> | 
				
			||||
 | 
				
			||||
        </template> | 
				
			||||
 | 
				
			||||
      </Column> | 
				
			||||
    </DataTable> | 
				
			||||
    </div> | 
				
			||||
  </div> | 
				
			||||
 | 
				
			||||
  <Dialog v-model:visible="itemDialog" :style="{width: '450px'}" :header="$t('New folder')" :modal="true" class="p-fluid"> | 
				
			||||
    <div class="p-field"> | 
				
			||||
      <label for="name">{{ $t('Name') }}</label> | 
				
			||||
      <InputText | 
				
			||||
          autocomplete="off" | 
				
			||||
          id="title" | 
				
			||||
          v-model.trim="item.title" | 
				
			||||
          required="true" | 
				
			||||
          autofocus | 
				
			||||
          :class="{'p-invalid': submitted && !item.title}" | 
				
			||||
      /> | 
				
			||||
      <small class="p-error" v-if="submitted && !item.title">$t('Title is required')</small> | 
				
			||||
    </div> | 
				
			||||
 | 
				
			||||
    <template #footer> | 
				
			||||
      <Button label="Cancel" icon="pi pi-times" class="p-button-text" @click="hideDialog"/> | 
				
			||||
      <Button label="Save" icon="pi pi-check" class="p-button-text" @click="saveItem" /> | 
				
			||||
    </template> | 
				
			||||
  </Dialog> | 
				
			||||
 | 
				
			||||
  <Dialog v-model:visible="deleteItemDialog" :style="{width: '450px'}" header="Confirm" :modal="true"> | 
				
			||||
    <div class="confirmation-content"> | 
				
			||||
      <i class="pi pi-exclamation-triangle p-mr-3" style="font-size: 2rem" /> | 
				
			||||
      <span v-if="item">Are you sure you want to delete <b>{{item.title}}</b>?</span> | 
				
			||||
    </div> | 
				
			||||
    <template #footer> | 
				
			||||
      <Button label="No" icon="pi pi-times" class="p-button-text" @click="deleteItemDialog = false"/> | 
				
			||||
      <Button label="Yes" icon="pi pi-check" class="p-button-text" @click="deleteItemButton" /> | 
				
			||||
    </template> | 
				
			||||
  </Dialog> | 
				
			||||
 | 
				
			||||
  <Dialog v-model:visible="deleteMultipleDialog" :style="{width: '450px'}" header="Confirm" :modal="true"> | 
				
			||||
    <div class="confirmation-content"> | 
				
			||||
      <i class="pi pi-exclamation-triangle p-mr-3" style="font-size: 2rem" /> | 
				
			||||
      <span v-if="item">Are you sure you want to delete the selected items?</span> | 
				
			||||
    </div> | 
				
			||||
    <template #footer> | 
				
			||||
      <Button label="No" icon="pi pi-times" class="p-button-text" @click="deleteMultipleDialog = false"/> | 
				
			||||
      <Button label="Yes" icon="pi pi-check" class="p-button-text" @click="deleteMultipleItems" /> | 
				
			||||
    </template> | 
				
			||||
  </Dialog> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import { mapActions, mapGetters } from 'vuex'; | 
				
			||||
import { mapFields } from 'vuex-map-fields'; | 
				
			||||
import ListMixin from '../../mixins/ListMixin'; | 
				
			||||
import ActionCell from '../../components/ActionCell.vue'; | 
				
			||||
import Toolbar from '../../components/Toolbar.vue'; | 
				
			||||
import ResourceFileIcon from '../../components/documents/ResourceFileIcon.vue'; | 
				
			||||
import ResourceFileLink from '../../components/documents/ResourceFileLink.vue'; | 
				
			||||
 | 
				
			||||
import DataFilter from '../../components/DataFilter'; | 
				
			||||
import DocumentsFilterForm from '../../components/documents/Filter'; | 
				
			||||
import { ref, reactive, onMounted, computed } from 'vue'; | 
				
			||||
import { useStore } from 'vuex'; | 
				
			||||
import axios from "axios"; | 
				
			||||
import {ENTRYPOINT} from "../../config/entrypoint"; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'UserRelUserList', | 
				
			||||
  servicePrefix: 'userreluser', | 
				
			||||
  components: { | 
				
			||||
    Toolbar, | 
				
			||||
  }, | 
				
			||||
  mixins: [ListMixin], | 
				
			||||
  setup() { | 
				
			||||
    const store = useStore(); | 
				
			||||
    const user = store.getters["security/getUser"]; | 
				
			||||
 | 
				
			||||
    const isLoadingSelect = ref(false); | 
				
			||||
    function addFriend(friend) { | 
				
			||||
      axios.put(friend['@id'], { | 
				
			||||
        relationType: 3, | 
				
			||||
      }).then(response => { | 
				
			||||
        console.log(response); | 
				
			||||
        isLoadingSelect.value = false; | 
				
			||||
      }).catch(function (error) { | 
				
			||||
        isLoadingSelect.value = false; | 
				
			||||
        console.log(error); | 
				
			||||
      }); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    return { | 
				
			||||
      addFriend, | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  data() { | 
				
			||||
    return { | 
				
			||||
      columns: [ | 
				
			||||
        { label: this.$i18n.t('User'), field: 'friend.username', name: 'friend', sortable: true}, | 
				
			||||
        { label: this.$i18n.t('Sent'), field: 'createdAt', name: 'createdAt', sortable: true}, | 
				
			||||
        { label: this.$i18n.t('Actions'), name: 'action', sortable: false} | 
				
			||||
      ], | 
				
			||||
      pageOptions: [10, 20, 50, this.$i18n.t('All')], | 
				
			||||
      selected: [], | 
				
			||||
      isBusy: false, | 
				
			||||
      selectedItems: [], | 
				
			||||
      // prime vue | 
				
			||||
      itemDialog: false, | 
				
			||||
      deleteItemDialog: false, | 
				
			||||
      deleteMultipleDialog: false, | 
				
			||||
      item: {}, | 
				
			||||
      submitted: false, | 
				
			||||
    }; | 
				
			||||
  }, | 
				
			||||
  mounted() { | 
				
			||||
    console.log('mounted'); | 
				
			||||
    this.filters = { | 
				
			||||
       friend: this.currentUser.id | 
				
			||||
    }; | 
				
			||||
    this.onUpdateOptions(this.options); | 
				
			||||
  }, | 
				
			||||
  computed: { | 
				
			||||
    // From crud.js list function | 
				
			||||
    ...mapGetters('resourcenode', { | 
				
			||||
      resourceNode: 'getResourceNode' | 
				
			||||
    }), | 
				
			||||
    ...mapGetters({ | 
				
			||||
      'isAuthenticated': 'security/isAuthenticated', | 
				
			||||
      'isAdmin': 'security/isAdmin', | 
				
			||||
      'currentUser': 'security/getUser', | 
				
			||||
    }), | 
				
			||||
 | 
				
			||||
    ...mapGetters('userreluser', { | 
				
			||||
      items: 'list', | 
				
			||||
    }), | 
				
			||||
 | 
				
			||||
    //...getters | 
				
			||||
 | 
				
			||||
    // From ListMixin | 
				
			||||
    ...mapFields('userreluser', { | 
				
			||||
      deletedItem: 'deleted', | 
				
			||||
      error: 'error', | 
				
			||||
      isLoading: 'isLoading', | 
				
			||||
      resetList: 'resetList', | 
				
			||||
      totalItems: 'totalItems', | 
				
			||||
      view: 'view' | 
				
			||||
    }), | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    // prime | 
				
			||||
    onPage(event) { | 
				
			||||
      this.options.itemsPerPage = event.rows; | 
				
			||||
      this.options.page = event.page + 1; | 
				
			||||
      this.options.sortBy = event.sortField; | 
				
			||||
      this.options.sortDesc = event.sortOrder === -1; | 
				
			||||
 | 
				
			||||
      this.onUpdateOptions(this.options); | 
				
			||||
    }, | 
				
			||||
    sortingChanged(event) { | 
				
			||||
      console.log('sortingChanged'); | 
				
			||||
      console.log(event); | 
				
			||||
      this.options.sortBy = event.sortField; | 
				
			||||
      this.options.sortDesc = event.sortOrder === -1; | 
				
			||||
 | 
				
			||||
      this.onUpdateOptions(this.options); | 
				
			||||
      // ctx.sortBy   ==> Field key for sorting by (or null for no sorting) | 
				
			||||
      // ctx.sortDesc ==> true if sorting descending, false otherwise | 
				
			||||
    }, | 
				
			||||
    openNew() { | 
				
			||||
      this.item = {}; | 
				
			||||
      this.submitted = false; | 
				
			||||
      this.itemDialog = true; | 
				
			||||
    }, | 
				
			||||
    hideDialog() { | 
				
			||||
      this.itemDialog = false; | 
				
			||||
      this.submitted = false; | 
				
			||||
    }, | 
				
			||||
    saveItem() { | 
				
			||||
      this.submitted = true; | 
				
			||||
 | 
				
			||||
      if (this.item.title.trim()) { | 
				
			||||
        if (this.item.id) { | 
				
			||||
        } else { | 
				
			||||
          //this.products.push(this.product); | 
				
			||||
          this.item.filetype = 'folder'; | 
				
			||||
          this.item.parentResourceNodeId = this.$route.params.node; | 
				
			||||
          this.item.resourceLinkList = JSON.stringify([{ | 
				
			||||
            gid: this.$route.query.gid, | 
				
			||||
            sid: this.$route.query.sid, | 
				
			||||
            cid: this.$route.query.cid, | 
				
			||||
            visibility: 2, // visible by default | 
				
			||||
          }]); | 
				
			||||
 | 
				
			||||
          this.create(this.item); | 
				
			||||
          this.showMessage('Saved'); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        this.itemDialog = false; | 
				
			||||
        this.item = {}; | 
				
			||||
      } | 
				
			||||
    }, | 
				
			||||
    editItem(item) { | 
				
			||||
      this.item = {...item}; | 
				
			||||
      this.itemDialog = true; | 
				
			||||
    }, | 
				
			||||
    confirmDeleteItem(item) { | 
				
			||||
      this.item = item; | 
				
			||||
      this.deleteItemDialog = true; | 
				
			||||
    }, | 
				
			||||
    confirmDeleteMultiple() { | 
				
			||||
      this.deleteMultipleDialog = true; | 
				
			||||
    }, | 
				
			||||
    markAsReadMultiple(){ | 
				
			||||
      console.log('markAsReadMultiple'); | 
				
			||||
      this.selectedItems.forEach(message => { | 
				
			||||
        message.read = true; | 
				
			||||
        this.update(message); | 
				
			||||
      }); | 
				
			||||
      this.selectedItems = null; | 
				
			||||
      this.resetList = true; | 
				
			||||
    }, | 
				
			||||
    reloadHandler() { | 
				
			||||
      this.onUpdateOptions(this.options); | 
				
			||||
    }, | 
				
			||||
    markAsUnReadMultiple(){ | 
				
			||||
      console.log('markAsUnReadMultiple'); | 
				
			||||
      this.selectedItems.forEach(message => { | 
				
			||||
        message.read = false; | 
				
			||||
        this.update(message); | 
				
			||||
      }); | 
				
			||||
      this.selectedItems = null; | 
				
			||||
      this.resetList = true; | 
				
			||||
      //this.onUpdateOptions(this.options); | 
				
			||||
    }, | 
				
			||||
    deleteMultipleItems() { | 
				
			||||
      console.log('deleteMultipleItems'); | 
				
			||||
      console.log(this.selectedItems); | 
				
			||||
      this.deleteMultipleAction(this.selectedItems); | 
				
			||||
      this.onRequest({ | 
				
			||||
        pagination: this.pagination, | 
				
			||||
      }); | 
				
			||||
      this.deleteMultipleDialog = false; | 
				
			||||
      this.selectedItems = null; | 
				
			||||
      //this.onUpdateOptions(this.options); | 
				
			||||
    }, | 
				
			||||
    deleteItemButton() { | 
				
			||||
      console.log('deleteItem'); | 
				
			||||
      this.deleteItem(this.item); | 
				
			||||
      //this.items = this.items.filter(val => val.iid !== this.item.iid); | 
				
			||||
      this.deleteItemDialog = false; | 
				
			||||
      this.item = {}; | 
				
			||||
      this.onUpdateOptions(this.options); | 
				
			||||
    }, | 
				
			||||
    onRowSelected(items) { | 
				
			||||
      this.selected = items | 
				
			||||
    }, | 
				
			||||
    selectAllRows() { | 
				
			||||
      this.$refs.selectableTable.selectAllRows() | 
				
			||||
    }, | 
				
			||||
    clearSelected() { | 
				
			||||
      this.$refs.selectableTable.clearSelected() | 
				
			||||
    }, | 
				
			||||
    async deleteSelected() { | 
				
			||||
      this.deleteMultipleAction(this.selected); | 
				
			||||
      this.onRequest({ | 
				
			||||
        pagination: this.pagination, | 
				
			||||
      }); | 
				
			||||
    }, | 
				
			||||
    //...actions, | 
				
			||||
    // From ListMixin | 
				
			||||
    ...mapActions('userreluser', { | 
				
			||||
      getPage: 'fetchAll', | 
				
			||||
      create: 'create', | 
				
			||||
      update: 'update', | 
				
			||||
      deleteItem: 'del', | 
				
			||||
      deleteMultipleAction: 'delMultiple' | 
				
			||||
    }), | 
				
			||||
    ...mapActions('resourcenode', { | 
				
			||||
      findResourceNode: 'findResourceNode', | 
				
			||||
    }), | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
					Loading…
					
					
				
		Reference in new issue