feat: Add a section in admin SettingsView to manage external links

Related to #1764
This commit is contained in:
Massedil
2025-07-03 22:11:59 +02:00
parent 56facb204a
commit ca304d6333
5 changed files with 79 additions and 4 deletions

View File

@@ -200,6 +200,11 @@ export const ADMIN_SETTINGS_FRAGMENT = gql`
instanceLongDescription
instanceSlogan
contact
externalUrls {
label
url
enabled
}
instanceLogo {
uuid
url
@@ -246,6 +251,7 @@ export const SAVE_ADMIN_SETTINGS = gql`
$instanceLongDescription: String
$instanceSlogan: String
$contact: String
$externalUrls: [ExternalUrlInput]
$instanceLogo: MediaInput
$instanceFavicon: MediaInput
$defaultPicture: MediaInput
@@ -268,6 +274,7 @@ export const SAVE_ADMIN_SETTINGS = gql`
instanceLongDescription: $instanceLongDescription
instanceSlogan: $instanceSlogan
contact: $contact
externalUrls: $externalUrls
instanceLogo: $instanceLogo
instanceFavicon: $instanceFavicon
defaultPicture: $defaultPicture

View File

@@ -84,6 +84,7 @@
"Add / Remove\u2026": "Add / Remove\u2026",
"Add a contact": "Add a contact",
"Add a new post": "Add a new post",
"Add a new link": "Add a new link",
"Add a note": "Add a note",
"Add a recipient": "Add a recipient",
"Add a todo": "Add a todo",
@@ -345,6 +346,7 @@
"Delete this discussion": "Delete this discussion",
"Delete this identity": "Delete this identity",
"Delete your identity": "Delete your identity",
"Delete this link": "Delete this link",
"Delete {eventTitle}": "Delete {eventTitle}",
"Delete {preferredUsername}": "Delete {preferredUsername}",
"Deleting comment": "Deleting comment",
@@ -453,6 +455,7 @@
"Explore events": "Explore events",
"Explore!": "Explore!",
"Export": "Export",
"External links":"External links",
"External provider URL": "External provider URL",
"External registration": "External registration",
"Failed to get location.": "Failed to get location.",
@@ -1283,6 +1286,7 @@
"This profile is from another instance, the informations shown here may be incomplete.": "This profile is from another instance, the informations shown here may be incomplete.",
"This profile is located on this instance, so you need to {access_the_corresponding_account} to suspend it.": "This profile is located on this instance, so you need to {access_the_corresponding_account} to suspend it.",
"This profile was not found": "This profile was not found",
"This section lets you add links to external websites to the menu.":"This section lets you add links to external websites to the menu.",
"This setting will be used to display the website and send you emails in the correct language.": "This setting will be used to display the website and send you emails in the correct language.",
"This user doesn't have any profiles": "This user doesn't have any profiles",
"This user was not found": "This user was not found",

View File

@@ -47,6 +47,7 @@
"Add an instance": "Ajouter une instance",
"Add a contact": "Ajouter un contact",
"Add a new post": "Ajouter un nouveau billet",
"Add a new link": "Ajouter un nouveau lien",
"Add a note": "Ajouter une note",
"Add a recipient": "Ajouter un·e destinataire",
"Add a todo": "Ajouter un todo",
@@ -337,6 +338,7 @@
"Delete this conversation": "Supprimer cette conversation",
"Delete this discussion": "Supprimer cette discussion",
"Delete this identity": "Supprimer cette identité",
"Delete this link": "Supprimer ce lien",
"Delete your identity": "Supprimer votre identité",
"Delete {eventTitle}": "Supprimer {eventTitle}",
"Delete {preferredUsername}": "Supprimer {preferredUsername}",
@@ -448,6 +450,7 @@
"Explore!": "Explorer !",
"Explore": "Explorer",
"Export": "Export",
"External links":"Liens externes",
"External provider URL": "URL du fournisseur externe",
"External registration": "Inscription externe",
"Ex: mobilizon.fr": "Ex : mobilizon.fr",
@@ -1289,6 +1292,7 @@
"This profile is from another instance, the informations shown here may be incomplete.": "Ce profil provient d'une autre instance, les informations montrées ici peuvent être incomplètes.",
"This profile is located on this instance, so you need to {access_the_corresponding_account} to suspend it.": "Ce profil se situe sur cette instance, vous devez donc {access_the_corresponding_account} afin de le suspendre.",
"This profile was not found": "Ce profil n'a pas été trouvé",
"This section lets you add links to external websites to the menu.": "Cette section vous permet d'ajouter des liens vers des sites internets externes au menu.",
"This setting will be used to display the website and send you emails in the correct language.": "Ce paramètre sera utilisé pour l'affichage du site et pour vous envoyer des courriels dans la bonne langue.",
"This URL doesn't seem to be valid": "Cette URL ne semble pas être valide",
"This URL is not supported": "Cette URL n'est pas supportée",

View File

@@ -28,6 +28,7 @@ export interface IAdminSettings {
contact: string;
instanceLogo: IMedia | null;
instanceFavicon: IMedia | null;
externalUrls: { url: string; label: string; enabled: boolean }[];
defaultPicture: IMedia | null;
primaryColor: string;
secondaryColor: string;

View File

@@ -458,6 +458,47 @@
</o-field>
</section>
<section class="mt-4 mb-4 p-4 border rounded shadow-sm bg-white">
<h2>{{ t("External links") }}</h2>
<small>
{{
t(
"This section lets you add links to external websites to the menu."
)
}}
</small>
<o-field>
<o-button :label="t('Add a new link')" @click="addLink" />
</o-field>
<div
class="mt-5 grid lg:grid-cols-[repeat(auto-fit,minmax(250px,0.5fr))] grid-cols-[repeat(auto-fit,minmax(250px,1fr))] gap-2"
v-if="settingsToWrite.externalUrls.length > 0"
>
<div
class="bg-mbz-yellow-alt-100 p-5"
v-for="(link, index) in settingsToWrite.externalUrls"
:key="index"
>
<o-field :label="t('URL')" class="!mt-0"
><o-input expanded v-model="link.url" type="text"
/></o-field>
<o-field :label="t('Label')"
><o-input expanded v-model="link.label" type="text"
/></o-field>
<o-field
><o-checkbox v-model="link.enabled" :label="t('Enabled')"
/></o-field>
<o-field>
<o-button
:label="t('Delete this link')"
variant="danger"
@click="deleteLink(index)"
/></o-field>
</div>
</div>
</section>
<o-button native-type="submit" variant="primary">{{
t("Save instance settings")
}}</o-button>
@@ -479,7 +520,7 @@ import {
import { IAdminSettings, ILanguage } from "@/types/admin.model";
import RouteName from "@/router/name";
import { useMutation, useQuery } from "@vue/apollo-composable";
import { ref, computed, watch, inject } from "vue";
import { ref, computed, watch, inject, toRaw } from "vue";
import { useI18n } from "vue-i18n";
import { useHead } from "@/utils/head";
import type { Notifier } from "@/plugins/notifier";
@@ -515,6 +556,7 @@ const defaultAdminSettings: IAdminSettings = {
registrationsOpen: false,
registrationsModeration: false,
instanceLanguages: [],
externalUrls: [],
};
const { onResult: onAdminSettingsResult } = useQuery<{
@@ -556,9 +598,25 @@ useHead({
const settingsToWrite = ref<IAdminSettings>(defaultAdminSettings);
watch(adminSettings, () => {
settingsToWrite.value = { ...adminSettings.value };
// We need to use structuredClone to clone deep properties of adminSettings (like externalUrls)
// {... } only shadow clone, so externalUrls is not reactive doing this
if (adminSettings.value) {
settingsToWrite.value = structuredClone(toRaw(adminSettings.value));
}
});
const addLink = () => {
settingsToWrite.value.externalUrls.push({
url: "",
label: "",
enabled: false,
});
};
const deleteLink = (index: number) => {
settingsToWrite.value.externalUrls.splice(index, 1);
};
const filteredLanguages = ref<string[]>([]);
const registrationsMode = computed({
@@ -614,14 +672,15 @@ const {
onDone: saveAdminSettingsDone,
onError: saveAdminSettingsError,
} = useMutation(SAVE_ADMIN_SETTINGS, () => ({
// We need to update the cache because we just changed admin settings
// We want to update the related query ADMIN_SETTINGS
update(cache, { data }) {
if (!data?.saveAdminSettings) {
console.error("can't acces new admin settings");
return;
}
// We need to update the cache because we just changed admin settings
// We want to update the related query ADMIN_SETTINGS
// Usefull if we comeback to this page
cache.writeQuery({
query: ADMIN_SETTINGS,
data: { adminSettings: data?.saveAdminSettings },