forked from geolba/tethys.backend
- add AvatarController.ts
- adapted menu.ts, NavBar.vue, NavBarItem.vue for highlighting active nav item - NavBarItemLabel.vue for app menu highlighting - adapted routes.ts - adapted app.edge for new favicon - adapted LayoutAuthenticated.vue (:showAsideMenu="false") for showing AsideMenu optional - new material icons: BriefcaseCheck.vue, SwapHorizontal.vue, AccountGroup.vue, Lock.vue - started with FirstRunWizard
This commit is contained in:
parent
ae0c471e93
commit
cefd9081ae
65
app/Controllers/Http/Api/AvatarController.ts
Normal file
65
app/Controllers/Http/Api/AvatarController.ts
Normal file
|
@ -0,0 +1,65 @@
|
|||
import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||
import { StatusCodes } from 'http-status-codes';
|
||||
// import * as fs from 'fs';
|
||||
// import * as path from 'path';
|
||||
|
||||
const prefixes = ['von', 'van'];
|
||||
|
||||
// node ace make:controller Author
|
||||
export default class AvatarController {
|
||||
public async generateAvatar({ request, response }: HttpContextContract) {
|
||||
try {
|
||||
const { name, background, textColor, size } = request.only(['name', 'background', 'textColor', 'size']);
|
||||
|
||||
// Generate initials
|
||||
// const initials = name
|
||||
// .split(' ')
|
||||
// .map((part) => part.charAt(0).toUpperCase())
|
||||
// .join('');
|
||||
const initials = this.getInitials(name);
|
||||
|
||||
// Define SVG content with dynamic values for initials, background color, text color, and size
|
||||
const svgContent = `
|
||||
<svg width="${size || 50}" height="${size || 50}" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="100%" height="100%" fill="#${background || '7F9CF5'}"/>
|
||||
<text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" font-weight="bold" font-family="Arial, sans-serif" font-size="${
|
||||
(size / 100) * 40 || 25
|
||||
}" fill="#${textColor || 'ffffff'}">${initials}</text>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
// Set response headers for SVG content
|
||||
response.header('Content-type', 'image/svg+xml');
|
||||
response.header('Cache-Control', 'no-cache');
|
||||
response.header('Pragma', 'no-cache');
|
||||
response.header('Expires', '0');
|
||||
|
||||
return response.send(svgContent);
|
||||
} catch (error) {
|
||||
return response.status(StatusCodes.OK).json({ error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
private getInitials(name) {
|
||||
const parts = name.split(' ');
|
||||
let initials = '';
|
||||
|
||||
if (parts.length >= 2) {
|
||||
const firstName = parts[0];
|
||||
const lastName = parts[parts.length - 1];
|
||||
|
||||
const firstInitial = firstName.charAt(0).toUpperCase();
|
||||
const lastInitial = lastName.charAt(0).toUpperCase();
|
||||
|
||||
if (prefixes.includes(lastName.toLowerCase()) && lastName === lastName.toUpperCase()) {
|
||||
initials = firstInitial + lastName.charAt(1).toUpperCase();
|
||||
} else {
|
||||
initials = firstInitial + lastInitial;
|
||||
}
|
||||
} else if (parts.length === 1) {
|
||||
initials = parts[0].substring(0, 2).toUpperCase();
|
||||
}
|
||||
|
||||
return initials;
|
||||
}
|
||||
}
|
BIN
public/favicon.png
Normal file
BIN
public/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
96
resources/js/Components/FirstrunWizard/components/Card.vue
Normal file
96
resources/js/Components/FirstrunWizard/components/Card.vue
Normal file
|
@ -0,0 +1,96 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2023 Marco Ambrosini <marcoambrosini@proton.me>
|
||||
-
|
||||
- @author Simon Lindner <szaimen@e.mail.de>
|
||||
- @author Marco Ambrosini <marcoambrosini@proton.me>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
-->
|
||||
<template>
|
||||
<element :is="isLink ? 'a' : 'div'" :href="href || undefined" class="card" :class="{ 'card--link': isLink }"
|
||||
:target="!isLink ? undefined : '_blank'" :rel="!isLink ? undefined : 'noreferrer'">
|
||||
<div v-if="!isLink" class="card__icon">
|
||||
<slot />
|
||||
</div>
|
||||
<div class="card__text">
|
||||
<h3 class="card__heading">
|
||||
{{ title }}
|
||||
</h3>
|
||||
<p>{{ subtitle }}</p>
|
||||
</div>
|
||||
</element>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Card',
|
||||
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
||||
href: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
|
||||
subtitle: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
isLink() {
|
||||
return this.href !== ''
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.card {
|
||||
display: flex;
|
||||
max-width: 250px;
|
||||
box-sizing: border-box;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.card__icon {
|
||||
display: flex;
|
||||
flex: 0 0 44px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.card__heading {
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.card--link {
|
||||
box-shadow: 0px 0px 10px 0px var(--color-box-shadow);
|
||||
border-radius: var(--border-radius-large);
|
||||
padding: calc(var(--default-grid-baseline) * 4);
|
||||
}
|
||||
|
||||
.card--link:focus-visible {
|
||||
outline: 2px solid var(--color-main-text);
|
||||
box-shadow: 0 0 0 4px var(--color-main-background);
|
||||
}
|
||||
</style>
|
56
resources/js/Components/FirstrunWizard/components/Page1.vue
Normal file
56
resources/js/Components/FirstrunWizard/components/Page1.vue
Normal file
|
@ -0,0 +1,56 @@
|
|||
<template>
|
||||
<div class="page__wrapper">
|
||||
<div class="page__scroller first-page">
|
||||
<h2 class="page__heading">
|
||||
{{ t('firstrunwizard', 'A collaboration platform that puts you in control') }}
|
||||
</h2>
|
||||
<div class="page__content">
|
||||
<Card :title="t('firstrunwizard', 'Privacy')"
|
||||
:subtitle="t('firstrunwizard', 'Host your data and files where you decide.')">
|
||||
<Lock :size="20" />
|
||||
</Card>
|
||||
<Card :title="t('firstrunwizard', 'Productivity')"
|
||||
:subtitle="t('firstrunwizard', 'Collaborate and communicate across any platform.')">
|
||||
<BriefcaseCheck :size="20" />
|
||||
</Card>
|
||||
<Card :title="t('firstrunwizard', 'Interoperability')"
|
||||
:subtitle="t('firstrunwizard', 'Import and export anything you want with open standards.')">
|
||||
<SwapHorizontal :size="20" />
|
||||
</Card>
|
||||
<Card :title="t('firstrunwizard', 'Community')"
|
||||
:subtitle="t('firstrunwizard', 'Enjoy constant improvements from a thriving open-source community.')">
|
||||
<AccountGroup :size="20" />
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Card from './Card.vue'
|
||||
|
||||
import Lock from 'vue-material-design-icons/Lock.vue'
|
||||
import BriefcaseCheck from 'vue-material-design-icons/BriefcaseCheck.vue'
|
||||
import SwapHorizontal from 'vue-material-design-icons/SwapHorizontal.vue'
|
||||
import AccountGroup from 'vue-material-design-icons/AccountGroup.vue'
|
||||
|
||||
export default {
|
||||
name: 'Page1',
|
||||
|
||||
components: {
|
||||
Card,
|
||||
Lock,
|
||||
BriefcaseCheck,
|
||||
SwapHorizontal,
|
||||
AccountGroup,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
/* @import "pageStyles"; */
|
||||
|
||||
.first-page {
|
||||
margin-top: 100px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,31 @@
|
|||
.page {
|
||||
&__wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
min-height: min(520px, 50vh);
|
||||
}
|
||||
|
||||
&__scroller {
|
||||
overflow-y: scroll;
|
||||
margin-top: calc(var(--default-grid-baseline) * 8);
|
||||
}
|
||||
|
||||
&__heading {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__subtitle {
|
||||
max-width: 450px;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: calc(var(--default-grid-baseline) * 6);
|
||||
justify-content: center;
|
||||
margin: calc(var(--default-grid-baseline) * 10) 0;
|
||||
}
|
||||
}
|
31
resources/js/Components/Icons/AccountGroup.vue
Normal file
31
resources/js/Components/Icons/AccountGroup.vue
Normal file
|
@ -0,0 +1,31 @@
|
|||
<template>
|
||||
<span v-bind="$attrs" :aria-hidden="!title" :aria-label="title" class="material-design-icon account-group-icon"
|
||||
role="img" @click="$emit('click', $event)">
|
||||
<svg :fill="fillColor" class="material-design-icon__svg" :width="size" :height="size" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M12,5.5A3.5,3.5 0 0,1 15.5,9A3.5,3.5 0 0,1 12,12.5A3.5,3.5 0 0,1 8.5,9A3.5,3.5 0 0,1 12,5.5M5,8C5.56,8 6.08,8.15 6.53,8.42C6.38,9.85 6.8,11.27 7.66,12.38C7.16,13.34 6.16,14 5,14A3,3 0 0,1 2,11A3,3 0 0,1 5,8M19,8A3,3 0 0,1 22,11A3,3 0 0,1 19,14C17.84,14 16.84,13.34 16.34,12.38C17.2,11.27 17.62,9.85 17.47,8.42C17.92,8.15 18.44,8 19,8M5.5,18.25C5.5,16.18 8.41,14.5 12,14.5C15.59,14.5 18.5,16.18 18.5,18.25V20H5.5V18.25M0,20V18.5C0,17.11 1.89,15.94 4.45,15.6C3.86,16.28 3.5,17.22 3.5,18.25V20H0M24,20H20.5V18.25C20.5,17.22 20.14,16.28 19.55,15.6C22.11,15.94 24,17.11 24,18.5V20Z">
|
||||
<title v-if="title">{{ title }}</title>
|
||||
</path>
|
||||
</svg>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "AccountGroupIcon",
|
||||
emits: ['click'],
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
},
|
||||
fillColor: {
|
||||
type: String,
|
||||
default: "currentColor"
|
||||
},
|
||||
size: {
|
||||
type: Number,
|
||||
default: 24
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
31
resources/js/Components/Icons/BriefcaseCheck.vue
Normal file
31
resources/js/Components/Icons/BriefcaseCheck.vue
Normal file
|
@ -0,0 +1,31 @@
|
|||
<template>
|
||||
<span v-bind="$attrs" :aria-hidden="!title" :aria-label="title" class="material-design-icon briefcase-check-icon"
|
||||
role="img" @click="$emit('click', $event)">
|
||||
<svg :fill="fillColor" class="material-design-icon__svg" :width="size" :height="size" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M10,2H14A2,2 0 0,1 16,4V6H20A2,2 0 0,1 22,8V19A2,2 0 0,1 20,21H4A2,2 0 0,1 2,19V8A2,2 0 0,1 4,6H8V4A2,2 0 0,1 10,2M14,6V4H10V6H14M10.5,17.5L17.09,10.91L15.68,9.5L10.5,14.67L8.41,12.59L7,14L10.5,17.5Z">
|
||||
<title v-if="title">{{ title }}</title>
|
||||
</path>
|
||||
</svg>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "BriefcaseCheckIcon",
|
||||
emits: ['click'],
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
},
|
||||
fillColor: {
|
||||
type: String,
|
||||
default: "currentColor"
|
||||
},
|
||||
size: {
|
||||
type: Number,
|
||||
default: 24
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
31
resources/js/Components/Icons/Lock.vue
Normal file
31
resources/js/Components/Icons/Lock.vue
Normal file
|
@ -0,0 +1,31 @@
|
|||
<template>
|
||||
<span v-bind="$attrs" :aria-hidden="!title" :aria-label="title" class="material-design-icon lock-icon" role="img"
|
||||
@click="$emit('click', $event)">
|
||||
<svg :fill="fillColor" class="material-design-icon__svg" :width="size" :height="size" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M12,17A2,2 0 0,0 14,15C14,13.89 13.1,13 12,13A2,2 0 0,0 10,15A2,2 0 0,0 12,17M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V10C4,8.89 4.9,8 6,8H7V6A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,3A3,3 0 0,0 9,6V8H15V6A3,3 0 0,0 12,3Z">
|
||||
<title v-if="title">{{ title }}</title>
|
||||
</path>
|
||||
</svg>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "LockIcon",
|
||||
emits: ['click'],
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
},
|
||||
fillColor: {
|
||||
type: String,
|
||||
default: "currentColor"
|
||||
},
|
||||
size: {
|
||||
type: Number,
|
||||
default: 24
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
30
resources/js/Components/Icons/SwapHorizontal.vue
Normal file
30
resources/js/Components/Icons/SwapHorizontal.vue
Normal file
|
@ -0,0 +1,30 @@
|
|||
<template>
|
||||
<span v-bind="$attrs" :aria-hidden="!title" :aria-label="title" class="material-design-icon swap-horizontal-icon"
|
||||
role="img" @click="$emit('click', $event)">
|
||||
<svg :fill="fillColor" class="material-design-icon__svg" :width="size" :height="size" viewBox="0 0 24 24">
|
||||
<path d="M21,9L17,5V8H10V10H17V13M7,11L3,15L7,19V16H14V14H7V11Z">
|
||||
<title v-if="title">{{ title }}</title>
|
||||
</path>
|
||||
</svg>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "SwapHorizontalIcon",
|
||||
emits: ['click'],
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
},
|
||||
fillColor: {
|
||||
type: String,
|
||||
default: "currentColor"
|
||||
},
|
||||
size: {
|
||||
type: Number,
|
||||
default: 24
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -19,12 +19,15 @@ import {
|
|||
mdiCloudDownloadOutline,
|
||||
mdiCloud,
|
||||
mdiCrop,
|
||||
mdiAccount,
|
||||
mdiCogOutline,
|
||||
mdiEmail,
|
||||
mdiAccountCog,
|
||||
mdiFormatListGroup ,
|
||||
mdiFormatListNumbered,
|
||||
// mdiEmail,
|
||||
mdiLogout,
|
||||
mdiGithub,
|
||||
mdiThemeLightDark,
|
||||
mdiViewDashboard,
|
||||
mdiMapSearch
|
||||
} from '@mdi/js';
|
||||
import NavBarItem from '@/Components/NavBarItem.vue';
|
||||
import NavBarItemLabel from '@/Components/NavBarItemLabel.vue';
|
||||
|
@ -32,20 +35,25 @@ import NavBarMenu from '@/Components/NavBarMenu.vue';
|
|||
import BaseDivider from '@/Components/BaseDivider.vue';
|
||||
import UserAvatarCurrentUser from '@/Components/UserAvatarCurrentUser.vue';
|
||||
import BaseIcon from '@/Components/BaseIcon.vue';
|
||||
import NavBarSearch from '@/Components/NavBarSearch.vue';
|
||||
// import NavBarSearch from '@/Components/NavBarSearch.vue';
|
||||
import { stardust } from '@eidellev/adonis-stardust/client';
|
||||
import type { User } from '@/Dataset';
|
||||
// import FirstrunWizard from '@/Components/FirstrunWizard/FirstrunWizard.vue'
|
||||
import Lock from 'vue-material-design-icons/Lock.vue'
|
||||
// import BriefcaseCheck from 'vue-material-design-icons/BriefcaseCheck.vue'
|
||||
// import SwapHorizontal from 'vue-material-design-icons/SwapHorizontal.vue'
|
||||
// import AccountGroup from 'vue-material-design-icons/AccountGroup.vue'
|
||||
|
||||
// const mainStore = MainService();
|
||||
// const userName = computed(() =>mainStore.userName);
|
||||
|
||||
const styleService = StyleService();
|
||||
// const props = defineProps({
|
||||
// user: {
|
||||
// type: Object,
|
||||
// default: () => ({}),
|
||||
// }
|
||||
// });
|
||||
const props = defineProps({
|
||||
showBurger: {
|
||||
type: Boolean,
|
||||
default: true // Set default value to true
|
||||
}
|
||||
});
|
||||
// const userName = computed(() => usePage().props.user.login)
|
||||
|
||||
const user: ComputedRef<User> = computed(() => {
|
||||
|
@ -80,32 +88,41 @@ const logout = async () => {
|
|||
// router.post(route('logout'));
|
||||
await router.post(stardust.route('logout'));
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav class="top-0 left-0 right-0 fixed bg-gray-50 h-14 z-40 w-screen transition-position xl:pl-60 lg:w-auto dark:bg-slate-800">
|
||||
<nav class="text-base top-0 left-0 right-0 fixed bg-gray-50 h-14 z-40 w-screen transition-position lg:w-auto dark:bg-slate-800"
|
||||
:class="{ 'xl:pl-60': props.showBurger == true }">
|
||||
<div class="flex lg:items-stretch" :class="containerMaxW">
|
||||
<div class="flex-1 items-stretch flex h-14">
|
||||
<NavBarItem type="flex lg:hidden" @click.prevent="layoutStore.asideMobileToggle()">
|
||||
<NavBarItem type="flex lg:hidden" @click.prevent="layoutStore.asideMobileToggle()" v-if="props.showBurger">
|
||||
<BaseIcon :path="layoutStore.isAsideMobileExpanded ? mdiBackburger : mdiForwardburger" size="24" />
|
||||
</NavBarItem>
|
||||
<NavBarItem type="hidden lg:flex xl:hidden" @click.prevent="menuOpenLg">
|
||||
<NavBarItem type="hidden lg:flex xl:hidden" @click.prevent="menuOpenLg" v-if="props.showBurger">
|
||||
<BaseIcon :path="mdiMenu" size="24" />
|
||||
</NavBarItem>
|
||||
<NavBarItem>
|
||||
<NavBarSearch />
|
||||
<NavBarItem route-name="apps.dashboard">
|
||||
<NavBarItemLabel :icon="mdiViewDashboard" label="Dashboard" size="22" is-hover-label-only route-name="apps.dashboard" />
|
||||
</NavBarItem>
|
||||
<NavBarItem route-name="apps.map">
|
||||
<NavBarItemLabel :icon="mdiMapSearch" label="Map" size="22" is-hover-label-only route-name="apps.map" />
|
||||
</NavBarItem>
|
||||
<!-- <NavBarItem>
|
||||
<NavBarSearch />
|
||||
</NavBarItem> -->
|
||||
</div>
|
||||
<div class="flex-none items-stretch flex h-14 lg:hidden">
|
||||
<NavBarItem @click.prevent="menuNavBarToggle">
|
||||
<BaseIcon :path="isMenuNavBarActive ? mdiClose : mdiDotsVertical" size="24" />
|
||||
</NavBarItem>
|
||||
</div>
|
||||
<div class="absolute w-screen top-14 left-0 bg-gray-50 shadow lg:w-auto lg:items-stretch lg:flex lg:grow lg:static lg:border-b-0 lg:overflow-visible lg:shadow-none dark:bg-slate-800"
|
||||
:class="[isMenuNavBarActive ? 'block' : 'hidden']">
|
||||
<div
|
||||
class="absolute w-screen top-14 left-0 bg-gray-50 shadow lg:w-auto lg:items-stretch lg:flex lg:grow lg:static lg:border-b-0 lg:overflow-visible lg:shadow-none dark:bg-slate-800"
|
||||
:class="[isMenuNavBarActive ? 'block' : 'hidden']"
|
||||
>
|
||||
<div class="max-h-screen-menu overflow-y-auto lg:overflow-visible lg:flex lg:items-stretch lg:justify-end lg:ml-auto">
|
||||
class="max-h-screen-menu overflow-y-auto lg:overflow-visible lg:flex lg:items-stretch lg:justify-end lg:ml-auto">
|
||||
|
||||
<!-- help menu -->
|
||||
<NavBarMenu>
|
||||
<NavBarItemLabel :icon="mdiMenu" label="Help menu" />
|
||||
<template #dropdown>
|
||||
|
@ -125,23 +142,29 @@ const logout = async () => {
|
|||
</template>
|
||||
</NavBarMenu>
|
||||
|
||||
<!-- personal menu -->
|
||||
<NavBarMenu>
|
||||
<NavBarItemLabel v-bind:label="user.login">
|
||||
<NavBarItemLabel v-bind:label="`hello ${user.login}`">
|
||||
<UserAvatarCurrentUser class="w-6 h-6 mr-3 inline-flex" />
|
||||
</NavBarItemLabel>
|
||||
|
||||
<template #dropdown>
|
||||
<!-- <NavBarItem> -->
|
||||
<!-- <NavBarItem route-name="admin.account.info"> -->
|
||||
<NavBarItem :route-name="'settings.user'">
|
||||
<NavBarItemLabel :icon="mdiAccount" label="My Profile" />
|
||||
<NavBarItemLabel :icon="mdiAccountCog" label="User Settings" />
|
||||
</NavBarItem>
|
||||
<NavBarItem v-if="userHasRoles(['moderator', 'administrator'])" :route-name="'settings'">
|
||||
<NavBarItemLabel :icon="mdiCogOutline" label="Settings" />
|
||||
<NavBarItem v-if="userHasRoles(['administrator'])" :route-name="'settings.overview'">
|
||||
<NavBarItemLabel :icon="mdiFormatListGroup" label="Administration" />
|
||||
</NavBarItem>
|
||||
<NavBarItem>
|
||||
<NavBarItem v-if="userHasRoles(['submitter'])" :route-name="'dataset.list'">
|
||||
<NavBarItemLabel :icon="mdiFormatListNumbered" label="Submitter Setup" />
|
||||
</NavBarItem>
|
||||
<NavBarItem v-if="userHasRoles(['editor'])" :route-name="'editor.dataset.list'">
|
||||
<NavBarItemLabel :icon="mdiFormatListNumbered" label="Editor Setup" />
|
||||
</NavBarItem>
|
||||
<!-- <NavBarItem>
|
||||
<NavBarItemLabel :icon="mdiEmail" label="Messages" />
|
||||
</NavBarItem>
|
||||
</NavBarItem> -->
|
||||
<BaseDivider nav-bar />
|
||||
<NavBarItem @click="logout">
|
||||
<NavBarItemLabel :icon="mdiLogout" label="Log Out" />
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<script setup>
|
||||
<script lang="ts" setup>
|
||||
import { StyleService } from '@/Stores/style';
|
||||
// import { Link } from '@inertiajs/vue3'
|
||||
import { Link } from '@inertiajs/vue3';
|
||||
|
@ -28,6 +28,10 @@ const props = defineProps({
|
|||
isDesktopIconOnly: Boolean,
|
||||
dropdown: Boolean,
|
||||
active: Boolean,
|
||||
underline: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const is = computed(() => {
|
||||
|
@ -44,20 +48,50 @@ const is = computed(() => {
|
|||
|
||||
const styleStore = StyleService();
|
||||
|
||||
const activeColor = props.activeColor ?? `${styleStore.navBarItemLabelActiveColorStyle} dark:text-slate-400`;
|
||||
// const activeColor = props.activeColor ?? `${styleStore.navBarItemLabelActiveColorStyle} dark:text-slate-400`;
|
||||
|
||||
const activeClass = computed(
|
||||
// () => props.routeName && route().current(props.routeName) == true ? props.activeColor : null
|
||||
() => (props.routeName && stardust.isCurrent(props.routeName) ? props.activeColor : null),
|
||||
);
|
||||
|
||||
// const active = computed(() => {
|
||||
// if (props.routeName && stardust.isCurrent(props.routeName)) {
|
||||
// return true;
|
||||
// }
|
||||
// else {
|
||||
// return false;
|
||||
// }
|
||||
// });
|
||||
|
||||
const activeClass = computed(() => {
|
||||
if (props.routeName && stardust.isCurrent(props.routeName)) {
|
||||
// console.log(props.item.route);
|
||||
const activeCls = [
|
||||
styleStore.navBarItemLabelActiveColorStyle,
|
||||
'dark:text-slate-400'
|
||||
];
|
||||
|
||||
if (props.underline) {
|
||||
activeCls.push('app-menu-entry__active');
|
||||
}
|
||||
|
||||
// return `${styleStore.navBarItemLabelActiveColorStyle} dark:text-slate-400 app-menu-entry__active`;
|
||||
return activeCls;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// const activeClass = computed(
|
||||
// // () => props.routeName && route().current(props.routeName) == true ? props.activeColor : null
|
||||
// () => (props.routeName && stardust.isCurrent(props.routeName) ? props.activeColor : null),
|
||||
// );
|
||||
// const itemRoute = computed(() => (props.routeName ? stardust.route(props.routeName): ''));
|
||||
|
||||
const componentClass = computed(() => {
|
||||
const base = [
|
||||
props.type,
|
||||
props.active
|
||||
? activeColor
|
||||
: `${styleStore.navBarItemLabelStyle} dark:text-white dark:hover:text-slate-400 ${styleStore.navBarItemLabelHoverStyle}`,
|
||||
`${styleStore.navBarItemLabelStyle} dark:text-white dark:hover:text-slate-400 ${styleStore.navBarItemLabelHoverStyle}`,
|
||||
// props.active
|
||||
// ? activeColor
|
||||
// : `${styleStore.navBarItemLabelStyle} dark:text-white dark:hover:text-slate-400 ${styleStore.navBarItemLabelHoverStyle}`,
|
||||
];
|
||||
|
||||
if (props.type === 'block') {
|
||||
|
@ -79,12 +113,31 @@ const componentClass = computed(() => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<component
|
||||
:is="is"
|
||||
class="items-center grow-0 shrink-0 relative cursor-pointer"
|
||||
:class="[componentClass, activeClass]"
|
||||
:href="routeName ? stardust.route(props.routeName, [props.param]) : href"
|
||||
>
|
||||
<component :is="is" class="items-center grow-0 shrink-0 relative cursor-pointer" :class="[componentClass, activeClass]"
|
||||
:href="routeName ? stardust.route(props.routeName, [props.param]) : href">
|
||||
<slot />
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.app-menu-entry__active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.app-menu-entry__active::before {
|
||||
content: "___";
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
/* border-bottom-color: var(--color-main-background); */
|
||||
/* transform: translateX(-50%); */
|
||||
width: 100%;
|
||||
/* height: 5px; */
|
||||
border-radius: 3px;
|
||||
background-color: var(--color-primary-element-text);
|
||||
/* left: 50%; */
|
||||
bottom: 6px;
|
||||
display: block;
|
||||
transition: all 0.1s ease-in-out;
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
|
@ -1,7 +1,9 @@
|
|||
<script setup>
|
||||
<script lang="ts" setup>
|
||||
import BaseIcon from '@/Components/BaseIcon.vue';
|
||||
import { computed } from 'vue';
|
||||
import { stardust } from '@eidellev/adonis-stardust/client';
|
||||
|
||||
defineProps({
|
||||
const props = defineProps({
|
||||
icon: {
|
||||
type: String,
|
||||
default: null,
|
||||
|
@ -10,12 +12,90 @@ defineProps({
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
size: {
|
||||
type: [String, Number],
|
||||
default: 16,
|
||||
},
|
||||
isDesktopIconOnly: Boolean,
|
||||
isHoverLabelOnly: Boolean,
|
||||
routeName: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const active = computed(() => {
|
||||
if (props.routeName && stardust.isCurrent(props.routeName)) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
const componentClass = computed(() => {
|
||||
const base = [
|
||||
// props.type,
|
||||
'flex',
|
||||
];
|
||||
if (props.isHoverLabelOnly ) {
|
||||
base.push(' flex-col items-center');
|
||||
}
|
||||
|
||||
return base;
|
||||
});
|
||||
|
||||
const labelClass = computed(() => {
|
||||
const base = [
|
||||
// props.type,
|
||||
'transition',
|
||||
];
|
||||
|
||||
if (props.isDesktopIconOnly && props.icon) {
|
||||
base.push('lg:hidden');
|
||||
}
|
||||
|
||||
if (props.isHoverLabelOnly && active.value == false) {
|
||||
base.push('hidden');
|
||||
} else if(props.isHoverLabelOnly && active.value == true) {
|
||||
base.push('display-block');
|
||||
}
|
||||
|
||||
|
||||
|
||||
return base;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="componentClass">
|
||||
<slot />
|
||||
<BaseIcon v-if="icon" :path="icon" class="transition-colors" />
|
||||
<span class="px-2 transition-colors" :class="{ 'lg:hidden': isDesktopIconOnly && icon }">{{ label }}</span>
|
||||
<BaseIcon v-if="icon" :path="props.icon" class="transition-colors" :size="props.size" />
|
||||
<!-- <span class="px-2 transition-colors" :class="{ 'lg:hidden': isDesktopIconOnly && props.icon }">{{ props.label }}</span> -->
|
||||
<span class="px-2 transition-colors" :class="labelClass">{{ props.label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- <style lang="css" scoped>
|
||||
.app-menu-entry__active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.app-menu-entry__active::before {
|
||||
/* content: "___"; */
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
/* border-bottom-color: var(--color-main-background); */
|
||||
/* transform: translateX(-50%); */
|
||||
width: 100%;
|
||||
/* height: 5px; */
|
||||
border-radius: 3px;
|
||||
border-bottom: 2px;
|
||||
/* background-color: var(--color-primary-element-text); */
|
||||
/* left: 50%; */
|
||||
bottom: 6px;
|
||||
display: block;
|
||||
transition: all 0.1s ease-in-out;
|
||||
opacity: 1;
|
||||
}
|
||||
</style> -->
|
||||
|
|
|
@ -19,19 +19,97 @@ const props = defineProps({
|
|||
const avatar = computed(
|
||||
// () => props.avatar ?? `https://avatars.dicebear.com/api/${props.api}/${props.username?.replace(/[^a-z0-9]+/i, '-')}.svg`
|
||||
|
||||
() => props.avatar ?? `https://avatars.dicebear.com/api/initials/${props.username}.svg`,
|
||||
// () => props.avatar ?? `https://avatars.dicebear.com/api/initials/${props.username}.svg`,
|
||||
|
||||
// () => {
|
||||
() => {
|
||||
const initials = props.username
|
||||
.split(' ')
|
||||
.map((part) => part.charAt(0).toUpperCase())
|
||||
.join('');
|
||||
|
||||
// return props.avatar ?? `https://www.gravatar.com/avatar/${props.username}?s=50`;
|
||||
// }
|
||||
return props.avatar ?? generateAvatarUrl(props.username);
|
||||
},
|
||||
);
|
||||
|
||||
const username = computed(() => props.username);
|
||||
|
||||
const darkenColor = (color) => {
|
||||
// Convert hex to RGB
|
||||
const r = parseInt(color.slice(0, 2), 16);
|
||||
const g = parseInt(color.slice(2, 4), 16);
|
||||
const b = parseInt(color.slice(4, 6), 16);
|
||||
|
||||
// Calculate darker color by reducing 20% of each RGB component
|
||||
const darkerR = Math.round(r * 0.6);
|
||||
const darkerG = Math.round(g * 0.6);
|
||||
const darkerB = Math.round(b * 0.6);
|
||||
|
||||
// Convert back to hex
|
||||
const darkerColor = ((darkerR << 16) + (darkerG << 8) + darkerB).toString(16);
|
||||
|
||||
return darkerColor.padStart(6, '0'); // Ensure it's 6 digits
|
||||
};
|
||||
|
||||
const getRandomColor = () => {
|
||||
return Math.floor(Math.random() * 16777215).toString(16);
|
||||
};
|
||||
|
||||
const adjustOpacity = (hexColor, opacity) => {
|
||||
// Remove # if present
|
||||
hexColor = hexColor.replace('#', '');
|
||||
// Convert hex to RGB
|
||||
// const r = parseInt(hexColor.slice(0, 2), 16);
|
||||
// const g = parseInt(hexColor.slice(2, 4), 16);
|
||||
// const b = parseInt(hexColor.slice(4, 6), 16);
|
||||
|
||||
// const r = parseInt(hexColor.slice(1, 3), 16);
|
||||
// const g = parseInt(hexColor.slice(3, 5), 16);
|
||||
// const b = parseInt(hexColor.slice(5, 7), 16);
|
||||
const [r, g, b] = hexColor.match(/\w\w/g).map(x => parseInt(x, 16));
|
||||
|
||||
return `rgba(${r},${g},${b},${opacity})`;
|
||||
};
|
||||
|
||||
const lightenColor = (hexColor, percent) => {
|
||||
let r = parseInt(hexColor.substring(0, 2), 16);
|
||||
let g = parseInt(hexColor.substring(2, 4), 16);
|
||||
let b = parseInt(hexColor.substring(4, 6), 16);
|
||||
|
||||
r = Math.floor(r * (100 + percent) / 100);
|
||||
g = Math.floor(g * (100 + percent) / 100);
|
||||
b = Math.floor(b * (100 + percent) / 100);
|
||||
|
||||
r = (r < 255) ? r : 255;
|
||||
g = (g < 255) ? g : 255;
|
||||
b = (b < 255) ? b : 255;
|
||||
|
||||
const lighterHex = ((r << 16) | (g << 8) | b).toString(16);
|
||||
|
||||
return lighterHex.padStart(6, '0');
|
||||
};
|
||||
|
||||
// backgroundColor = '7F9CF5',
|
||||
const generateAvatarUrl = (name) => {
|
||||
const initials = name
|
||||
.split(' ')
|
||||
.map((part) => part.charAt(0).toUpperCase())
|
||||
.join('');
|
||||
|
||||
const originalColor = getRandomColor();
|
||||
|
||||
const backgroundColor = lightenColor(originalColor, 60); // Lighten by 20%
|
||||
|
||||
const textColor = darkenColor(originalColor);
|
||||
|
||||
// const avatarUrl = `https://ui-avatars.com/api/?name=${initials}&size=50&background=${backgroundColor}&color=${textColor}`;
|
||||
const avatarUrl = `/api/avatar?name=${name}&size=50&background=${backgroundColor}&textColor=${textColor}`;
|
||||
return avatarUrl;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<img :src="avatar" :alt="username" class="rounded-full block h-auto w-full max-w-full bg-gray-100 dark:bg-slate-800" />
|
||||
<img :src="avatar" :alt="username"
|
||||
class="rounded-full block h-auto w-full max-w-full bg-gray-100 dark:bg-slate-800" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -10,27 +10,33 @@ const styleService = StyleService();
|
|||
|
||||
const layoutService = LayoutService();
|
||||
|
||||
// defineProps({
|
||||
const props = defineProps({
|
||||
showAsideMenu: {
|
||||
type: Boolean,
|
||||
default: true // Set default value to true
|
||||
}
|
||||
// user: {
|
||||
// type: Object,
|
||||
// default: () => ({}),
|
||||
// }
|
||||
// });
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="{
|
||||
<div :class="{
|
||||
'dark': styleService.darkMode,
|
||||
'overflow-hidden lg:overflow-visible': layoutService.isAsideMobileExpanded,
|
||||
}"
|
||||
>
|
||||
<div
|
||||
:class="{ 'ml-60 lg:ml-0': layoutService.isAsideMobileExpanded }"
|
||||
class="pt-14 xl:pl-60 min-h-screen w-screen transition-position lg:w-auto bg-gray-50 dark:bg-slate-800 dark:text-slate-100"
|
||||
>
|
||||
<NavBar :class="{ 'ml-60 lg:ml-0': layoutService.isAsideMobileExpanded }" />
|
||||
}">
|
||||
<div :class="{
|
||||
'ml-60 lg:ml-0': layoutService.isAsideMobileExpanded,
|
||||
'xl:pl-60': props.showAsideMenu==true }"
|
||||
class="pt-14 min-h-screen w-screen transition-position lg:w-auto bg-gray-50 dark:bg-slate-800 dark:text-slate-100">
|
||||
<NavBar :class="{ 'ml-60 lg:ml-0': layoutService.isAsideMobileExpanded }" :showBurger="props.showAsideMenu" />
|
||||
<!-- Conditionally render AsideMenu based on showAsideMenu prop -->
|
||||
<template v-if="showAsideMenu">
|
||||
<AsideMenu />
|
||||
</template>
|
||||
<!-- slot for main content -->
|
||||
<slot></slot>
|
||||
<FooterBar />
|
||||
</div>
|
||||
|
|
|
@ -28,7 +28,7 @@ const form = useForm({
|
|||
});
|
||||
|
||||
const submit = async () => {
|
||||
await form.post(stardust.route('role.store'), form);
|
||||
await form.post(stardust.route('settings.role.store'), form);
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -38,7 +38,7 @@ const submit = async () => {
|
|||
<SectionMain>
|
||||
<SectionTitleLineWithButton :icon="mdiAccountKey" title="Add role" main>
|
||||
<BaseButton
|
||||
:route-name="stardust.route('role.index')"
|
||||
:route-name="stardust.route('settings.role.index')"
|
||||
:icon="mdiArrowLeftBoldOutline"
|
||||
label="Back"
|
||||
color="white"
|
||||
|
|
|
@ -37,7 +37,7 @@ const form = useForm({
|
|||
|
||||
const submit = async () => {
|
||||
// await Inertia.post(stardust.route('user.store'), form);
|
||||
await form.put(stardust.route('role.update', [props.role.id]), form);
|
||||
await form.put(stardust.route('settings.role.update', [props.role.id]), form);
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -47,7 +47,7 @@ const submit = async () => {
|
|||
<SectionMain>
|
||||
<SectionTitleLineWithButton :icon="mdiAccountKey" title="Update role" main>
|
||||
<BaseButton
|
||||
:route-name="stardust.route('role.index')"
|
||||
:route-name="stardust.route('settings.role.index')"
|
||||
:icon="mdiArrowLeftBoldOutline"
|
||||
label="Back"
|
||||
color="white"
|
||||
|
|
|
@ -47,7 +47,7 @@ const formDelete = useForm({});
|
|||
// function destroy(id) {
|
||||
// const destroy = async (id) => {
|
||||
// if (confirm('Are you sure you want to delete?')) {
|
||||
// await formDelete.delete(stardust.route('role.destroy', [id]));
|
||||
// await formDelete.delete(stardust.route('settings.role.destroy', [id]));
|
||||
// }
|
||||
// };
|
||||
|
||||
|
@ -59,7 +59,7 @@ const destroy = (id, e) => {
|
|||
|
||||
const onConfirm = async (id) => {
|
||||
// let id = 6;
|
||||
await formDelete.delete(stardust.route('role.destroy', [id]));
|
||||
await formDelete.delete(stardust.route('settings.role.destroy', [id]));
|
||||
deleteId.value = null;
|
||||
};
|
||||
|
||||
|
@ -90,7 +90,7 @@ const onCancel = (id) => {
|
|||
<SectionTitleLineWithButton :icon="mdiAccountKey" title="Roles" main>
|
||||
<BaseButton
|
||||
v-if="can.create"
|
||||
:route-name="stardust.route('role.create')"
|
||||
:route-name="stardust.route('settings.role.create')"
|
||||
:icon="mdiPlus"
|
||||
label="Add"
|
||||
color="info"
|
||||
|
@ -139,7 +139,7 @@ const onCancel = (id) => {
|
|||
<tr v-for="role in roles" :key="role.id">
|
||||
<td data-label="Name">
|
||||
<Link
|
||||
:href="stardust.route('role.show', [role.id])"
|
||||
:href="stardust.route('settings.role.show', [role.id])"
|
||||
class="no-underline hover:underline text-cyan-600 dark:text-cyan-400"
|
||||
>
|
||||
{{ role.name }}
|
||||
|
@ -152,7 +152,7 @@ const onCancel = (id) => {
|
|||
<BaseButtons type="justify-start lg:justify-end" no-wrap>
|
||||
<BaseButton
|
||||
v-if="can.edit"
|
||||
:route-name="stardust.route('role.edit', [role.id])"
|
||||
:route-name="stardust.route('settings.role.edit', [role.id])"
|
||||
color="info"
|
||||
:icon="mdiSquareEditOutline"
|
||||
small
|
||||
|
|
|
@ -30,7 +30,7 @@ const props = defineProps({
|
|||
<SectionMain>
|
||||
<SectionTitleLineWithButton :icon="mdiAccountKey" title="View role" main>
|
||||
<BaseButton
|
||||
:route-name="stardust.route('role.index')"
|
||||
:route-name="stardust.route('settings.role.index')"
|
||||
:icon="mdiArrowLeftBoldOutline"
|
||||
label="Back"
|
||||
color="white"
|
||||
|
|
|
@ -34,7 +34,7 @@ const form = useForm({
|
|||
});
|
||||
|
||||
const submit = async () => {
|
||||
await router.post(stardust.route('user.store'), form);
|
||||
await router.post(stardust.route('settings.user.store'), form);
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -44,7 +44,7 @@ const submit = async () => {
|
|||
<SectionMain>
|
||||
<SectionTitleLineWithButton :icon="mdiAccountKey" title="Add user" main>
|
||||
<BaseButton
|
||||
:route-name="stardust.route('user.index')"
|
||||
:route-name="stardust.route('settings.user.index')"
|
||||
:icon="mdiArrowLeftBoldOutline"
|
||||
label="Back"
|
||||
color="modern"
|
||||
|
@ -52,7 +52,7 @@ const submit = async () => {
|
|||
small
|
||||
/>
|
||||
</SectionTitleLineWithButton>
|
||||
<!-- @submit.prevent="form.post(stardust.route('user.store'))" -->
|
||||
<!-- @submit.prevent="form.post(stardust.route('settings.user.store'))" -->
|
||||
<CardBox form @submit.prevent="submit()">
|
||||
<FormField label="Login" :class="{ 'text-red-400': errors.login }">
|
||||
<FormControl v-model="form.login" type="text" placeholder="Enter Login" :errors="errors.login">
|
||||
|
|
|
@ -44,7 +44,7 @@ const form = useForm({
|
|||
|
||||
const submit = async () => {
|
||||
// await Inertia.post(stardust.route('user.store'), form);
|
||||
await router.put(stardust.route('user.update', [props.user.id]), form);
|
||||
await router.put(stardust.route('settings.user.update', [props.user.id]), form);
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -54,7 +54,7 @@ const submit = async () => {
|
|||
<SectionMain>
|
||||
<SectionTitleLineWithButton :icon="mdiAccountKey" title="Update user" main>
|
||||
<BaseButton
|
||||
:route-name="stardust.route('user.index')"
|
||||
:route-name="stardust.route('settings.user.index')"
|
||||
:icon="mdiArrowLeftBoldOutline"
|
||||
label="Back"
|
||||
color="white"
|
||||
|
|
|
@ -66,7 +66,7 @@ const destroy = async (id) => {
|
|||
<SectionTitleLineWithButton :icon="mdiAccountKey" title="Tethys Users" main>
|
||||
<BaseButton
|
||||
v-if="can.create"
|
||||
:route-name="stardust.route('user.create')"
|
||||
:route-name="stardust.route('settings.user.create')"
|
||||
:icon="mdiPlus"
|
||||
label="Add"
|
||||
color="modern"
|
||||
|
@ -80,7 +80,7 @@ const destroy = async (id) => {
|
|||
</NotificationBar>
|
||||
<!-- <NotificationBar color="success" :icon="mdiAlertBoxOutline">{{ users.meta }}</NotificationBar> -->
|
||||
<CardBox class="mb-6" has-table>
|
||||
<form @submit.prevent="form.get(stardust.route('user.index'))">
|
||||
<form @submit.prevent="form.get(stardust.route('settings.user.index'))">
|
||||
<div class="py-2 flex">
|
||||
<div class="flex pl-4">
|
||||
<input
|
||||
|
@ -113,7 +113,7 @@ const destroy = async (id) => {
|
|||
<tr v-for="user in users.data" :key="user.id">
|
||||
<td data-label="Login">
|
||||
<Link
|
||||
v-bind:href="stardust.route('user.show', [user.id])"
|
||||
v-bind:href="stardust.route('settings.user.show', [user.id])"
|
||||
class="no-underline hover:underline text-cyan-600 dark:text-cyan-400"
|
||||
>
|
||||
{{ user.login }}
|
||||
|
@ -127,7 +127,7 @@ const destroy = async (id) => {
|
|||
<BaseButtons type="justify-start lg:justify-end" no-wrap>
|
||||
<BaseButton
|
||||
v-if="can.edit"
|
||||
:route-name="stardust.route('user.edit', [user.id])"
|
||||
:route-name="stardust.route('settings.user.edit', [user.id])"
|
||||
color="info"
|
||||
:icon="mdiSquareEditOutline"
|
||||
small
|
||||
|
|
|
@ -30,7 +30,7 @@ defineProps({
|
|||
<SectionMain>
|
||||
<SectionTitleLineWithButton :icon="mdiAccountKey" title="View user" main>
|
||||
<BaseButton
|
||||
:route-name="stardust.route('user.index')"
|
||||
:route-name="stardust.route('settings.user.index')"
|
||||
:icon="mdiArrowLeftBoldOutline"
|
||||
label="Back"
|
||||
color="white"
|
||||
|
|
|
@ -57,7 +57,7 @@ const datasets = computed(() => mainService.datasets);
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<LayoutAuthenticated>
|
||||
<LayoutAuthenticated :showAsideMenu="false">
|
||||
<Head title="Dashboard" />
|
||||
|
||||
<!-- <section class="p-6" v-bind:class="containerMaxW"> -->
|
||||
|
|
|
@ -76,7 +76,7 @@ const getRowClass = (dataset) => {
|
|||
|
||||
<!-- table -->
|
||||
<CardBox class="mb-6" has-table>
|
||||
<div v-if="props.datasets && props.datasets.length > 0">
|
||||
<div v-if="props.datasets.data.length > 0">
|
||||
<table class="pure-table">
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { Head } from '@inertiajs/vue3';
|
||||
import { ref, Ref } from 'vue';
|
||||
import { mdiChartTimelineVariant } from '@mdi/js';
|
||||
import LayoutGuest from '@/Layouts/LayoutGuest.vue';
|
||||
import LayoutAuthenticated from '@/Layouts/LayoutAuthenticated.vue';
|
||||
import SectionMain from '@/Components/SectionMain.vue';
|
||||
import BaseButton from '@/Components/BaseButton.vue';
|
||||
import SectionTitleLineWithButton from '@/Components/SectionTitleLineWithButton.vue';
|
||||
|
@ -47,7 +47,7 @@ const mapOptions: MapOptions = {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<LayoutGuest>
|
||||
<LayoutAuthenticated :showAsideMenu="false">
|
||||
<Head title="Map" />
|
||||
|
||||
<!-- <section class="p-6" v-bind:class="containerMaxW"> -->
|
||||
|
@ -99,5 +99,5 @@ const mapOptions: MapOptions = {
|
|||
</div>
|
||||
</SectionMain>
|
||||
<!-- </section> -->
|
||||
</LayoutGuest>
|
||||
</LayoutAuthenticated>
|
||||
</template>
|
||||
|
|
|
@ -101,7 +101,7 @@ const getRowClass = (dataset) => {
|
|||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
<tr v-for="dataset in props.datasets.data" :key="dataset.id" :class="getRowClass(dataset)">
|
||||
<td data-label="Login" class="py-4 whitespace-nowrap text-gray-700 dark:text-white">
|
||||
<!-- <Link v-bind:href="stardust.route('user.show', [user.id])"
|
||||
<!-- <Link v-bind:href="stardust.route('settings.user.show', [user.id])"
|
||||
class="no-underline hover:underline text-cyan-600 dark:text-cyan-400">
|
||||
{{ user.login }}
|
||||
</Link> -->
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {
|
||||
mdiMonitor,
|
||||
// mdiMonitor,
|
||||
mdiGithub,
|
||||
mdiAccountEye,
|
||||
mdiAccountGroup,
|
||||
|
@ -7,13 +7,15 @@ import {
|
|||
mdiPublish,
|
||||
mdiAccountArrowUp,
|
||||
mdiFormatListNumbered,
|
||||
mdiLock
|
||||
mdiLock,
|
||||
mdiFormatListGroup,
|
||||
mdiShieldCrownOutline,
|
||||
} from '@mdi/js';
|
||||
|
||||
export default [
|
||||
{
|
||||
// route: 'dataset.create',
|
||||
icon: mdiAccountArrowUp,
|
||||
icon: mdiAccountEdit ,
|
||||
label: 'Personal',
|
||||
// roles: ['submitter'],
|
||||
isOpen: true,
|
||||
|
@ -30,11 +32,11 @@ export default [
|
|||
// },
|
||||
],
|
||||
},
|
||||
{
|
||||
route: 'apps.dashboard',
|
||||
icon: mdiMonitor,
|
||||
label: 'Dashboard',
|
||||
},
|
||||
// {
|
||||
// route: 'apps.dashboard',
|
||||
// icon: mdiMonitor,
|
||||
// label: 'Dashboard',
|
||||
// },
|
||||
// {
|
||||
// route: 'permission.index',
|
||||
// icon: mdiAccountKey,
|
||||
|
@ -46,23 +48,33 @@ export default [
|
|||
// label: 'Roles'
|
||||
// },
|
||||
{
|
||||
route: 'user.index',
|
||||
icon: mdiShieldCrownOutline,
|
||||
label: 'Administration',
|
||||
roles: ['administrator'],
|
||||
isOpen: true,
|
||||
permanent: true,
|
||||
children: [
|
||||
{
|
||||
route: 'settings.overview',
|
||||
icon: mdiFormatListGroup,
|
||||
label: 'Overview',
|
||||
roles: ['administrator'],
|
||||
},
|
||||
{
|
||||
route: 'settings.user.index',
|
||||
icon: mdiAccountGroup,
|
||||
label: 'Users',
|
||||
roles: ['administrator'],
|
||||
},
|
||||
{
|
||||
route: 'role.index',
|
||||
route: 'settings.role.index',
|
||||
icon: mdiAccountEye,
|
||||
label: 'Roles',
|
||||
roles: ['administrator'],
|
||||
},
|
||||
{
|
||||
href: '/oai',
|
||||
icon: mdiAccountEye,
|
||||
label: 'OAI',
|
||||
target: '_blank',
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
// route: 'dataset.create',
|
||||
icon: mdiAccountArrowUp,
|
||||
|
@ -112,4 +124,10 @@ export default [
|
|||
label: 'Gitea',
|
||||
target: '_blank',
|
||||
},
|
||||
{
|
||||
href: '/oai',
|
||||
icon: mdiAccountEye,
|
||||
label: 'OAI',
|
||||
target: '_blank',
|
||||
},
|
||||
];
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" type="image/png" href="/favicon.ico">
|
||||
{{-- <link rel="icon" type="image/png" href="/favicon.ico"> --}}
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
{{-- <link rel="icon" href="/apps/theming/favicon/settings?v=ad28c447"> --}}
|
||||
|
||||
@routes
|
||||
@entryPointStyles('app')
|
||||
|
|
|
@ -54,6 +54,10 @@ Route.group(() => {
|
|||
return inertia.render('Dashboard');
|
||||
}).as('dashboard');
|
||||
|
||||
Route.get('/map', async ({ inertia }) => {
|
||||
return inertia.render('Map');
|
||||
}).as('map');
|
||||
|
||||
Route.get('/', async ({ inertia }) => {
|
||||
const users = await User.query().orderBy('login');
|
||||
return inertia.render('App', {
|
||||
|
@ -85,9 +89,7 @@ Route.group(() => {
|
|||
.as('apps')
|
||||
.middleware('auth');
|
||||
|
||||
Route.get('/map', async ({ inertia }) => {
|
||||
return inertia.render('Map');
|
||||
}).as('map');
|
||||
|
||||
|
||||
// Route.on("/login").render("signin");
|
||||
Route.get('/app/login', async ({ inertia }) => {
|
||||
|
@ -104,7 +106,8 @@ Route.post('/signout', 'Auth/AuthController.logout').as('logout');
|
|||
Route.group(() => {
|
||||
Route.get('/settings', async ({ inertia }) => {
|
||||
return inertia.render('Admin/Settings');
|
||||
}).as('settings');
|
||||
|
||||
}).as('overview');
|
||||
|
||||
Route.get('/user', 'UsersController.index').as('user.index').middleware(['can:user-list']);
|
||||
Route.get('/user/create', 'UsersController.create').as('user.create').middleware(['can:user-create']);
|
||||
|
@ -137,6 +140,7 @@ Route.group(() => {
|
|||
})
|
||||
.namespace('App/Controllers/Http/Admin')
|
||||
.prefix('admin')
|
||||
.as('settings')
|
||||
// .middleware(['auth', 'can:dataset-list,dataset-publish']);
|
||||
.middleware(['auth', 'is:administrator,moderator']);
|
||||
|
||||
|
@ -189,7 +193,7 @@ Route.group(() => {
|
|||
.middleware(['auth', 'can:dataset-delete']);
|
||||
|
||||
Route.get('/person', 'PersonController.index').as('person.index').middleware(['auth']);
|
||||
// Route.get('/user/:id/edit', 'UsersController.edit').as('user.edit').where('id', Route.matchers.number());
|
||||
// Route.get('/user/:id/edit', 'UsersController.edit').as('settings.user.edit').where('id', Route.matchers.number());
|
||||
// Route.put('/user/:id/update', 'UsersController.update').as('user.update').where('id', Route.matchers.number());
|
||||
// Route.delete('/user/:id', 'UsersController.destroy').as('user.destroy').where('id', Route.matchers.number());
|
||||
// Route.resource('user', 'DatasetController');
|
||||
|
|
|
@ -18,6 +18,8 @@ Route.group(() => {
|
|||
Route.get('/years', 'HomeController.findYears');
|
||||
|
||||
Route.get('/download/:id', 'FileController.findOne').as('file.findOne');
|
||||
|
||||
Route.get('/avatar/:name/:background?/:textColor?/:size?', 'AvatarController.generateAvatar')
|
||||
});
|
||||
// .middleware("auth:api");
|
||||
})
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 119 KiB |
Loading…
Reference in New Issue
Block a user