Login, Download and visuals update

This commit is contained in:
2023-10-28 04:13:01 -05:00
parent 2b72508206
commit b3fd9637eb
16 changed files with 303 additions and 178 deletions

View File

@@ -16,6 +16,7 @@ declare module 'vue' {
AImage: typeof import('ant-design-vue/es')['Image']
AInput: typeof import('ant-design-vue/es')['Input']
AInputSearch: typeof import('ant-design-vue/es')['InputSearch']
AModal: typeof import('ant-design-vue/es')['Modal']
APageHeader: typeof import('ant-design-vue/es')['PageHeader']
APopover: typeof import('ant-design-vue/es')['Popover']
AppNavigation: typeof import('./src/components/AppNavigation.vue')['default']
@@ -27,6 +28,7 @@ declare module 'vue' {
FileExplorer: typeof import('./src/components/FileExplorer.vue')['default']
FileViewer: typeof import('./src/components/FileViewer.vue')['default']
HeaderMain: typeof import('./src/components/HeaderMain.vue')['default']
LoginModal: typeof import('./src/components/LoginModal.vue')['default']
NotificationLoading: typeof import('./src/components/NotificationLoading.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']

View File

@@ -5,7 +5,8 @@
--table-background: #535353;
--primary-color: #ffffff;
--secondary-color: #ccc;
--blue-color: #66ffeb
--blue-color: #66ffeb;
--red-color: #ff4d4f;
}
@media (prefers-color-scheme: dark) {
:root {
@@ -15,7 +16,8 @@
--table-background: #535353;
--primary-color: #ffffff;
--secondary-color: #ccc;
--blue-color: #66ffeb
--blue-color: #66ffeb;
--red-color: #ff4d4f;
}
}
body {
@@ -93,5 +95,11 @@ body {
.ant-empty-description{
color: var(--primary-color);
}
.ant-modal .ant-modal-content{
background-color: var(--secondary-background);
}
.ant-modal .ant-modal-close-x{
color: var(--font-color);
}
}

View File

@@ -1,5 +1,6 @@
<template>
<main>
<context-holder />
<!-- <h2 v-if="!documentStore.loading && documentStore.error"> {{ documentStore.error }} </h2> -->
<div class="carousel-container" v-if="!documentStore.loading && documentStore.mainDocument[0] && documentStore.mainDocument[0].type === 'file'">
<FileCarousel></FileCarousel>
@@ -32,13 +33,27 @@
<template #content>
<div class="more-action">
<div class="action-container">
<a-button type="text" class="action-button" :icon="h(ImportOutlined)"/>Open
<a :href="`${record.type === 'folder'? linkBasePath+'#' : filesBasePath}/${record.name}`">
<a-button type="text" class="action-button" :icon="h(ImportOutlined)"/>
</a>
Open
</div>
<div v-if="record.type === 'folder-file'" class="action-container">
<a :href="`${filesBasePath}/${record.name}`" download>
<a-button type="text" class="action-button" :icon="h(DownloadOutlined)"/>
</a>
Download
</div>
<div class="action-container">
<a-button type="text" class="action-button" :icon="h(EditOutlined)"/> Rename
<a-button type="text" class="action-button" @click="edit(record.key)" :icon="h(EditOutlined)"/> Rename
</div>
<div class="action-container">
<a-button type="text" class="action-button" :icon="h(LinkOutlined)"/> Share
<a-button
type="text"
class="action-button"
@click="share(`${record.type === 'folder'? linkBasePath+'/#' : filesBasePath}/${record.name}`)"
:icon="h(LinkOutlined)"
/> Share
</div>
<div class="action-container">
<a-button type="text" class="action-button" :icon="h(CopyOutlined)"/> Copy
@@ -69,14 +84,14 @@
import { EditOutlined, ImportOutlined, CheckOutlined,CopyOutlined, ScissorOutlined, LinkOutlined, DownloadOutlined, DeleteOutlined, EllipsisOutlined } from '@ant-design/icons-vue'
import type { TableColumnsType } from 'ant-design-vue';
import Router from '@/router/index';
import { message } from 'ant-design-vue';
import type { Document, FolderDocument } from '@/repositories/Document';
import FileCarousel from './FileCarousel.vue';
const [messageApi, contextHolder] = message.useMessage();
type Key = string | number;
const documentStore = useDocumentStore()
watchEffect(()=>{
console.log(documentStore.mainDocument)
})
const editableData: UnwrapRef<Record<string, Document>> = reactive({});
const state = reactive<{
selectedRowKeys: Key[];
@@ -143,7 +158,10 @@
Object.assign(documentStore.mainDocument.filter(item => key === item.key)[0], editableData[key]);
delete editableData[key];
};
const share = async (url : string) => {
await navigator.clipboard.writeText(location.origin + url)
messageApi.success("Link successfully copied to the clipboard");
}
</script>
<style>

View File

@@ -30,11 +30,6 @@ import { url_document_get } from '@/repositories/Document';
const dataURL = ref('')
watchEffect(()=>{
console.log('😎😎😎😎')
console.log(url_document_get)
console.log(Router.currentRoute)
console.log(Router.currentRoute.value.path)
console.log('----------')
dataURL.value = new URL(
url_document_get + Router.currentRoute.value.path,
location.origin

View File

@@ -1,8 +1,9 @@
<script setup lang="ts">
import { useDocumentStore } from '@/stores/documents'
import LoginModal from '@/components/LoginModal.vue'
import UploadButton from '@/components/UploadButton.vue'
import { InfoCircleOutlined, SettingOutlined, PlusSquareOutlined, SearchOutlined, DeleteOutlined, DownloadOutlined, FileAddFilled, LinkOutlined, FolderAddFilled, FileFilled, FolderFilled } from '@ant-design/icons-vue'
import { h, ref, defineProps } from 'vue';
import { h, ref } from 'vue';
const documentStore = useDocumentStore()
const searchQuery = ref<string>('')
@@ -94,8 +95,8 @@ function download(){
</a-tooltip>
</template>
</div>
<div class="actions-list">
<LoginModal></LoginModal>
<template v-if="showSearchInput">
<a-input-search
v-model="searchQuery"

View File

@@ -0,0 +1,90 @@
<template>
<a-tooltip title="Login">
<template v-if="DocumentStore.isUserLogged">
<a-button @click="logout" type="text" class="action-button" :icon="h(UserDeleteOutlined)" />
</template>
<template v-else>
<a-button @click="showModal" type="text" class="action-button" :icon="h(UserOutlined)" />
</template>
</a-tooltip>
<a-modal v-model:open="DocumentStore.user.isOpenLoginModal" :confirm-loading="confirmLoading" okText="Login" @ok="login">
<div class="login-container">
<a-form :model="loginForm">
<a-form-item label="Username" prop="username" :rules="[{ required: true, message: 'Please input your username!' }]">
<a-input v-model:value="loginForm.username" />
</a-form-item>
<a-form-item label="Password" prop="password" :rules="[{ required: true, message: 'Please input your password!' }]">
<a-input type="password" v-model:value="loginForm.password" />
</a-form-item>
<h3 v-if="loginForm.error.length > 0" class="error-text">{{loginForm.error}}</h3>
</a-form>
</div>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, h } from 'vue';
import { UserOutlined, UserDeleteOutlined } from '@ant-design/icons-vue';
import { useDocumentStore } from '@/stores/documents';
import { loginUser, logoutUser } from '@/repositories/User';
import type { ISimpleError } from '@/repositories/Client';
const DocumentStore = useDocumentStore();
const confirmLoading = ref<boolean>(false);
const showModal = () => {
DocumentStore.user.isOpenLoginModal = true;
};
const logout = async () => {
try {
await logoutUser();
} catch (error) {} finally {
location.reload();
}
}
const loginForm = ref({
username: '',
password: '',
error: '',
});
const login = async () => {
try {
loginForm.value.error = '';
confirmLoading.value = true;
const user = await loginUser(loginForm.value.username, loginForm.value.password);
if(user){
location.reload();
}
} catch (error) {
const httpError = error as ISimpleError
if(httpError.name){
loginForm.value.error = httpError.message
}
}finally{
confirmLoading.value = false;
}
};
</script>
<style scoped>
.login-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 30vh;
}
.button-login {
background-color: var(--secondary-color);
color: var(--secondary-background);
}
.ant-btn-primary:not(:disabled):hover{
background-color: var(--blue-color);
}
.error-text {
color :var(--red-color)
}
</style>

View File

@@ -1,4 +1,4 @@
import axios from 'axios'
import axios, {AxiosError} from 'axios'
/* Base domain for all request */
export const baseURL = import.meta.env.VITE_URL_DOCUMENT
@@ -17,9 +17,10 @@ Client.interceptors.response.use(
// Do something with response data
return response
},
(error) => {
const msg = error.response?.data.message ?? 'Unexpected error'
const code = error.code ? Number(error.code) : 500
(error: AxiosError<any>) => {
const msg = error.response && error.response.data && error.response.data.error ?
error.response.data.error.message : 'Unexpected error'
const code = error.code ? Number(error.response?.status) : 500
const standardizedError = new SimpleError(code, msg)
return Promise.reject(standardizedError)

View File

@@ -23,6 +23,14 @@ export type FileDocument = BaseDocument & {
data: string;
};
export type errorEvent = {
error: {
code : number;
message: string;
redirect: string;
}
};
export type Document = FolderDocument | FileDocument;
@@ -44,19 +52,36 @@ export class DocumentHandler {
case !!msg.update:
this.handleUpdateMessage(msg);
break;
case !!msg.error:
this.handleError(msg);
break;
default:
}
}
private handleRootMessage({ root }: { root: FileStructure }) {
if (this.store && this.store.root) this.store.root = root;
if (this.store && this.store.root) {
this.store.user.isLoggedIn = true;
this.store.root = root;
}
}
private handleUpdateMessage(updateData: { update: FileStructure[] }) {
const root = updateData.update[0]
if(root) this.store.root = root
if(root) {
this.store.user.isLoggedIn = true;
this.store.root = root
}
}
private handleError(msg: errorEvent){
if(msg.error.code === 401){
this.store.user.isOpenLoginModal = true;
this.store.user.isLoggedIn = false;
return
}
}
}
export class DocumentUploadHandler {
constructor( private store: DocumentStore = useDocumentStore() ) {
this.handleWebSocketMessage = this.handleWebSocketMessage.bind(this);

View File

@@ -0,0 +1,23 @@
import Client from '@/repositories/Client'
export const url_login = '/login'
export const url_logout = '/logout '
export async function loginUser(username : string, password: string){
try {
const user = await Client.post(url_login, {
username, password
})
return user;
} catch (error) {
throw error
}
}
export async function logoutUser(){
try {
const data = await Client.post(url_logout)
return data;
} catch (error) {
throw error
}
}

View File

@@ -1,6 +1,5 @@
import { createRouter, createWebHashHistory } from 'vue-router'
import ExplorerView from '../views/ExplorerView.vue'
import LoginView from '../views/LoginView.vue'
const router = createRouter({
history: createWebHashHistory(import.meta.env.BASE_URL),

View File

@@ -10,6 +10,10 @@ type DirectoryData = {
[filename: string]: FileData;
};
export type FileStructure = {id: string, mtime: number, size: number, dir: DirectoryData};
type User = {
isOpenLoginModal: boolean,
isLoggedIn : boolean,
}
export type DocumentStore = {
root: FileStructure,
@@ -20,6 +24,7 @@ export type DocumentStore = {
wsWatch: WebSocket | undefined,
wsUpload: WebSocket | undefined,
selectedDocuments: Document[],
user: User,
error: string,
}
@@ -35,6 +40,7 @@ export const useDocumentStore = defineStore({
wsUpload: undefined,
selectedDocuments: [] as Document[],
error: '' as string,
user: { isLoggedIn: false, isOpenLoginModal: false } as User
}),
actions: {
@@ -140,7 +146,7 @@ export const useDocumentStore = defineStore({
for (const d of this.document) {
if ("mtime" in d) d.modified = formatUnixDate(d.mtime)
}
}
},
},
getters: {
mainDocument(): Document[] {
@@ -151,6 +157,9 @@ export const useDocumentStore = defineStore({
},
rootMain(): DirectoryData | undefined {
if(this.root) return this.root.dir
},
isUserLogged(): boolean{
return this.user.isLoggedIn
}
},
});

View File

@@ -1,46 +0,0 @@
<template>
<div class="login-container">
<a-form :model="loginForm">
<a-form-item label="Username" prop="username" :rules="[{ required: true, message: 'Please input your username!' }]">
<a-input v-model:value="loginForm.username" />
</a-form-item>
<a-form-item label="Password" prop="password" :rules="[{ required: true, message: 'Please input your password!' }]">
<a-input type="password" v-model:value="loginForm.password" />
</a-form-item>
<a-form-item>
<a-button type="primary" @click="login" class="button-login">Login</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
const loginForm = ref({
username: '',
password: '',
});
const login = () => {
};
</script>
<style scoped>
.login-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.button-login {
background-color: var(--secondary-color);
color: var(--secondary-background);
}
.ant-btn-primary:not(:disabled):hover{
background-color: var(--blue-color);
}
</style>