Merge branch 'feature/vuejs' into feature/vue-serverview
This commit is contained in:
commit
e65854c8c2
35 changed files with 827 additions and 192 deletions
2
resources/assets/scripts/bootstrap.js
vendored
2
resources/assets/scripts/bootstrap.js
vendored
|
@ -17,8 +17,8 @@ try {
|
|||
*/
|
||||
|
||||
window.axios = require('axios');
|
||||
|
||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
||||
window.axios.defaults.headers.common['Accept'] = 'application/json';
|
||||
window.axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.token || '';
|
||||
|
||||
if (typeof phpdebugbar !== 'undefined') {
|
||||
|
|
|
@ -5,13 +5,14 @@
|
|||
>
|
||||
<div class="flex flex-wrap -mx-3 mb-6">
|
||||
<div class="input-open">
|
||||
<input class="input" id="grid-email" type="email" aria-labelledby="grid-email" ref="email" required
|
||||
<input class="input" id="grid-email" type="email" aria-labelledby="grid-email-label" required
|
||||
ref="email"
|
||||
v-bind:class="{ 'has-content': email.length > 0 }"
|
||||
v-bind:readonly="showSpinner"
|
||||
v-bind:value="email"
|
||||
v-on:input="updateEmail($event)"
|
||||
/>
|
||||
<label for="grid-email">{{ $t('strings.email') }}</label>
|
||||
<label for="grid-email" id="grid-email-label">{{ $t('strings.email') }}</label>
|
||||
<p class="text-grey-darker text-xs">{{ $t('auth.forgot_password.label_help') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -25,6 +26,7 @@
|
|||
</div>
|
||||
<div class="pt-6 text-center">
|
||||
<router-link class="text-xs text-grey tracking-wide no-underline uppercase hover:text-grey-dark"
|
||||
aria-label="Go to login"
|
||||
:to="{ name: 'login' }"
|
||||
>
|
||||
{{ $t('auth.go_to_login') }}
|
||||
|
@ -68,6 +70,10 @@
|
|||
email: this.$props.email,
|
||||
})
|
||||
.then(function (response) {
|
||||
if (!(response.data instanceof Object)) {
|
||||
throw new Error('An error was encountered while processing this request.');
|
||||
}
|
||||
|
||||
self.$data.submitDisabled = false;
|
||||
self.$data.showSpinner = false;
|
||||
self.success(response.data.status);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div>
|
||||
<flash/>
|
||||
<flash container="mb-2"/>
|
||||
<login-form
|
||||
v-if="this.$route.name === 'login'"
|
||||
v-bind:user="user"
|
||||
|
|
|
@ -5,29 +5,30 @@
|
|||
>
|
||||
<div class="flex flex-wrap -mx-3 mb-6">
|
||||
<div class="input-open">
|
||||
<input class="input" id="grid-username" type="text" name="user" aria-labelledby="grid-username" required
|
||||
<input class="input" id="grid-username" type="text" name="user" aria-labelledby="grid-username-label" required
|
||||
ref="email"
|
||||
:class="{ 'has-content' : user.email.length > 0 }"
|
||||
:readonly="showSpinner"
|
||||
:value="user.email"
|
||||
v-on:input="updateEmail($event)"
|
||||
/>
|
||||
<label for="grid-username">{{ $t('strings.user_identifier') }}</label>
|
||||
<label id="grid-username-label" for="grid-username">{{ $t('strings.user_identifier') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-wrap -mx-3 mb-6">
|
||||
<div class="input-open">
|
||||
<input class="input" id="grid-password" type="password" name="password" aria-labelledby="grid-password" required
|
||||
<input class="input" id="grid-password" type="password" name="password" aria-labelledby="grid-password-label" required
|
||||
ref="password"
|
||||
:class="{ 'has-content' : user.password && user.password.length > 0 }"
|
||||
:readonly="showSpinner"
|
||||
v-model="user.password"
|
||||
/>
|
||||
<label for="grid-password">{{ $t('strings.password') }}</label>
|
||||
<label id="grid-password-label" for="grid-password">{{ $t('strings.password') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-blue btn-jumbo" type="submit" v-bind:disabled="showSpinner">
|
||||
<button id="grid-login-button" class="btn btn-blue btn-jumbo" type="submit" aria-label="Log in"
|
||||
v-bind:disabled="showSpinner">
|
||||
<span class="spinner white" v-bind:class="{ hidden: ! showSpinner }"> </span>
|
||||
<span v-bind:class="{ hidden: showSpinner }">
|
||||
{{ $t('auth.sign_in') }}
|
||||
|
@ -35,7 +36,7 @@
|
|||
</button>
|
||||
</div>
|
||||
<div class="pt-6 text-center">
|
||||
<router-link class="text-xs text-grey tracking-wide no-underline uppercase hover:text-grey-dark"
|
||||
<router-link class="text-xs text-grey tracking-wide no-underline uppercase hover:text-grey-dark" aria-label="Forgot password"
|
||||
:to="{ name: 'forgot-password' }">
|
||||
{{ $t('auth.forgot_password.label') }}
|
||||
</router-link>
|
||||
|
@ -81,6 +82,12 @@
|
|||
password: this.$props.user.password,
|
||||
})
|
||||
.then(function (response) {
|
||||
// If there is a 302 redirect or some other odd behavior (basically, response that isnt
|
||||
// in JSON format) throw an error and don't try to continue with the login.
|
||||
if (!(response.data instanceof Object)) {
|
||||
throw new Error('An error was encountered while processing this request.');
|
||||
}
|
||||
|
||||
if (response.data.complete) {
|
||||
localStorage.setItem('token', response.data.token);
|
||||
self.$store.dispatch('login');
|
||||
|
@ -94,6 +101,7 @@
|
|||
.catch(function (err) {
|
||||
self.$props.user.password = '';
|
||||
self.$data.showSpinner = false;
|
||||
self.$refs.password.focus();
|
||||
self.$store.dispatch('logout');
|
||||
|
||||
if (!err.response) {
|
||||
|
@ -105,7 +113,6 @@
|
|||
response.data.errors.forEach(function (error) {
|
||||
self.error(error.detail);
|
||||
});
|
||||
self.$refs.password.focus();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
@ -93,6 +93,10 @@
|
|||
token: this.$props.token,
|
||||
})
|
||||
.then(function (response) {
|
||||
if (!(response.data instanceof Object)) {
|
||||
throw new Error('An error was encountered while processing this login.');
|
||||
}
|
||||
|
||||
return window.location = response.data.redirect_to;
|
||||
})
|
||||
.catch(function (err) {
|
||||
|
|
|
@ -37,6 +37,10 @@
|
|||
};
|
||||
},
|
||||
mounted: function () {
|
||||
if ((this.$route.query.token || '').length < 1) {
|
||||
return this.$router.push({ name: 'login' });
|
||||
}
|
||||
|
||||
this.$refs.code.focus();
|
||||
},
|
||||
methods: {
|
||||
|
@ -49,8 +53,13 @@
|
|||
authentication_code: this.$data.code,
|
||||
})
|
||||
.then(function (response) {
|
||||
if (!(response.data instanceof Object)) {
|
||||
throw new Error('An error was encountered while processing this login.');
|
||||
}
|
||||
|
||||
localStorage.setItem('token', response.data.token);
|
||||
self.$store.dispatch('login');
|
||||
|
||||
window.location = response.data.intended;
|
||||
})
|
||||
.catch(function (err) {
|
||||
|
|
42
resources/assets/scripts/components/core/Navigation.vue
Normal file
42
resources/assets/scripts/components/core/Navigation.vue
Normal file
|
@ -0,0 +1,42 @@
|
|||
<template>
|
||||
<div class="nav">
|
||||
<div class="logo">
|
||||
<router-link :to="{ name: 'dashboard' }">
|
||||
Pterodactyl
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="menu">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{ name: 'dashboard' }">
|
||||
<server-icon aria-label="Server dashboard"/>
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link :to="{ name: 'account' }">
|
||||
<user-icon aria-label="Profile management"/>
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<a :href="this.route('admin.index')">
|
||||
<settings-icon aria-label="Administrative controls"/>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a :href="this.route('auth.logout')">
|
||||
<log-out-icon aria-label="Sign out"/>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LogOutIcon, ServerIcon, SettingsIcon, UserIcon } from 'vue-feather-icons'
|
||||
|
||||
export default {
|
||||
name: 'navigation',
|
||||
components: { LogOutIcon, ServerIcon, SettingsIcon, UserIcon }
|
||||
};
|
||||
</script>
|
|
@ -1,86 +1,71 @@
|
|||
<template>
|
||||
<div>
|
||||
<flash container="mt-4"/>
|
||||
<div class="server-search animate fadein">
|
||||
<input type="text"
|
||||
:placeholder="$t('dashboard.index.search')"
|
||||
@input="onChange"
|
||||
v-model="search"
|
||||
ref="search"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="this.loading" class="my-4 animate fadein">
|
||||
<div class="text-center h-16">
|
||||
<span class="spinner spinner-xl"></span>
|
||||
<navigation/>
|
||||
<div class="container">
|
||||
<flash container="mt-4"/>
|
||||
<div class="server-search animate fadein">
|
||||
<input type="text"
|
||||
:placeholder="$t('dashboard.index.search')"
|
||||
@input="onChange"
|
||||
v-model="search"
|
||||
ref="search"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<transition-group class="w-full m-auto mt-4 animate fadein sm:flex flex-wrap content-start" v-else>
|
||||
<div class="server-box animate fadein" :key="index" v-for="(server, index) in servers.models">
|
||||
<router-link :to="{ name: 'server', params: { id: server.identifier }}" class="content">
|
||||
<div class="float-right">
|
||||
<div class="indicator"></div>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<div class="text-black font-bold text-xl">
|
||||
{{ server.name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-0 flex">
|
||||
<div class="usage">
|
||||
<div class="indicator-title">{{ $t('dashboard.index.cpu_title') }}</div>
|
||||
</div>
|
||||
<div class="usage">
|
||||
<div class="indicator-title">{{ $t('dashboard.index.memory_title') }}</div>
|
||||
</div>
|
||||
<div class="mb-0 flex">
|
||||
<div class="usage">
|
||||
<div class="indicator-title">CPU</div>
|
||||
</div>
|
||||
<div class="usage">
|
||||
<div class="indicator-title">Memory</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-4 flex text-center">
|
||||
<div class="inline-block border border-grey-lighter border-l-0 p-4 flex-1">
|
||||
<span class="font-bold text-xl">---</span>
|
||||
<span class="font-light text-sm">%</span>
|
||||
</div>
|
||||
<div class="inline-block border border-grey-lighter border-l-0 border-r-0 p-4 flex-1">
|
||||
<span class="font-bold text-xl">---</span>
|
||||
<span class="font-light text-sm">Mb</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="text-sm">
|
||||
<p class="text-grey">{{ server.node }}</p>
|
||||
<p class="text-grey-dark">{{ server.allocation.ip }}:{{ server.allocation.port }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</router-link>
|
||||
<div v-if="this.loading" class="my-4 animate fadein">
|
||||
<div class="text-center h-16">
|
||||
<span class="spinner spinner-xl"></span>
|
||||
</div>
|
||||
</div>
|
||||
</transition-group>
|
||||
<transition-group class="w-full m-auto mt-4 animate fadein sm:flex flex-wrap content-start" v-else>
|
||||
<server-box
|
||||
v-for="(server, index) in servers.models"
|
||||
v-bind:key="index"
|
||||
v-bind:server="server"
|
||||
/>
|
||||
</transition-group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { DateTime } from 'luxon';
|
||||
import { ServerCollection } from '../../models/server';
|
||||
import _ from 'lodash';
|
||||
import Flash from '../Flash';
|
||||
import ServerBox from './ServerBox';
|
||||
import Navigation from '../core/Navigation';
|
||||
|
||||
export default {
|
||||
name: 'dashboard',
|
||||
components: { Flash },
|
||||
components: { Navigation, ServerBox, Flash },
|
||||
data: function () {
|
||||
return {
|
||||
backgroundedAt: DateTime.local(),
|
||||
documentVisible: true,
|
||||
loading: true,
|
||||
search: '',
|
||||
servers: new ServerCollection,
|
||||
}
|
||||
},
|
||||
|
||||
mounted: function () {
|
||||
/**
|
||||
* Start loading the servers before the DOM $.el is created.
|
||||
*/
|
||||
created: function () {
|
||||
this.loadServers();
|
||||
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
this.documentVisible = document.visibilityState === 'visible';
|
||||
this._handleDocumentVisibilityChange(this.documentVisible);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Once the page is mounted set a function to run every 5 seconds that will
|
||||
* iterate through the visible servers and fetch their resource usage.
|
||||
*/
|
||||
mounted: function () {
|
||||
this._iterateServerResourceUse();
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -100,7 +85,9 @@
|
|||
.then(response => {
|
||||
this.servers = new ServerCollection;
|
||||
response.data.data.forEach(obj => {
|
||||
this.servers.add(obj.attributes);
|
||||
this.getResourceUse(
|
||||
this.servers.add(obj.attributes)
|
||||
);
|
||||
});
|
||||
|
||||
if (this.servers.models.length === 0) {
|
||||
|
@ -128,6 +115,56 @@
|
|||
onChange: _.debounce(function () {
|
||||
this.loadServers(this.$data.search);
|
||||
}, 500),
|
||||
|
||||
/**
|
||||
* Get resource usage for an individual server for rendering purposes.
|
||||
*
|
||||
* @param {Server} server
|
||||
*/
|
||||
getResourceUse: function (server) {
|
||||
window.axios.get(this.route('api.client.servers.resources', { server: server.identifier }))
|
||||
.then(response => {
|
||||
if (!(response.data instanceof Object)) {
|
||||
throw new Error('Received an invalid response object back from status endpoint.');
|
||||
}
|
||||
|
||||
window.events.$emit(`server:${server.uuid}::resources`, response.data.attributes);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Iterates over all of the active servers and gets their resource usage.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_iterateServerResourceUse: function (initialTimeout = 5000) {
|
||||
window.setTimeout(() => {
|
||||
if (this.documentVisible) {
|
||||
return window.setTimeout(this._iterateServerResourceUse(), 5000);
|
||||
}
|
||||
}, initialTimeout);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle changes to document visibilty to keep server statuses updated properly.
|
||||
*
|
||||
* @param {Boolean} isVisible
|
||||
* @private
|
||||
*/
|
||||
_handleDocumentVisibilityChange: function (isVisible) {
|
||||
if (!isVisible) {
|
||||
this.backgroundedAt = DateTime.local();
|
||||
return;
|
||||
}
|
||||
|
||||
// If it has been more than 30 seconds since this window was put into the background
|
||||
// lets go ahead and refresh all of the listed servers so that they have fresh stats.
|
||||
const diff = DateTime.local().diff(this.backgroundedAt, 'seconds');
|
||||
this._iterateServerResourceUse(diff.seconds > 30 ? 1 : 5000);
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
113
resources/assets/scripts/components/dashboard/ServerBox.vue
Normal file
113
resources/assets/scripts/components/dashboard/ServerBox.vue
Normal file
|
@ -0,0 +1,113 @@
|
|||
<template>
|
||||
<div class="server-box animate fadein">
|
||||
<router-link :to="{ name: 'server', params: { id: server.identifier }}" class="content">
|
||||
<div class="float-right">
|
||||
<div class="indicator" :class="status"></div>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<div class="text-black font-bold text-xl">
|
||||
{{ server.name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-0 flex">
|
||||
<div class="usage">
|
||||
<div class="indicator-title">{{ $t('dashboard.index.cpu_title') }}</div>
|
||||
</div>
|
||||
<div class="usage">
|
||||
<div class="indicator-title">{{ $t('dashboard.index.memory_title') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-4 flex text-center">
|
||||
<div class="inline-block border border-grey-lighter border-l-0 p-4 flex-1">
|
||||
<span class="font-bold text-xl">{{ cpu > 0 ? cpu : '—' }}</span>
|
||||
<span class="font-light text-sm">%</span>
|
||||
</div>
|
||||
<div class="inline-block border border-grey-lighter border-l-0 border-r-0 p-4 flex-1">
|
||||
<span class="font-bold text-xl">{{ memory > 0 ? memory : '—' }}</span>
|
||||
<span class="font-light text-sm">MB</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="text-sm">
|
||||
<p class="text-grey">{{ server.node }}</p>
|
||||
<p class="text-grey-dark">{{ server.allocation.ip }}:{{ server.allocation.port }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import get from 'lodash/get';
|
||||
|
||||
export default {
|
||||
name: 'server-box',
|
||||
props: {
|
||||
server: { type: Object, required: true },
|
||||
},
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
resources: undefined,
|
||||
cpu: 0,
|
||||
memory: 0,
|
||||
status: '',
|
||||
};
|
||||
},
|
||||
|
||||
created: function () {
|
||||
window.events.$on(`server:${this.server.uuid}::resources`, data => {
|
||||
this.resources = data;
|
||||
this.status = this.getServerStatus();
|
||||
|
||||
this.memory = Number(get(data, 'memory.current', 0)).toFixed(0);
|
||||
this.cpu = this._calculateCpu(
|
||||
Number(get(data, 'cpu.current', 0)),
|
||||
Number(this.server.limits.cpu)
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Set the CSS to use for displaying the server's current status.
|
||||
*/
|
||||
getServerStatus: function () {
|
||||
if (!(this.resources instanceof Object)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!this.resources.installed || this.resources.suspended) {
|
||||
return '';
|
||||
}
|
||||
|
||||
switch (this.resources.state) {
|
||||
case 'off':
|
||||
return 'offline';
|
||||
case 'on':
|
||||
case 'starting':
|
||||
case 'stopping':
|
||||
return 'online';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculate the CPU usage for a given server relative to their set maximum.
|
||||
*
|
||||
* @param {Number} current
|
||||
* @param {Number} max
|
||||
* @return {Number}
|
||||
* @private
|
||||
*/
|
||||
_calculateCpu: function (current, max) {
|
||||
if (max === 0) {
|
||||
return parseFloat(current.toFixed(1));
|
||||
}
|
||||
|
||||
return parseFloat((current / max * 100).toFixed(1));
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -17,7 +17,7 @@ import {
|
|||
export const routes = [
|
||||
{ name: 'login', path: '/auth/login', component: Login },
|
||||
{ name: 'forgot-password', path: '/auth/password', component: Login },
|
||||
{ name: 'checkpoint', path: '/checkpoint', component: Login },
|
||||
{ name: 'checkpoint', path: '/auth/checkpoint', component: Login },
|
||||
{
|
||||
name: 'reset-password',
|
||||
path: '/auth/password/reset/:token',
|
||||
|
@ -27,7 +27,7 @@ export const routes = [
|
|||
}
|
||||
},
|
||||
|
||||
{ name : 'index', path: '/', component: Dashboard },
|
||||
{ name : 'dashboard', path: '/', component: Dashboard },
|
||||
{ name : 'account', path: '/account', component: Account },
|
||||
{ name : 'account.api', path: '/account/api', component: Account },
|
||||
{ name : 'account.security', path: '/account/security', component: Account },
|
||||
|
|
|
@ -49,11 +49,7 @@ code {
|
|||
@apply .block .pb-4 .no-underline;
|
||||
|
||||
@screen smx {
|
||||
@apply .w-1/2 .pr-4;
|
||||
|
||||
&:nth-of-type(2n) {
|
||||
padding-right: 0;
|
||||
}
|
||||
@apply .w-full;
|
||||
}
|
||||
|
||||
@screen md {
|
||||
|
|
|
@ -24,11 +24,18 @@
|
|||
|
||||
& > .logo {
|
||||
@apply .mx-8 .font-sans .font-thin .text-2xl .text-white .inline-block .pt-2;
|
||||
|
||||
& a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@screen xsx {
|
||||
@apply .hidden
|
||||
}
|
||||
}
|
||||
|
||||
& > .menu {
|
||||
@apply .float-right .mx-8 .inline-block;
|
||||
|
||||
& > ul {
|
||||
@apply .list-reset;
|
||||
& > li {
|
||||
|
@ -39,11 +46,22 @@
|
|||
|
||||
&:hover {
|
||||
@apply .bg-blue-dark;
|
||||
}
|
||||
|
||||
& .feather {
|
||||
@apply .h-4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@screen xsx {
|
||||
@apply .w-full .text-center;
|
||||
}
|
||||
|
||||
@screen sm {
|
||||
@apply .float-right .mx-8 .inline-block;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
|
|
|
@ -26,5 +26,11 @@
|
|||
* Assorted Other CSS
|
||||
*/
|
||||
body {
|
||||
@apply .font-sans;
|
||||
@apply .font-sans;
|
||||
}
|
||||
|
||||
.container {
|
||||
@screen xsx {
|
||||
@apply .px-2;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,58 +1,12 @@
|
|||
@extends('templates/wrapper')
|
||||
|
||||
@section('above-container')
|
||||
<header class="bg-blue text-white text-xl rounded-b fixed pin-t w-full z-40 shadow-md">
|
||||
<div class="container h-16 mx-auto flex">
|
||||
<img class="h-12 mt-2 mr-3" src="/assets/img/pterodactyl-flat.svg">
|
||||
<div class="py-6">PTERODACTYL</div>
|
||||
<div class="flex-grow"></div>
|
||||
<nav class="nav text-lg">
|
||||
<router-link to="/"><font-awesome-icon class="mr-2" fixed-with icon="server"></font-awesome-icon>Servers</router-link>
|
||||
<a href="#"><font-awesome-icon class="mr-2" fixed-with icon="cogs"></font-awesome-icon>Admin</a>
|
||||
<a href="#"><font-awesome-icon class="mr-2" fixed-with icon="user"></font-awesome-icon>schrej</a>
|
||||
<a href="#"><font-awesome-icon fixed-with icon="sign-out-alt"></font-awesome-icon></a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
<div class="h-16 mb-6"></div>
|
||||
{{--<div class="nav">
|
||||
<div class="logo">
|
||||
Pterodactyl
|
||||
</div>
|
||||
<div class="menu">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link to="/">
|
||||
<span>Your Servers</span>
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span>Admin</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">
|
||||
<span>dane</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('auth.logout') }}">
|
||||
<span>Logout</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>--}}
|
||||
@endsection
|
||||
|
||||
@section('container')
|
||||
<router-view></router-view>
|
||||
@endsection
|
||||
|
||||
@section('below-container')
|
||||
<div class="flex-grow"></div>
|
||||
<div class="container py-4">
|
||||
<div class="w-full m-auto mt-0 container">
|
||||
<p class="text-right text-grey-dark text-xs">
|
||||
{!! trans('strings.copyright', ['year' => date('Y')]) !!}
|
||||
</p>
|
||||
|
|
|
@ -24,14 +24,14 @@
|
|||
|
||||
@include('layouts.scripts')
|
||||
</head>
|
||||
<body class="{{ $css['body'] ?? 'bg-white' }}">
|
||||
<div id="pterodactyl" class="flex flex-col min-h-screen">
|
||||
<body class="{{ $css['body'] ?? 'bg-grey-lighter' }}">
|
||||
@section('content')
|
||||
@yield('above-container')
|
||||
<div class="container">
|
||||
<div id="pterodactyl" class="flex flex-col min-h-screen">
|
||||
@yield('container')
|
||||
</div>
|
||||
@yield('below-container')
|
||||
</div>
|
||||
@show
|
||||
@section('scripts')
|
||||
{!! $asset->js('assets/scripts/bundle.js') !!}
|
||||
@show
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue