Implement proper login/logout UI, fix breadcrumbs on file listing.

This commit is contained in:
Leo Vasanko 2023-11-07 16:10:56 +00:00
parent c695c09ecc
commit 54d6ea6332
8 changed files with 67 additions and 19 deletions

View File

@ -13,6 +13,7 @@
"format": "prettier --write src/"
},
"dependencies": {
"@imengyu/vue3-context-menu": "^1.3.3",
"@vueuse/core": "^10.4.1",
"esbuild": "^0.19.5",
"lodash": "^4.17.21",

View File

@ -57,9 +57,9 @@
<td class="menu"></td>
</tr>
<template
v-for="doc of sorted(props.documents as Document[])"
v-for="(doc, index) in sortedDocuments"
:key="doc.key">
<tr class="folder-change" v-if="doc.loc !== prevloc && ((prevloc = doc.loc) || true)">
<tr class="folder-change" v-if="showFolderBreadcrumb(index)">
<th colspan="5"><BreadCrumb :path="doc.loc ? doc.loc.split('/') : []" /></th>
</tr>
@ -188,6 +188,12 @@ const rename = (doc: Document, newName: string) => {
}
doc.name = newName // We should get an update from watch but this is quicker
}
const sortedDocuments = computed(() => sorted(props.documents as Document[]))
const showFolderBreadcrumb = (i: number) => {
const docs = sortedDocuments.value
const docloc = docs[i].loc
return i === 0 ? docloc !== loc.value : docloc !== docs[i - 1].loc
}
defineExpose({
newFolder() {
const now = Date.now() / 1000
@ -230,7 +236,7 @@ defineExpose({
},
cursorMove(d: number, select = false) {
// Move cursor up or down (keyboard navigation)
const documents = sorted(props.documents as Document[])
const documents = sortedDocuments.value
if (documents.length === 0) {
cursor.value = null
return
@ -358,8 +364,6 @@ const allSelected = computed({
})
const loc = computed(() => props.path.join('/'))
let prevloc = ''
onBeforeUpdate(() => { prevloc = loc.value })
const contextMenu = (ev: Event, doc: Document) => {
cursor.value = doc

View File

@ -20,14 +20,26 @@
/>
</template>
<SvgButton ref="searchButton" name="find" @click.prevent="toggleSearchInput" />
<SvgButton name="cog" @click="console.log('settings menu')" />
<SvgButton name="cog" @click="settingsMenu" />
</div>
</nav>
<context-menu v-model:show="showMenu">
<context-menu-item label="Simple item" @click="onMenuClick(1)" />
<context-menu-sperator /><!--use this to add sperator-->
<context-menu-group label="Menu with child">
<context-menu-item label="Item1" @click="onMenuClick(2)" />
<context-menu-item label="Item2" @click="onMenuClick(3)" />
<context-menu-group label="Child with v-for 50">
<context-menu-item v-for="index of 50" :key="index" :label="'Item3-'+index" @click="onLoopMenuClick(index)" />
</context-menu-group>
</context-menu-group>
</context-menu>
</template>
<script setup lang="ts">
import { useDocumentStore } from '@/stores/documents'
import { ref, nextTick } from 'vue'
import ContextMenu from '@imengyu/vue3-context-menu'
const documentStore = useDocumentStore()
const showSearchInput = ref<boolean>(false)
@ -50,6 +62,17 @@ const toggleSearchInput = () => {
})
}
const settingsMenu = (e: Event) => {
// show the context menu
ContextMenu.showContextMenu({
// @ts-ignore
x: e.target.getBoundingClientRect().right, y: e.target.getBoundingClientRect().bottom,
items: [
{ label: "Logout", onClick: () => { documentStore.logout() } },
]
})
}
defineExpose({
toggleSearchInput,
closeSearch,

View File

@ -1,8 +1,5 @@
<template>
<button v-if="store.isUserLogged" @click="logout" class="action-button">
Logout {{ store.user.username }}
</button>
<ModalDialog v-if="store.user.isOpenLoginModal" title="Login">
<ModalDialog v-if="store.user.isOpenLoginModal" title="Authentication required">
<form @submit.prevent="login">
<div class="login-container">
<label for="username">Username:</label>
@ -26,7 +23,10 @@
<h3 v-if="loginForm.error.length > 0" class="error-text">
{{ loginForm.error }}
</h3>
<input id="submit" type="submit" class="button-login" />
<div class="dialog-buttons">
<div class="spacer"></div>
<input id="submit" type="submit" value="Login" class="button-login" />
</div>
</form>
</ModalDialog>
</template>
@ -81,10 +81,19 @@ const login = async () => {
align-items: center;
margin: 1rem 0;
}
.dialog-buttons {
display: flex;
justify-content: space-between;
align-items: center;
}
.button-login {
cursor: pointer;
border: 0;
border-radius: .5rem;
padding: .5rem;
margin-left: auto;
background-color: var(--secondary-color);
color: var(--secondary-background);
background-color: var(--accent-color);
color: var(--primary-color);
}
.ant-btn-primary:not(:disabled):hover {
background-color: var(--blue-color);

View File

@ -46,6 +46,8 @@ body:has(dialog[open])::before {
/* Hide the dialog by default */
dialog[open] {
background: #ddd;
color: black;
display: block;
border: none;
border-radius: 0.5rem;
@ -58,8 +60,8 @@ dialog[open] {
}
dialog[open] > h1 {
background: #00f;
color: #fff;
background: var(--accent-color);
color: black;
font-size: 1rem;
margin: -1rem -1rem 0 -1rem;
padding: 0.5rem 1rem 0.5rem 1rem;

View File

@ -8,6 +8,9 @@ import router from './router'
import piniaPluginPersistedState from 'pinia-plugin-persistedstate'
import ContextMenu from '@imengyu/vue3-context-menu'
import '@imengyu/vue3-context-menu/lib/vue3-context-menu.css'
const app = createApp(App)
app.config.errorHandler = err => {
/* handle error */
@ -17,6 +20,6 @@ app.config.errorHandler = err => {
const pinia = createPinia()
pinia.use(piniaPluginPersistedState)
app.use(pinia)
app.use(router)
app.use(ContextMenu)
app.mount('#app')

View File

@ -4,7 +4,6 @@ export type Document = {
loc: string
name: string
key: FUID
type: 'folder' | 'file'
size: number
sizedisp: string
mtime: number

View File

@ -3,7 +3,6 @@ import type {
DirEntry,
FileEntry,
FUID,
DirList,
SelectedItems
} from '@/repositories/Document'
import { formatSize, formatUnixDate, haystackFormat } from '@/utils'
@ -67,8 +66,10 @@ export const useDocumentStore = defineStore({
// Recurse but replace recursive structure with boolean
loc = doc.loc ? `${doc.loc}/${doc.name}` : doc.name
queue.push(...Object.entries(doc.dir).map(mapper))
// @ts-ignore
doc.dir = true
}
// @ts-ignore
else doc.dir = false
}
// Pre sort directory entries folders first then files, names in natural ordering
@ -77,7 +78,7 @@ export const useDocumentStore = defineStore({
b.dir - a.dir ||
collator.compare(a.name, b.name)
)
this.document = docs
this.document = docs as Document[]
},
updateUploadingDocuments(key: number, progress: number) {
for (const d of this.uploadingDocuments) {
@ -107,6 +108,12 @@ export const useDocumentStore = defineStore({
this.user.privileged = privileged
this.user.isLoggedIn = true
this.user.isOpenLoginModal = false
},
async logout() {
const res = await fetch('/logout', { method: 'POST' })
if (!res.ok) throw Error(`Logout failed: ${res.statusText}`)
this.$reset()
history.go() // Reload page
}
},
getters: {