Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ VITE_SIGNOUT_REDIRECT_URI=your-signout-redirect-uri
VITE_API_BASE_URL=your-api-base-url
VITE_LOGTO_ENDPOINT=your-logto-endpoint
VITE_LOGTO_APP_ID=your-logto-app-id
VITE_SHOP_BASE_URL=your-shop-base-url
2 changes: 1 addition & 1 deletion frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 9 additions & 2 deletions frontend/src/assets/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@
--breakpoint-6xl: 192rem;
--breakpoint-7xl: 224rem;

/* motion durations, scaled by how far the animated element travels: state
feedback and small components are quick, while large panels get more time
to enter than to leave */
--duration-small: 200ms;
--duration-large-enter: 300ms;
--duration-large-leave: 200ms;

/* animations */
--animate-spin-relaxed: spin 1.5s linear infinite;
--animate-fade-in: fade-in 0.3s ease-in;
Expand Down Expand Up @@ -83,7 +90,7 @@
/* fade transition */
.fade-enter-active,
.fade-leave-active {
transition: all 0.3s ease;
transition: all var(--duration-small) ease;
}

.fade-enter-from,
Expand All @@ -94,7 +101,7 @@
/* slide-down transition */
.slide-down-enter-active,
.slide-down-leave-active {
transition: all 0.3s ease;
transition: all var(--duration-small) ease;
}

.slide-down-enter-from,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/account/ChangePasswordDrawer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ async function validateAndChangePassword() {
<NeSideDrawer
:is-shown="isShown"
:title="$t('account.change_password')"
:close-aria-label="$t('common.shell.close_side_drawer')"
:close-aria-label="$t('shell.close_side_drawer')"
@close="closeDrawer"
>
<form @submit.prevent>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/account/ChangePictureDrawer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ function savePicture() {
<NeSideDrawer
:is-shown="isShown"
:title="$t('account.change_picture')"
:close-aria-label="$t('common.shell.close_side_drawer')"
:close-aria-label="$t('shell.close_side_drawer')"
@close="closeDrawer"
>
<form @submit.prevent>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ function copyKeyAndCloseDrawer() {
<NeSideDrawer
:is-shown="isShown"
:title="$t('account.api_keys.create_api_key')"
:close-aria-label="$t('common.shell.close_side_drawer')"
:close-aria-label="$t('shell.close_side_drawer')"
@show="onShow"
@close="closeDrawer"
>
Expand Down
43 changes: 43 additions & 0 deletions frontend/src/components/addons/AddonInUseModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<!--
Copyright (C) 2026 Nethesis S.r.l.
SPDX-License-Identifier: GPL-3.0-or-later

Shown instead of the delete confirmation when the catalog reports the add-on as
in use: the backend refuses the DELETE, so there is nothing to confirm and the
modal only explains why.
-->

<script setup lang="ts">
import { NeModal } from '@nethesis/vue-components'
import type { Addon } from '@/lib/addons/addons'

const { visible = false, addon = undefined } = defineProps<{
visible: boolean
addon: Addon | undefined
}>()

const emit = defineEmits(['close'])
</script>

<template>
<NeModal
:visible="visible"
:title="$t('addons.addon_in_use')"
kind="warning"
:primary-label="$t('common.close')"
cancel-label=""
primary-button-kind="primary"
:close-aria-label="$t('common.close')"
@close="emit('close')"
@primary-click="emit('close')"
>
<div class="space-y-4">
<p>
{{ $t('addons.addon_in_use_description', { name: addon?.display_name }) }}
</p>
<p>
{{ $t('addons.addon_in_use_report_hint') }}
</p>
</div>
</NeModal>
</template>
36 changes: 36 additions & 0 deletions frontend/src/components/addons/AddonLogo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!--
Copyright (C) 2026 Nethesis S.r.l.
SPDX-License-Identifier: GPL-3.0-or-later

The logo of an add-on, from its catalog id alone: the application logo for a
NethServer module, the product logo for a system-wide service. Views that
already hold the Addon can reach for the two logo components directly β€” this
exists for the ones holding nothing but the id, such as the report, whose
endpoints carry no catalog fields.

The id prefix is deliberately not read: an application name may itself contain
a hyphen, which is why applies_to exists. So the catalog is the only source,
and an id it does not know gets no logo rather than a guessed one.
-->

<script setup lang="ts">
import { computed } from 'vue'
import ApplicationLogo from '@/components/applications/ApplicationLogo.vue'
import SystemLogo from '@/components/systems/SystemLogo.vue'
import { getAddonApplication } from '@/lib/addons/addons'
import { useAddons } from '@/queries/addons/addons'

const { addonId } = defineProps<{ addonId: string }>()

// One shared query however many rows ask for it: the catalog arrives whole and
// is cached for the page, so this is a lookup, not a request per add-on.
const { state: catalog } = useAddons()

const addon = computed(() => catalog.value.data?.find((item) => item.id === addonId))
const application = computed(() => (addon.value ? getAddonApplication(addon.value) : ''))
</script>

<template>
<ApplicationLogo v-if="application" :app="application" />
<SystemLogo v-else-if="addon?.system_type" :system="addon.system_type" />
</template>
Loading
Loading