Skip to content

Commit

Permalink
feat: add custom shelves
Browse files Browse the repository at this point in the history
To create first shelves, go to your profile page and add shelves from the menu.
A navigation item will then be available with your shelves.
Use it to track favourites tags, custom lists, virtual shelves etc...
  • Loading branch information
bayang committed Jun 16, 2022
1 parent 6582515 commit 3b50e2f
Show file tree
Hide file tree
Showing 19 changed files with 752 additions and 73 deletions.
66 changes: 65 additions & 1 deletion src/jelu-ui/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ store.dispatch('getUser')
console.log("ok nav")
initialLoad.value = false
store.dispatch('getServerSettings')
store.dispatch('getUserShelves')
// } catch(e) {
// console.log("error nav")
// console.log(e)
Expand All @@ -53,6 +54,9 @@ const username = computed(() => {
const isLogged = computed(() => {
return store.getters.getLogged
})
const shelves = computed(() => {
return store.getters.getShelves
})
onMounted(() => {
console.log('Component is mounted!')
Expand Down Expand Up @@ -171,7 +175,6 @@ watch(() => route.name, (newVal, oldVal) => {
</ul>
</div>
<router-link
class
:to="{ name: 'home' }"
>
<img
Expand Down Expand Up @@ -276,6 +279,35 @@ watch(() => route.name, (newVal, oldVal) => {
{{ t('nav.history') }}
</router-link>
</li>
<li
v-if="shelves !== null && shelves.length > 0 && isLogged"
tabindex="0"
class="mr-1"
>
<a class="font-sans text-xl capitalize">
{{ t('nav.shelves') }}
<svg
class="fill-current"
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
><path d="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z" /></svg>
</a>
<ul class="p-2 bg-base-100 z-50">
<li
v-for="shelf in shelves"
:key="shelf"
>
<router-link
v-if="isLogged"
:to="{ name: 'tag-detail', params: { tagId: shelf.targetId }, query: {sort: 'modificationDate,desc'} }"
>
{{ shelf.name }}
</router-link>
</li>
</ul>
</li>
</ul>
<div
v-if="isLogged && showSearchInput"
Expand Down Expand Up @@ -324,6 +356,38 @@ watch(() => route.name, (newVal, oldVal) => {
</button>
</div>
<div class="navbar-end">
<div
v-if="isLogged && shelves != null && shelves.length > 0"
class="dropdown lg:hidden"
>
<label
tabindex="0"
class="btn btn-ghost rounded-btn lg:hidden"
>
<span class="h-fit"><i class="mdi mdi-bookshelf mdi-24px" /></span>
</label>
<div
tabindex="0"
class="dropdown-content mt-2 -left-20"
>
<ul
tabindex="0"
class="menu menu-compact dropdown-content mt-3 p-2 shadow bg-base-100 rounded-box w-52"
>
<li
v-for="shelf in shelves"
:key="shelf"
>
<router-link
v-if="isLogged"
:to="{ name: 'tag-detail', params: { tagId: shelf.targetId }, query: {sort: 'modificationDate,desc'} }"
>
{{ shelf.name }}
</router-link>
</li>
</ul>
</div>
</div>
<div class="dropdown dropdown-end">
<label
tabindex="0"
Expand Down
2 changes: 1 addition & 1 deletion src/jelu-ui/src/components/AdminAuthors.vue
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ const rightDoD = computed(() => {
</h1>{{ t('authors_merge.authors_merge_description') }}
</o-step-item>
</o-steps>
<div class="field jelu-authorinput">
<div class="field">
<o-field :label="t('authors_merge.search_message')">
<o-autocomplete
:data="filteredAuthors"
Expand Down
186 changes: 149 additions & 37 deletions src/jelu-ui/src/components/ProfilePage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ import { useStore } from 'vuex'
import { key } from '../store'
import UserModalVue from './UserModal.vue'
import { Provider } from "../model/User"
import { Shelf } from "../model/Shelf"
import dataService from "../services/DataService";
import { Tag } from "../model/Tag"
const {oruga} = useProgrammatic()
const { oruga } = useProgrammatic()
const { t } = useI18n({
inheritLocale: true,
Expand All @@ -26,6 +29,30 @@ const user = computed(() => {
return store.getters.getUser
})
let filteredTags: Ref<Array<Tag>> = ref([]);
const isFetching = ref(false)
function getFilteredTags(text: string) {
isFetching.value = true
dataService.findTagsByCriteria(text).then((data) => filteredTags.value = data.content)
isFetching.value = false
}
function createShelfFromTag(tag: Tag, event: UIEvent) {
console.log(tag)
console.log(event)
// we receive from oruga weird events while nothing is selected
// so try to get rid of those null data we receive
if (tag != null && event != null && tag.id != null) {
dataService.saveShelf({name: tag.name, targetId: tag.id})
.then(res => {
console.log("saved shef " + res.name)
store.dispatch('getUserShelves')
})
.catch(err => console.log("failed to save shelf " + tag.name + " " + err))
}
}
function toggleUserModal() {
showModal.value = !showModal.value
oruga.modal.open({
Expand All @@ -41,51 +68,136 @@ function toggleUserModal() {
});
}
function deleteShelf(shelf: Shelf) {
dataService.deleteShelf(shelf.id)
.then(res => {
console.log("deleted shelf " + shelf.id)
store.dispatch('getUserShelves')
})
.catch(err => console.log("failed to delete shelf " + shelf.id))
}
function modalClosed() {
console.log("modal closed")
store.dispatch('getUser')
}
const shelves = computed(() => {
return store.getters.getShelves
})
</script>

<template>
<div class="w-fit">
<h1 class="typewriter text-2xl mb-3 capitalize">
{{ t('settings.profile') }} :
</h1>
<div class="card card-side bg-base-100 shadow-2xl">
<Avatar
:username="store != null && store.getters.getUsername"
class="ml-4 mt-8"
/>
<div class="card-body">
<p>
<span class="capitalize">{{ t('settings.username') }}</span> :
<strong>{{ store.getters.getUsername }}</strong>
<br>
<span class="capitalize">{{ t('settings.role', 2) }}</span> :
<span class="badge badge-info tag is-info">USER</span> &nbsp;
<span
v-if="store.getters.isAdmin"
class="badge badge-warning tag is-warning"
>ADMIN</span>
</p>
<div class="card-actions justify-end">
<button
v-if="user.provider !== Provider.LDAP"
v-tooltip="t('profile.edit_user')"
class="btn btn-circle btn-ghost"
@click="toggleUserModal"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="text-base-content"
<div class="w-fit flex flex-wrap justify-items-center justify-self-center">
<div>
<h1 class="typewriter text-2xl mb-3 capitalize">
{{ t('settings.profile') }} :
</h1>
<div class="card card-side bg-base-200 shadow-2xl">
<Avatar
:username="store != null && store.getters.getUsername"
class="ml-4 mt-8"
/>
<div class="card-body">
<p>
<span class="capitalize">{{ t('settings.username') }}</span> :
<strong>{{ store.getters.getUsername }}</strong>
<br>
<span class="capitalize">{{ t('settings.role', 2) }}</span> :
<span class="badge badge-info tag is-info">USER</span> &nbsp;
<span
v-if="store.getters.isAdmin"
class="badge badge-warning tag is-warning"
>ADMIN</span>
</p>
<div class="card-actions justify-end">
<button
v-if="user.provider !== Provider.LDAP"
v-tooltip="t('profile.edit_user')"
class="btn btn-circle btn-ghost"
@click="toggleUserModal"
>
<path d="M19.045 7.401c.378-.378.586-.88.586-1.414s-.208-1.036-.586-1.414l-1.586-1.586c-.378-.378-.88-.586-1.414-.586s-1.036.208-1.413.585L4 13.585V18h4.413L19.045 7.401zm-3-3 1.587 1.585-1.59 1.584-1.586-1.585 1.589-1.584zM6 16v-1.585l7.04-7.018 1.586 1.586L7.587 16H6zm-2 4h16v2H4z" />
</svg>
</button>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="text-base-content"
>
<path
d="M19.045 7.401c.378-.378.586-.88.586-1.414s-.208-1.036-.586-1.414l-1.586-1.586c-.378-.378-.88-.586-1.414-.586s-1.036.208-1.413.585L4 13.585V18h4.413L19.045 7.401zm-3-3 1.587 1.585-1.59 1.584-1.586-1.585 1.589-1.584zM6 16v-1.585l7.04-7.018 1.586 1.586L7.587 16H6zm-2 4h16v2H4z"
/>
</svg>
</button>
</div>
</div>
</div>
</div>

<div class="sm:mx-1 w-full sm:w-fit">
<h1 class="typewriter text-2xl mb-3 capitalize">
{{ t('settings.shelves') }} :
</h1>
<div>
<ul>
<li
v-for="shelf in shelves"
:key="shelf"
class="my-2"
>
<div class="alert shadow-lg w-full">
<div class="w-full justify-around">
<i class="mdi mdi-bookshelf mdi-24px" />
<div>
<h3 class="font-bold">
{{ shelf.name }}
</h3>
</div>
<div class="flex-none">
<button
class="btn btn-sm"
@click="deleteShelf(shelf)"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
clip-rule="evenodd"
/>
</svg>
</button>
</div>
</div>
</div>
</li>
</ul>
</div>
</div>
<div class="sm:mx-1 w-full sm:w-fit">
<div
v-if="shelves.length >= 10"
class="mt-4 text-lg"
>
{{ t('settings.shelves_max_number_reached') }}
</div>
<div v-else>
<div class="field">
<o-field :label="t('settings.shelf_choose_tag')">
<o-autocomplete
:data="filteredTags"
:clear-on-select="true"
field="name"
:loading="isFetching"
:debounce-typing="100"
@typing="getFilteredTags"
@select="createShelfFromTag"
/>
</o-field>
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit 3b50e2f

Please sign in to comment.