diff --git a/aleksis/core/assets/App.vue b/aleksis/core/assets/App.vue index 1ff35d220a89a2e11f002c24483cadd9786f2c00..af8ec419330c2699fa23718602c5c13d6ac693b3 100644 --- a/aleksis/core/assets/App.vue +++ b/aleksis/core/assets/App.vue @@ -1,9 +1,13 @@ <template> <v-app v-cloak> <loading - v-if=" + v-if=" $apollo.loading && - (!currentUser || !whoAmI || !systemProperties || !messages || !footerMenu) + (!currentUser || + !whoAmI || + !systemProperties || + !messages || + !footerMenu) " > </loading> @@ -12,97 +16,103 @@ <v-list nav dense shaped> <v-list-item class="logo"> <a id="logo-container" href="/" class="brand-logo"> - <brand-logo :site-preferences="systemProperties.sitePreferences" /> + <brand-logo + :site-preferences="systemProperties.sitePreferences" + /> <!-- FIXME Translation --> </a> </v-list-item> <v-list-item class="search"> - <sidenav-search/> + <sidenav-search /> </v-list-item> <v-list-item-group :value="$route.name"> <div v-for="menuItem in sideNavMenu" :key="menuItem.name"> <v-list-group - v-if="menuItem.subMenu.length > 0" - href="#!" - :prepend-icon="menuItem.icon" - :value="$route.matched.slice(-2).shift().name === menuItem.name" + v-if="menuItem.subMenu.length > 0" + href="#!" + :prepend-icon="menuItem.icon" + :value="$route.matched.slice(-2).shift().name === menuItem.name" > <template #activator> <v-list-item-title - >{{ $t(menuItem.titleKey) }} + >{{ $t(menuItem.titleKey) }} </v-list-item-title> </template> <v-list-item - v-for="subMenuItem in menuItem.subMenu" - :to="{ name: subMenuItem.name }" - :target="subMenuItem.newTab ? '_blank' : '_self'" - :key="subMenuItem.name" - :value="subMenuItem.name" + v-for="subMenuItem in menuItem.subMenu" + :to="{ name: subMenuItem.name }" + :target="subMenuItem.newTab ? '_blank' : '_self'" + :key="subMenuItem.name" + :value="subMenuItem.name" > <v-list-item-icon> <v-icon v-if="subMenuItem.icon" - >{{ subMenuItem.icon }} + >{{ subMenuItem.icon }} </v-icon> </v-list-item-icon> <v-list-item-title - >{{ $t(subMenuItem.titleKey) }} + >{{ $t(subMenuItem.titleKey) }} </v-list-item-title> </v-list-item> </v-list-group> <v-list-item - v-else - :to="{ name: menuItem.name }" - :target="menuItem.newTab ? '_blank' : '_self'" - :value="menuItem.name" + v-else + :to="{ name: menuItem.name }" + :target="menuItem.newTab ? '_blank' : '_self'" + :value="menuItem.name" > <v-list-item-icon> <v-icon v-if="menuItem.icon">{{ menuItem.icon }}</v-icon> </v-list-item-icon> - <v-list-item-title>{{ $t(menuItem.titleKey) }}</v-list-item-title> + <v-list-item-title>{{ + $t(menuItem.titleKey) + }}</v-list-item-title> </v-list-item> </div> </v-list-item-group> </v-list> - <template v-slot:append> - <div class="pa-2"> - <language-form :available-languages="systemProperties.availableLanguages" /> + <template #append> + <div class="pa-4 d-flex justify-center align-center"> + <v-spacer /> + <language-form + :available-languages="systemProperties.availableLanguages" + /> + <v-spacer /> </div> </template> </v-navigation-drawer> <v-app-bar app color="primary white--text"> - <v-app-bar-nav-icon - @click="drawer = !drawer" - color="white"/> + <v-app-bar-nav-icon @click="drawer = !drawer" color="white" /> <v-toolbar-title - tag="a" - class="white--text text-decoration-none" - href="/" + tag="a" + class="white--text text-decoration-none" + href="/" > {{ systemProperties.sitePreferences.generalTitle }} </v-toolbar-title> - <v-spacer/> + <v-spacer /> <div v-if="currentUser.isAuthenticated" class="d-flex"> - <notification-list/> + <notification-list /> <v-menu offset-y> <template #activator="{ on, attrs }"> <v-avatar v-bind="attrs" v-on="on"> <img - v-if=" + v-if=" systemProperties.sitePreferences.accountPersonPreferPhoto && whoAmI.photo " - :src="whoAmI.photo.url" - :alt="whoAmI.fullName" - :title="whoAmI.fullName" + :src="whoAmI.photo.url" + :alt="whoAmI.fullName" + :title="whoAmI.fullName" /> <img - v-else-if="whoAmI.avatarUrl" - :src="whoAmI.avatarUrl" - :alt="whoAmI.fullName + '(' + $t('person.avatar') + ')'" - :title="whoAmI.fullName + '(' + $t('person.avatar') + ')'" + v-else-if="whoAmI.avatarUrl" + :src="whoAmI.avatarUrl" + :alt="whoAmI.fullName + '(' + $t('person.avatar') + ')'" + :title="whoAmI.fullName + '(' + $t('person.avatar') + ')'" /> <v-icon v-else>mdi-person</v-icon> </v-avatar> @@ -115,14 +125,14 @@ <div v-for="menuItem in accountMenu" :key="menuItem.name"> <v-divider v-if="menuItem.divider"></v-divider> <v-list-item - :to="{ name: menuItem.name }" - :target="menuItem.newTab ? '_blank' : '_self'" + :to="{ name: menuItem.name }" + :target="menuItem.newTab ? '_blank' : '_self'" > <v-list-item-icon> <v-icon v-if="menuItem.icon">{{ menuItem.icon }}</v-icon> </v-list-item-icon> <v-list-item-title - >{{ $t(menuItem.titleKey) }} + >{{ $t(menuItem.titleKey) }} </v-list-item-title> </v-list-item> </div> @@ -132,56 +142,63 @@ </v-app-bar> <v-main> <v-container> - <cache-notification/> + <cache-notification /> <message-box type="error" v-if="whoAmI && whoAmI.isDummy"> {{ $t("base.person_is_dummy") }} </message-box> <message-box - type="error" - v-else-if="!whoAmI && currentUser.isAnonymous" + type="error" + v-else-if="!whoAmI && currentUser.isAnonymous" > {{ $t("base.user_not_linked_to_person") }} </message-box> <div v-if="messages"> <message-box - v-for="(message, idx) in messages" - :type="message.tags" - :key="idx" - >{{ message.message }} + v-for="(message, idx) in messages" + :type="message.tags" + :key="idx" + >{{ message.message }} </message-box> </div> - <router-view/> + <router-view /> </v-container> </v-main> - <celery-progress-bottom/> + <celery-progress-bottom /> <v-footer - app - absolute - inset - dark - class="pa-0 d-flex" - color="primary lighten-1" + app + absolute + inset + dark + class="pa-0 d-flex" + color="primary lighten-1" > <v-card flat tile class="primary white--text flex-grow-1"> <v-card-text v-if="footerMenu.items" class="pa-0"> <v-container class="px-6"> - <v-row - justify="center" - no-gutters - > - <v-btn v-for="menu_item in footerMenu.items" text rounded :href="menu_item.url" color="white" class="ma-2"> - <v-icon v-if="menu_item.icon" left>{{ "mdi-" + menu_item.icon }}</v-icon> + <v-row justify="center" no-gutters> + <v-btn + v-for="menu_item in footerMenu.items" + :key="menu_item.name" + text + rounded + :href="menu_item.url" + color="white" + class="ma-2" + > + <v-icon v-if="menu_item.icon" left>{{ + "mdi-" + menu_item.icon + }}</v-icon> {{ menu_item.name }} </v-btn> </v-row> </v-container> </v-card-text> - <v-divider/> + <v-divider /> <v-card-text class="pa-0"> <v-container class="px-6"> @@ -189,29 +206,29 @@ <v-col class="white--text d-flex align-center subtitle-2"> <div> <router-link - to="/about" - class="white--text text-decoration-none" - >{{ $t("base.about_aleksis") }} + to="/about" + class="white--text text-decoration-none" + >{{ $t("base.about_aleksis") }} </router-link> <span>© The AlekSIS Team</span> </div> </v-col> <v-col class="d-flex justify-end"> <v-btn - v-if="systemProperties.sitePreferences.footerImprintUrl" - small - text - :href="systemProperties.sitePreferences.footerImprintUrl" - color="white" + v-if="systemProperties.sitePreferences.footerImprintUrl" + small + text + :href="systemProperties.sitePreferences.footerImprintUrl" + color="white" > {{ $t("base.imprint") }} </v-btn> <v-btn - v-if="systemProperties.sitePreferences.footerPrivacyUrl" - small - text - :href="systemProperties.sitePreferences.footerPrivacyUrl" - color="white" + v-if="systemProperties.sitePreferences.footerPrivacyUrl" + small + text + :href="systemProperties.sitePreferences.footerPrivacyUrl" + color="white" > {{ $t("base.privacy_policy") }} </v-btn> @@ -263,16 +280,21 @@ export default { for (const route of this.$router.getRoutes()) { if (route.meta && route.meta["permission"]) { permArray.push(route.meta["permission"]); - }; + } } this.$data.menuPermissionNames = permArray; - this.$data.accountMenu = this.buildMenu(this.$router.getRoutes(), "inAccountMenu"); - this.$data.sideNavMenu = this.buildMenu(this.$router.getRoutes(), "inMenu"); + this.$data.accountMenu = this.buildMenu( + this.$router.getRoutes(), + "inAccountMenu" + ); + this.$data.sideNavMenu = this.buildMenu( + this.$router.getRoutes(), + "inMenu" + ); }, buildMenu(routes, menuKey) { - let menu = {}; // Top-level entries @@ -291,12 +313,12 @@ export default { // Sub menu entries for (const route of routes) { if ( - route.name && - route.meta && - route.meta[menuKey] && - route.parent && - route.parent.name && - route.parent.name in menu + route.name && + route.meta && + route.meta[menuKey] && + route.parent && + route.parent.name && + route.parent.name in menu ) { let menuItem = { ...route.meta, @@ -332,7 +354,7 @@ export default { name: "footer", }; }, - update: data => data.customMenuByName + update: (data) => data.customMenuByName, }, menuPermissions: { query: gqlGlobalPermissions, @@ -341,7 +363,7 @@ export default { permissions: this.$data.menuPermissionNames, }; }, - update: data => data.globalPermissionsByName + update: (data) => data.globalPermissionsByName, }, }, watch: { @@ -349,13 +371,13 @@ export default { this.$i18n.locale = newProperties.currentLanguage; this.$vuetify.lang.current = newProperties.currentLanguage; this.$vuetify.theme.themes.light.primary = - newProperties.sitePreferences.themePrimary; + newProperties.sitePreferences.themePrimary; this.$vuetify.theme.themes.light.secondary = - newProperties.sitePreferences.themeSecondary; + newProperties.sitePreferences.themeSecondary; this.$vuetify.theme.themes.dark.primary = - newProperties.sitePreferences.themePrimary; + newProperties.sitePreferences.themePrimary; this.$vuetify.theme.themes.dark.secondary = - newProperties.sitePreferences.themeSecondary; + newProperties.sitePreferences.themeSecondary; }, whoAmI: function (person) { this.$vuetify.theme.dark = person.preferences.themeDesignMode === "dark"; diff --git a/aleksis/core/assets/app.js b/aleksis/core/assets/app.js index 2dc88307919f2ae2e9c5fec558b895750902aa1f..e992ca487ef872b1d0cfc6c2cff89ae616640441 100644 --- a/aleksis/core/assets/app.js +++ b/aleksis/core/assets/app.js @@ -77,11 +77,13 @@ const router = new VueRouter({ mode: "history", }); -if (document.getElementById('sentry_settings') !== null) { - const Sentry = await import("@sentry/vue"); - const { BrowserTracing } = await import("@sentry/tracing"); +if (document.getElementById("sentry_settings") !== null) { + const Sentry = import("@sentry/vue"); + const { BrowserTracing } = import("@sentry/tracing"); - const sentry_settings = JSON.parse(document.getElementById('sentry_settings').textContent); + const sentry_settings = JSON.parse( + document.getElementById("sentry_settings").textContent + ); Sentry.init({ Vue, dsn: sentry_settings.dsn, diff --git a/aleksis/core/assets/components/Error404.vue b/aleksis/core/assets/components/Error404.vue index f3767ccc320cb3a30af52eb13728b7f0c0c2f970..6c662fe553292dfcb494d6133399a531a7114b63 100644 --- a/aleksis/core/assets/components/Error404.vue +++ b/aleksis/core/assets/components/Error404.vue @@ -2,17 +2,19 @@ <div class="d-flex justify-center align-center"> <h1 class="mx-2">{{ $t("network_errors.error_404") }}</h1> - <v-btn class="mx-2" color="primary white--text" @click="$router.push('/')">{{ $t("network_errors.take_me_back") }}</v-btn> + <v-btn + class="mx-2" + color="primary white--text" + @click="$router.push('/')" + >{{ $t("network_errors.take_me_back") }}</v-btn + > </div> </template> <script> - export default { name: "Error404", -} +}; </script> -<style scoped> - -</style> +<style scoped></style> diff --git a/aleksis/core/assets/components/LanguageForm.vue b/aleksis/core/assets/components/LanguageForm.vue index b3eda751c198db8644f4251c2436744aba393f50..7757b9f46622ad0ba3e0158be75f85220a5f0454 100644 --- a/aleksis/core/assets/components/LanguageForm.vue +++ b/aleksis/core/assets/components/LanguageForm.vue @@ -7,12 +7,17 @@ item-value="code" menu-props="auto" outlined - prepend-icon="mdi-translate" color="primary" single-line return-object + dense + style="width: 75px" @input="setLanguage(language)" - ></v-select> + > + <template #selection="{ item, index }"> + <span class="text-uppercase">{{ item.code }}</span> + </template> + </v-select> </template> <script> diff --git a/aleksis/core/assets/components/LegacyBaseTemplate.vue b/aleksis/core/assets/components/LegacyBaseTemplate.vue index 98ac15e8455671b4e4f5a662ead86a837d5aaf51..bbaefabd0d4fb1fa4e64abd0b21335322b74194b 100644 --- a/aleksis/core/assets/components/LegacyBaseTemplate.vue +++ b/aleksis/core/assets/components/LegacyBaseTemplate.vue @@ -30,7 +30,7 @@ export default { let qs = []; for (const [param, value] of Object.entries(this.$route.query)) { qs.push(`${param}=${encodeURIComponent(value)}`); - }; + } return "?" + qs.join("&"); }, }, diff --git a/aleksis/core/assets/globalPermissions.graphql b/aleksis/core/assets/globalPermissions.graphql index 934521b48636c6d4dbf800d43e4d881ffdb5947b..0d521e45e26ac6f22bba64d08a1f04b57e59dff2 100644 --- a/aleksis/core/assets/globalPermissions.graphql +++ b/aleksis/core/assets/globalPermissions.graphql @@ -1,4 +1,4 @@ -query ($permissions: [String]!){ +query ($permissions: [String]!) { globalPermissionsByName(permissions: $permissions) { name result diff --git a/aleksis/core/schema/__init__.py b/aleksis/core/schema/__init__.py index c0a81bc083573a6b9b2fb4380c5860e660076869..55a217ea986dcbe451c537dfb1e9ad569fa8d05c 100644 --- a/aleksis/core/schema/__init__.py +++ b/aleksis/core/schema/__init__.py @@ -12,10 +12,7 @@ from haystack.utils.loading import UnifiedIndex from ..models import CustomMenu, Notification, Person, TaskUserAssignment from ..util.apps import AppConfig from ..util.core_helpers import get_allowed_object_ids, get_app_module, get_app_packages, has_person -from .celery_progress import ( - CeleryProgressFetchedMutation, - CeleryProgressType, -) +from .celery_progress import CeleryProgressFetchedMutation, CeleryProgressType from .custom_menu import CustomMenuType from .group import GroupType # noqa from .installed_apps import AppType diff --git a/aleksis/core/settings.py b/aleksis/core/settings.py index 42fdfa4c0992b1fa604dd4d60270ad158efe1915..5131891f94ec5bfde444a65e33ef8473d4156485 100644 --- a/aleksis/core/settings.py +++ b/aleksis/core/settings.py @@ -580,6 +580,7 @@ YARN_INSTALLED_APPS = [ "rollup-plugin-license@^3.0.1", "vue-i18n@8", "@sentry/vue@^7.28.0", + "prettier@^2.8.1", "eslint@^8.26.0", "eslint-plugin-vue@^9.7.0", "eslint-config-prettier@^8.5.0", diff --git a/aleksis/core/vite.config.js b/aleksis/core/vite.config.js index 0e47b88e7e0c63ab64c6639d604d8c08b0983b84..6094093d5b4cd095aefe83f64e281e5a8dfb74b4 100644 --- a/aleksis/core/vite.config.js +++ b/aleksis/core/vite.config.js @@ -6,7 +6,7 @@ import vue from "@vitejs/plugin-vue2"; import { nodeResolve } from "@rollup/plugin-node-resolve"; import graphql from "@rollup/plugin-graphql"; import virtual from "@rollup/plugin-virtual"; -const license = require('rollup-plugin-license'); +const license = require("rollup-plugin-license"); const django_values = JSON.parse(fs.readFileSync("./django-vite-values.json")); @@ -50,7 +50,9 @@ export default defineConfig({ } // Split each AlekSIS app in its own chunk - for (const [appPackage, ep] of Object.entries(django_values.appEntrypoints)) { + for (const [appPackage, ep] of Object.entries( + django_values.appEntrypoints + )) { if (id.includes(ep)) { return appPackage; } @@ -80,7 +82,7 @@ export default defineConfig({ }, thirdParty: { allow: { - test: 'MIT OR Apache-2.0 OR 0BSD OR BSD-3-Clause', + test: "MIT OR Apache-2.0 OR 0BSD OR BSD-3-Clause", failOnUnlicensed: true, failOnViolation: true, },