Adding permissions per user, add volume number sort

This commit is contained in:
Mark Cooper 2021-09-06 17:42:15 -05:00
parent 1d7d2a1dac
commit ee452d41ee
18 changed files with 241 additions and 43 deletions

View file

@ -30,8 +30,10 @@
<ui-btn small class="text-sm mx-2" @click="toggleSelectAll">{{ isAllSelected ? 'Select None' : 'Select All' }}</ui-btn>
<div class="flex-grow" />
<ui-btn v-show="!processingBatchDelete" color="warning" small class="mx-2" @click="batchEditClick"><span class="material-icons text-gray-200 pt-1">edit</span></ui-btn>
<ui-btn color="error" small class="mx-2" :loading="processingBatchDelete" @click="batchDeleteClick"><span class="material-icons text-gray-200 pt-1">delete</span></ui-btn>
<template v-if="userCanUpdate">
<ui-btn v-show="!processingBatchDelete" color="warning" small class="mx-2" @click="batchEditClick"><span class="material-icons text-gray-200 pt-1">edit</span></ui-btn>
</template>
<ui-btn v-if="userCanDelete" color="error" small class="mx-2" :loading="processingBatchDelete" @click="batchDeleteClick"><span class="material-icons text-gray-200 pt-1">delete</span></ui-btn>
<span class="material-icons text-4xl px-4 hover:text-gray-100 cursor-pointer" :class="processingBatchDelete ? 'text-gray-400' : ''" @click="cancelSelectionMode">close</span>
</div>
</div>
@ -69,6 +71,12 @@ export default {
},
audiobooksShowing() {
return this.$store.getters['audiobooks/getFiltered']()
},
userCanUpdate() {
return this.$store.getters['user/getUserCanUpdate']
},
userCanDelete() {
return this.$store.getters['user/getUserCanDelete']
}
},
methods: {

View file

@ -19,13 +19,16 @@
<span class="material-icons" :style="{ fontSize: playIconFontSize + 'rem' }">play_circle_filled</span>
</div>
</div>
<div v-show="!isSelectionMode" class="absolute cursor-pointer hover:text-yellow-300 hover:scale-125 transform duration-50" :style="{ top: 0.375 * sizeMultiplier + 'rem', right: 0.375 * sizeMultiplier + 'rem' }" @click.stop.prevent="editClick">
<div v-if="userCanUpdate" v-show="!isSelectionMode" class="absolute cursor-pointer hover:text-yellow-300 hover:scale-125 transform duration-50" :style="{ top: 0.375 * sizeMultiplier + 'rem', right: 0.375 * sizeMultiplier + 'rem' }" @click.stop.prevent="editClick">
<span class="material-icons" :style="{ fontSize: sizeMultiplier + 'rem' }">edit</span>
</div>
<div class="absolute cursor-pointer hover:text-yellow-300 hover:scale-125 transform duration-100" :style="{ top: 0.375 * sizeMultiplier + 'rem', left: 0.375 * sizeMultiplier + 'rem' }" @click.stop.prevent="selectBtnClick">
<div v-if="userCanUpdate || userCanDelete" class="absolute cursor-pointer hover:text-yellow-300 hover:scale-125 transform duration-100" :style="{ top: 0.375 * sizeMultiplier + 'rem', left: 0.375 * sizeMultiplier + 'rem' }" @click.stop.prevent="selectBtnClick">
<span class="material-icons" :class="selected ? 'text-yellow-400' : ''" :style="{ fontSize: 1.25 * sizeMultiplier + 'rem' }">{{ selected ? 'radio_button_checked' : 'radio_button_unchecked' }}</span>
</div>
</div>
<div v-show="!isSelectionMode" class="absolute bottom-0 left-0 h-1 shadow-sm" :class="userIsRead ? 'bg-success' : 'bg-yellow-400'" :style="{ width: width * userProgressPercent + 'px' }"></div>
<ui-tooltip v-if="showError" :text="errorText" class="absolute bottom-4 left-0">
@ -156,6 +159,12 @@ export default {
classes.push('border-2 border-yellow-400')
}
return classes
},
userCanUpdate() {
return this.$store.getters['user/getUserCanUpdate']
},
userCanDelete() {
return this.$store.getters['user/getUserCanDelete']
}
},
methods: {

View file

@ -48,6 +48,10 @@ export default {
text: 'Added At',
value: 'addedAt'
},
{
text: 'Volume #',
value: 'book.volumeNumber'
},
{
text: 'Duration',
value: 'duration'

View file

@ -18,7 +18,7 @@
</div>
<div class="flex py-2">
<div class="px-2">
<ui-input-dropdown v-model="newUser.type" label="Account Type" :disabled="isEditingRoot" :editable="false" :items="accountTypes" />
<ui-input-dropdown v-model="newUser.type" label="Account Type" :disabled="isEditingRoot" :editable="false" :items="accountTypes" @input="userTypeUpdated" />
</div>
<div class="flex-grow" />
<div v-show="!isEditingRoot" class="flex items-center pt-4 px-2">
@ -26,6 +26,37 @@
<ui-toggle-switch v-model="newUser.isActive" :disabled="isEditingRoot" />
</div>
</div>
<div v-if="!isEditingRoot && newUser.permissions" class="w-full border-t border-b border-black-200 py-2 mt-4">
<p class="text-lg mb-2">Permissions</p>
<div class="flex items-center my-2 max-w-lg">
<div class="w-1/2">
<p>Can Download</p>
</div>
<div class="w-1/2">
<ui-toggle-switch v-model="newUser.permissions.download" />
</div>
</div>
<div class="flex items-center my-2 max-w-lg">
<div class="w-1/2">
<p>Can Update</p>
</div>
<div class="w-1/2">
<ui-toggle-switch v-model="newUser.permissions.update" />
</div>
</div>
<div class="flex items-center my-2 max-w-lg">
<div class="w-1/2">
<p>Can Delete</p>
</div>
<div class="w-1/2">
<ui-toggle-switch v-model="newUser.permissions.delete" />
</div>
</div>
</div>
<div class="flex pt-4">
<div class="flex-grow" />
<ui-btn color="success" type="submit">Submit</ui-btn>
@ -144,6 +175,13 @@ export default {
toggleActive() {
this.newUser.isActive = !this.newUser.isActive
},
userTypeUpdated(type) {
this.newUser.permissions = {
download: type !== 'guest',
update: type === 'admin',
delete: type === 'admin'
}
},
init() {
this.isNew = !this.account
if (this.account) {
@ -151,14 +189,20 @@ export default {
username: this.account.username,
password: this.account.password,
type: this.account.type,
isActive: this.account.isActive
isActive: this.account.isActive,
permissions: { ...this.account.permissions }
}
} else {
this.newUser = {
username: null,
password: null,
type: 'user',
isActive: true
isActive: true,
permissions: {
download: true,
update: false,
delete: false
}
}
}
}

View file

@ -6,7 +6,7 @@
</div>
</template>
<div class="absolute -top-10 left-0 w-full flex">
<template v-for="tab in tabs">
<template v-for="tab in availableTabs">
<div :key="tab.id" class="w-28 rounded-t-lg flex items-center justify-center mr-1 cursor-pointer hover:bg-bg font-book border-t border-l border-r border-black-300 tab" :class="selectedTab === tab.id ? 'tab-selected bg-bg pb-px' : 'bg-primary text-gray-400'" @click="selectTab(tab.id)">{{ tab.title }}</div>
</template>
</div>
@ -58,6 +58,15 @@ export default {
show: {
handler(newVal) {
if (newVal) {
var availableTabIds = this.availableTabs.map((tab) => tab.id)
if (!availableTabIds.length) {
this.show = false
return
}
if (!availableTabIds.includes(this.selectedTab)) {
this.selectedTab = availableTabIds[0]
}
if (this.audiobook && this.audiobook.id === this.selectedAudiobookId) {
if (this.fetchOnShow) this.fetchFull()
return
@ -86,6 +95,20 @@ export default {
this.$store.commit('setEditModalTab', val)
}
},
userCanUpdate() {
return this.$store.getters['user/getUserCanUpdate']
},
userCanDownload() {
return this.$store.getters['user/getUserCanDownload']
},
availableTabs() {
if (!this.userCanUpdate && !this.userCanDownload) return []
return this.tabs.filter((tab) => {
if ((tab.id === 'download' || tab.id === 'tracks') && this.userCanDownload) return true
if (tab.id !== 'download' && tab.id !== 'tracks' && this.userCanUpdate) return true
return false
})
},
height() {
var maxHeightAllowed = window.innerHeight - 150
return Math.min(maxHeightAllowed, 650)

View file

@ -54,7 +54,7 @@
<div class="absolute bottom-0 left-0 w-full py-4 bg-bg" :class="isScrollable ? 'box-shadow-md-up' : 'box-shadow-sm-up border-t border-primary border-opacity-50'">
<div class="flex px-4">
<ui-btn color="error" type="button" small @click.stop.prevent="deleteAudiobook">Remove</ui-btn>
<ui-btn v-if="userCanDelete" color="error" type="button" small @click.stop.prevent="deleteAudiobook">Remove</ui-btn>
<div class="flex-grow" />
<ui-btn type="submit">Submit</ui-btn>
</div>
@ -113,12 +113,9 @@ export default {
book() {
return this.audiobook ? this.audiobook.book || {} : {}
},
// userAudiobook() {
// return this.$store.getters['user/getUserAudiobook'](this.audiobookId)
// },
// userProgress() {
// return this.userAudiobook ? this.userAudiobook.progress : 0
// },
userCanDelete() {
return this.$store.getters['user/getUserCanDelete']
},
genres() {
return this.$store.state.audiobooks.genres
},

View file

@ -1,7 +1,7 @@
<template>
<div class="w-full h-full overflow-y-auto overflow-x-hidden px-4 py-6">
<div class="flex mb-4">
<nuxt-link :to="`/audiobook/${audiobook.id}/edit`">
<nuxt-link v-if="userCanUpdate" :to="`/audiobook/${audiobook.id}/edit`">
<ui-btn color="primary">Edit Track Order</ui-btn>
</nuxt-link>
</div>
@ -11,7 +11,7 @@
<th class="text-left">Filename</th>
<th class="text-left">Size</th>
<th class="text-left">Duration</th>
<th class="text-center">Download</th>
<th v-if="userCanDownload" class="text-center">Download</th>
</tr>
<template v-for="track in tracks">
<tr :key="track.index">
@ -27,7 +27,7 @@
<td class="font-mono">
{{ $secondsToTimestamp(track.duration) }}
</td>
<td class="font-mono text-center">
<td v-if="userCanDownload" class="font-mono text-center">
<a :href="`/local/${track.path}`" download><span class="material-icons icon-text">download</span></a>
</td>
</tr>
@ -58,12 +58,18 @@ export default {
}
}
},
computed: {},
computed: {
userCanUpdate() {
return this.$store.getters['user/getUserCanUpdate']
},
userCanDownload() {
return this.$store.getters['user/getUserCanDownload']
}
},
methods: {
init() {
this.audioFiles = this.audiobook.audioFiles
this.tracks = this.audiobook.tracks
console.log('INIT', this.audiobook)
}
}
}

View file

@ -4,7 +4,7 @@
<p class="pr-4">Other Audio Files</p>
<span class="bg-black-400 rounded-xl py-1 px-2 text-sm font-mono">{{ files.length }}</span>
<div class="flex-grow" />
<nuxt-link :to="`/audiobook/${audiobookId}/edit`" class="mr-4">
<nuxt-link v-if="userCanUpdate" :to="`/audiobook/${audiobookId}/edit`" class="mr-4">
<ui-btn small color="primary">Manage Tracks</ui-btn>
</nuxt-link>
<div class="cursor-pointer h-10 w-10 rounded-full hover:bg-black-400 flex justify-center items-center duration-500" :class="showTracks ? 'transform rotate-180' : ''">
@ -56,7 +56,11 @@ export default {
showTracks: false
}
},
computed: {},
computed: {
userCanUpdate() {
return this.$store.getters['user/getUserCanUpdate']
}
},
methods: {
clickBar() {
this.showTracks = !this.showTracks

View file

@ -4,7 +4,7 @@
<p class="pr-4">Audio Tracks</p>
<span class="bg-black-400 rounded-xl py-1 px-2 text-sm font-mono">{{ tracks.length }}</span>
<div class="flex-grow" />
<nuxt-link :to="`/audiobook/${audiobookId}/edit`" class="mr-4">
<nuxt-link v-if="userCanUpdate" :to="`/audiobook/${audiobookId}/edit`" class="mr-4">
<ui-btn small color="primary">Manage Tracks</ui-btn>
</nuxt-link>
<div class="cursor-pointer h-10 w-10 rounded-full hover:bg-black-400 flex justify-center items-center duration-500" :class="showTracks ? 'transform rotate-180' : ''">
@ -19,7 +19,7 @@
<th class="text-left">Filename</th>
<th class="text-left">Size</th>
<th class="text-left">Duration</th>
<th class="text-center">Download</th>
<th v-if="userCanDownload" class="text-center">Download</th>
</tr>
<template v-for="track in tracks">
<tr :key="track.index">
@ -35,7 +35,7 @@
<td class="font-mono">
{{ $secondsToTimestamp(track.duration) }}
</td>
<td class="text-center">
<td v-if="userCanDownload" class="text-center">
<a :href="`/local/${track.path}`" download><span class="material-icons icon-text">download</span></a>
</td>
</tr>
@ -60,7 +60,14 @@ export default {
showTracks: false
}
},
computed: {},
computed: {
userCanDownload() {
return this.$store.getters['user/getUserCanDownload']
},
userCanUpdate() {
return this.$store.getters['user/getUserCanUpdate']
}
},
methods: {
clickBar() {
this.showTracks = !this.showTracks