Move more stuff to Pankow
This commit is contained in:
@@ -1,41 +0,0 @@
|
||||
<template>
|
||||
<div class="cloudron-header">
|
||||
<div class="cloudron-header-left">
|
||||
<slot name="left"/>
|
||||
</div>
|
||||
<div class="cloudron-header-right">
|
||||
<slot name="right"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.cloudron-header {
|
||||
width: 100%;
|
||||
background-color: #f8f8f8;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,.1);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.cloudron-header-left {
|
||||
display: block;
|
||||
margin: auto 0px;
|
||||
}
|
||||
|
||||
.cloudron-header-right {
|
||||
display: block;
|
||||
margin: auto 0px;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,93 +0,0 @@
|
||||
<template>
|
||||
<div class="directory-view">
|
||||
<div class="directory-view-header">
|
||||
<div class="directory-view-header-icon"></div>
|
||||
<div class="directory-view-header-name">Name</div>
|
||||
<div class="directory-view-header-size">Size</div>
|
||||
<div class="directory-view-header-modified">Modified</div>
|
||||
</div>
|
||||
<div class="directory-view-body-container">
|
||||
<div class="directory-view-body">
|
||||
<DirectoryViewListItem v-for="item in items" :item="item"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { ref } from 'vue';
|
||||
import Button from 'primevue/button';
|
||||
|
||||
import DirectoryViewListItem from './DirectoryViewListItem.vue';
|
||||
|
||||
export default {
|
||||
name: 'DirectoryView',
|
||||
data() {
|
||||
return {
|
||||
items: []
|
||||
};
|
||||
},
|
||||
components: {
|
||||
DirectoryViewListItem
|
||||
},
|
||||
mounted() {
|
||||
// fill with fake items
|
||||
for (let i = 0; i < 100; ++i) {
|
||||
this.items.push({
|
||||
type: i < 20 ? 'directory' : 'file',
|
||||
name: 'Entry ' + i,
|
||||
size: parseInt(Math.random() * 1000000),
|
||||
modified: new Date(+(new Date()) - Math.floor(Math.random() * 10000000000)),
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.directory-view {
|
||||
background-color: white;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.directory-view-header {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.directory-view-body-container {
|
||||
overflow: hidden;
|
||||
height: calc(100% - 38px); /* 38px is the header size */
|
||||
}
|
||||
|
||||
.directory-view-body {
|
||||
padding: 0 10px;
|
||||
height: 100%;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.directory-view-header-icon {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.directory-view-header-name {
|
||||
padding-left: 10px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.directory-view-header-size {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.directory-view-header-modified {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -1,77 +0,0 @@
|
||||
<template>
|
||||
<div class="row">
|
||||
<div class="col icon"><img :src="item.type === 'file' ? '/mime-types/text-x-plain.svg' : '/mime-types/inode-directory.svg'"/></div>
|
||||
<div class="col label">{{ item.name }}</div>
|
||||
<div class="col size">{{ prettyFileSize(item.size) }}</div>
|
||||
<div class="col modified">{{ prettyDate(item.modified) }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { prettyDate, prettyFileSize } from '../utils';
|
||||
|
||||
export default {
|
||||
name: 'DirectoryViewListItem',
|
||||
props: {
|
||||
item: Object
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {
|
||||
prettyFileSize,
|
||||
prettyDate
|
||||
},
|
||||
mounted() {
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.row:hover {
|
||||
background-color: whitesmoke;
|
||||
}
|
||||
|
||||
.col {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.icon {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.icon > img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
object-fit: cover;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.label {
|
||||
padding-left: 5px;
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.size {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.modified {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div class="preview-panel">
|
||||
<span>Preview for {{ item.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'PreviewPanel',
|
||||
props: {
|
||||
item: Object
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.preview-panel {
|
||||
min-width: 260px;
|
||||
width: 20%;
|
||||
padding: 0 30px;
|
||||
}
|
||||
|
||||
</style>
|
||||
+20
-7
@@ -1,15 +1,16 @@
|
||||
<template>
|
||||
<div>
|
||||
<CloudronHeader>
|
||||
<NavigationBar>
|
||||
<template #left>
|
||||
<span>You are logged in</span>
|
||||
</template>
|
||||
<template #right>
|
||||
<Button label="Logout" @click="onLogout"/>
|
||||
</template>
|
||||
</CloudronHeader>
|
||||
</NavigationBar>
|
||||
<div class="main-view">
|
||||
<DirectoryView />
|
||||
<DirectoryView @selection-changed="onSelectionChanged"/>
|
||||
<PreviewPanel :item="activeItem"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -18,20 +19,31 @@
|
||||
|
||||
import Button from 'primevue/button';
|
||||
|
||||
import DirectoryView from '../components/DirectoryView.vue';
|
||||
import CloudronHeader from '../components/CloudronHeader.vue';
|
||||
import { DirectoryView, NavigationBar } from 'pankow';
|
||||
|
||||
import PreviewPanel from '../components/PreviewPanel.vue';
|
||||
|
||||
export default {
|
||||
name: 'Home',
|
||||
components: {
|
||||
DirectoryView,
|
||||
CloudronHeader,
|
||||
Button
|
||||
NavigationBar,
|
||||
Button,
|
||||
PreviewPanel
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeItem: {}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onLogout() {
|
||||
delete localStorage.accessToken;
|
||||
this.$router.push('/login');
|
||||
},
|
||||
onSelectionChanged(item) {
|
||||
console.log('also changed here', item);
|
||||
this.activeItem = item;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -41,6 +53,7 @@ export default {
|
||||
<style scoped>
|
||||
|
||||
.main-view {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
+11
-64
@@ -1,91 +1,38 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<img style="margin-top: -84px" src="/logo.png" width="128" height="128" />
|
||||
|
||||
<form @submit="onLogin" @submit.prevent>
|
||||
<h1>Login to <b>Cloudron</b></h1>
|
||||
|
||||
<div class="field">
|
||||
<label for="usernameInput">Username</label>
|
||||
<InputText id="usernameInput" type="username" v-model="username" :disabled="busy" required/>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="passwordInput">Password</label>
|
||||
<Password input-id="passwordInput" v-model="password" :disabled="busy" toggle-mask :feedback="false" required :class="{ 'p-invalid': error }" input-style="width: 100%"/>
|
||||
<small v-show="error" :class="{ 'p-invalid': error }">Wrong username or password.</small>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="totpTokenInput">2FA Token (if enabled)</label>
|
||||
<InputMask id="totpTokenInput" v-model="totpToken" :disabled="busy" slotChar="." mask="999999" />
|
||||
</div>
|
||||
|
||||
<div class="action-bar">
|
||||
<a href="">Reset password</a>
|
||||
<Button class="submit-button" type="submit" label="Sign in" id="loginButton" :loading="busy" :disabled="busy || !username || !password"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<LoginView :login-url="loginUrl" @error="onError" @success="onSuccess"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import superagent from 'superagent';
|
||||
import safe from 'safetydance';
|
||||
|
||||
// if imported in script setup tag they are also registered with the vue app as components to be used in html
|
||||
import Button from 'primevue/button';
|
||||
import InputText from 'primevue/inputtext';
|
||||
import Password from 'primevue/password';
|
||||
import InputMask from 'primevue/inputmask';
|
||||
import { LoginView } from 'pankow';
|
||||
|
||||
// can be exposed as env var, see develop.sh
|
||||
const BASE_URL = import.meta.env.VITE_API_ORIGIN ? 'https://' + import.meta.env.VITE_API_ORIGIN : '';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Button, InputText, Password, InputMask
|
||||
LoginView
|
||||
},
|
||||
emits: [ 'success', 'error' ],
|
||||
data() {
|
||||
return {
|
||||
username: '',
|
||||
password: '',
|
||||
totpToken: '',
|
||||
error: '',
|
||||
busy: false
|
||||
busy: false,
|
||||
loginUrl: `${BASE_URL}/api/v1/cloudron/login`
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async onLogin() {
|
||||
this.error = false;
|
||||
this.busy = true;
|
||||
|
||||
const [err, res] = await safe(superagent.post(`${BASE_URL}/api/v1/cloudron/login`).send({ username: this.username, password: this.password, totpToken: this.totpToken }));
|
||||
|
||||
this.busy = false;
|
||||
|
||||
if (err && err.status === 401) {
|
||||
this.error = 'Invalid username or password';
|
||||
this.password = '';
|
||||
return;
|
||||
}
|
||||
if (err) return console.error(err);
|
||||
|
||||
localStorage.accessToken = res.body.accessToken;
|
||||
onError(error) {
|
||||
console.error('Error loggin in', error);
|
||||
},
|
||||
onSuccess(accessToken) {
|
||||
console.log('Success loggin in');
|
||||
|
||||
localStorage.accessToken = accessToken;
|
||||
this.$router.push('/home');
|
||||
|
||||
this.$emit('success', res.body.accessToken);
|
||||
|
||||
this.username = '';
|
||||
this.password = '';
|
||||
this.totpToken = '';
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
setTimeout(function () { document.getElementById('usernameInput').focus(); }, 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user