Modification de l'éditeur de contenu
This commit is contained in:
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
<div :class="['w-full', sideBarStore.sidebarWidth]">
|
<div :class="['w-full', sideBarStore.sidebarWidth]">
|
||||||
<div v-if="!brandingStore.loading"
|
<div v-if="!brandingStore.loading"
|
||||||
class="min-h-screen justify-center items-center py-10"
|
class="min-h-screen justify-center items-center"
|
||||||
:style="{ backgroundColor: brandingStore.colors.background }">
|
:style="{ backgroundColor: brandingStore.colors.background }">
|
||||||
<router-view class="p-2"></router-view>
|
<router-view class="p-2"></router-view>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {ref} from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import HTMLContentEditor from "@/views/contents/HTMLContentEditor.vue";
|
import HTMLContentEditor from "@/views/contents/HTMLContentEditor.vue";
|
||||||
import QuickyContentEditor from "@/views/contents/QuickyContentEditor.vue";
|
import QuickyContentEditor from "@/views/contents/QuickyContentEditor.vue";
|
||||||
|
import { useCreatorProfileStore } from "@/stores/creatorProfileStore.js";
|
||||||
|
import { useClient } from "@/plugins/api.js";
|
||||||
|
|
||||||
const showQuickyEditor = ref(true);
|
const showQuickyEditor = ref(true);
|
||||||
const showHtmlEditor = ref(false);
|
const showHtmlEditor = ref(false);
|
||||||
@@ -15,20 +17,53 @@ const toggleHtmlEditor = () => {
|
|||||||
showHtmlEditor.value = true;
|
showHtmlEditor.value = true;
|
||||||
showQuickyEditor.value = false;
|
showQuickyEditor.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const client = useClient();
|
||||||
|
const creatorProfileStore = useCreatorProfileStore();
|
||||||
|
const creatorData = ref(null);
|
||||||
|
const isLoading = ref(true); // Indicateur de chargement
|
||||||
|
|
||||||
|
const fetchCreatorData = async () => {
|
||||||
|
const creatorName = creatorProfileStore.creator?.name;
|
||||||
|
|
||||||
|
if (!creatorName) {
|
||||||
|
console.error("Nom du créateur introuvable dans le store.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await client.get(`/api/creators/@${creatorName}`);
|
||||||
|
creatorData.value = response.data;
|
||||||
|
console.log("Données du créateur récupérées :", creatorData.value);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Erreur lors de la récupération des données du créateur : ${error.response?.data || error.message}`);
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false; // Indique que le chargement est terminé
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Appeler la fonction lors du montage du composant
|
||||||
|
onMounted(() => {
|
||||||
|
fetchCreatorData();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col h-screen">
|
<div v-if="isLoading" class="flex items-center justify-center h-screen">
|
||||||
|
Chargement en cours...
|
||||||
|
</div>
|
||||||
|
|
||||||
<header class="text-2xl text-center py-4 bg-gray-200">
|
|
||||||
Création de contenu
|
|
||||||
|
<div v-else class="flex flex-col h-screen" :style="{ backgroundColor: creatorData.colors?.background, color: creatorData?.colors?.onSurface}">
|
||||||
|
<div class="max-w-[1000px] mx-auto shadow-2xl rounded-lg overflow-hidden">
|
||||||
|
<header class="text-2xl text-center py-4" :style="{ backgroundColor: creatorData?.colors?.primary, color: creatorData?.colors?.onPrimary }">
|
||||||
|
Éditeur de contenu
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
||||||
<div class="flex flex-grow">
|
<div class="flex flex-grow">
|
||||||
|
<aside class="side-menu flex flex-col items-center py-6 " :style="{ backgroundColor: creatorData?.colors?.secondary, color: creatorData?.colors?.onSecondary }">
|
||||||
<aside class="side-menu flex flex-col items-center py-6 bg-gray-100 w-1/4">
|
<div class="text-xl uppercase mb-6 px-2">Type de contenu</div>
|
||||||
<div class="text-xl uppercase mb-6">Éditeurs de contenu</div>
|
|
||||||
|
|
||||||
<v-btn
|
<v-btn
|
||||||
:variant="showQuickyEditor ? 'elevated' : 'plain'"
|
:variant="showQuickyEditor ? 'elevated' : 'plain'"
|
||||||
@@ -37,20 +72,12 @@ const toggleHtmlEditor = () => {
|
|||||||
>
|
>
|
||||||
Quicky
|
Quicky
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
|
||||||
<!-- <v-btn-->
|
|
||||||
<!-- :variant="showHtmlEditor ? 'elevated' : 'plain'"-->
|
|
||||||
<!-- @click="toggleHtmlEditor"-->
|
|
||||||
<!-- class="normal-button"-->
|
|
||||||
<!-- >-->
|
|
||||||
<!-- Article-->
|
|
||||||
<!-- </v-btn>-->
|
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<!-- Contenu principal -->
|
<main>
|
||||||
<main class="flex-grow p-6 bg-white">
|
|
||||||
<div v-if="showQuickyEditor">
|
<div v-if="showQuickyEditor">
|
||||||
<quicky-content-editor/>
|
<QuickyContentEditor :creator-data="creatorData" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="showHtmlEditor">
|
<div v-if="showHtmlEditor">
|
||||||
@@ -59,6 +86,7 @@ const toggleHtmlEditor = () => {
|
|||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -77,7 +105,6 @@ const toggleHtmlEditor = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
background-color: #f0f0f0;
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,157 +1,273 @@
|
|||||||
<script setup>
|
<template>
|
||||||
import { useClient } from '@/plugins/api.js';
|
<div class="mx-auto max-w-xl p-4 shadow-md rounded-lg bg-white overflow-y-auto w-[1000px]">
|
||||||
import { ref } from 'vue';
|
<!-- Thumbnail Editor -->
|
||||||
import { v7 } from 'uuid';
|
<div v-if="step === 1" class="flex flex-col items-center">
|
||||||
import { useCreatorProfileStore } from "@/stores/creatorProfileStore.js";
|
<h2 class="text-lg font-bold mb-4">Éditeur de Thumbnail</h2>
|
||||||
|
|
||||||
const client = useClient();
|
<!-- Thumbnail Preview -->
|
||||||
const title = ref('');
|
<div
|
||||||
const message = ref('');
|
class="shadow-md rounded-md bg-gray-50 border custom-border w-[400px] h-[250px] mb-4 flex items-center justify-center cursor-pointer"
|
||||||
const files = ref([]);
|
@click="$refs.thumbnailInput.click()"
|
||||||
const Thumbnail = ref();
|
>
|
||||||
const externalUrls = ref([]);
|
<img
|
||||||
|
v-if="ThumbnailPreview"
|
||||||
|
:src="ThumbnailPreview"
|
||||||
|
class="rounded-md w-full h-full object-cover"
|
||||||
|
alt="Thumbnail Preview"
|
||||||
|
/>
|
||||||
|
<div v-else class="text-gray-500">Cliquez pour sélectionner une image</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
const creatorProfileStore = useCreatorProfileStore();
|
<!-- Titre -->
|
||||||
|
|
||||||
const addUrl = () => {
|
|
||||||
externalUrls.value.push('');
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeUrl = (index) => {
|
|
||||||
externalUrls.value.splice(index, 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
async function publishPost() {
|
|
||||||
// Validation : le thumbnail est obligatoire
|
|
||||||
if (!Thumbnail.value) {
|
|
||||||
alert("Veuillez ajouter un thumbnail avant de publier."); // Utilisez une alerte ou une notification Vuetify
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const formData = new FormData();
|
|
||||||
|
|
||||||
formData.append('id', v7()); // UUID pour l'ID du contenu
|
|
||||||
formData.append('creatorId', creatorProfileStore.creator.id); // Récupère l'ID du créateur
|
|
||||||
formData.append('title', title.value);
|
|
||||||
formData.append('description', message.value);
|
|
||||||
|
|
||||||
// Ajout du thumbnail
|
|
||||||
formData.append('Thumbnail', Thumbnail.value);
|
|
||||||
|
|
||||||
// Ajout des fichiers
|
|
||||||
files.value.forEach(file => {
|
|
||||||
formData.append('files', file); // Ajoute les fichiers au FormData
|
|
||||||
});
|
|
||||||
|
|
||||||
// Ajout des URLs externes
|
|
||||||
externalUrls.value.forEach(url => {
|
|
||||||
formData.append('externalUrls', url); // Les URLs sont ajoutées comme tableau
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await client.post(
|
|
||||||
`/api/contents`,
|
|
||||||
formData,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'multipart/form-data',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log('Content published:', response.data);
|
|
||||||
resetForm(); // Réinitialise le formulaire après le succès
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error publishing content:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const resetForm = () => {
|
|
||||||
title.value = '';
|
|
||||||
message.value = '';
|
|
||||||
files.value = [];
|
|
||||||
Thumbnail.value = null;
|
|
||||||
externalUrls.value = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
const cancelPost = resetForm;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<v-form>
|
|
||||||
<v-card class="text-center rounded-xl w-[600px]">
|
|
||||||
<v-card-title class="font-medium">
|
|
||||||
Créer un Contenu
|
|
||||||
</v-card-title>
|
|
||||||
|
|
||||||
<v-card-text>
|
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="title"
|
v-model="title"
|
||||||
label="Titre"
|
label="Titre"
|
||||||
density="comfortable"
|
density="comfortable"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
clearable
|
clearable
|
||||||
|
class="mb-4 w-[400px]"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- Upload Thumbnail -->
|
||||||
|
<v-file-input
|
||||||
|
ref="thumbnailInput"
|
||||||
|
v-model="Thumbnail"
|
||||||
|
label="Télécharger un Thumbnail"
|
||||||
|
variant="outlined"
|
||||||
|
dropzone
|
||||||
|
clearable
|
||||||
|
class="mb-6 w-[400px]"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Message d'avertissement -->
|
||||||
|
<p v-if="warningMessage" class="text-red-500 text-sm mb-4">{{ warningMessage }}</p>
|
||||||
|
|
||||||
|
<!-- Bouton pour passer à l'étape suivante -->
|
||||||
|
<v-btn color="primary" variant="contained" class="w-[400px]" @click="goToContentEditor">
|
||||||
|
Next
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Content Editor -->
|
||||||
|
<div v-if="step === 2" class="flex flex-col items-center">
|
||||||
|
<h2 class="text-lg font-bold mb-4">Content Editor</h2>
|
||||||
|
|
||||||
|
<!-- Carrousel -->
|
||||||
|
<div class="relative w-[400px] h-[250px] mb-6">
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 flex items-center justify-center bg-black/50 rounded-md"
|
||||||
|
v-if="carouselItems.length === 0"
|
||||||
|
>
|
||||||
|
<p class="text-white">Aucun élément à afficher</p>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<!-- Image ou vidéo en cours -->
|
||||||
|
<img
|
||||||
|
v-if="carouselItems[carouselIndex].includes('blob')"
|
||||||
|
:src="carouselItems[carouselIndex]"
|
||||||
|
class="w-full h-full object-contain rounded-md"
|
||||||
|
alt="Carrousel Image"
|
||||||
|
/>
|
||||||
|
<iframe
|
||||||
|
v-else
|
||||||
|
:src="`${carouselItems[carouselIndex]}?autoplay=1&mute=1`"
|
||||||
|
frameborder="0"
|
||||||
|
allow="autoplay"
|
||||||
|
class="w-full h-full rounded-md"
|
||||||
|
allowfullscreen
|
||||||
|
></iframe>
|
||||||
|
</div>
|
||||||
|
<!-- Flèches pour naviguer -->
|
||||||
|
<button
|
||||||
|
class="absolute top-1/2 left-2 transform -translate-y-1/2 text-white bg-black/50 rounded-full p-2"
|
||||||
|
@click="previousCarouselItem"
|
||||||
|
>
|
||||||
|
<v-icon>mdi-chevron-left</v-icon>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="absolute top-1/2 right-2 transform -translate-y-1/2 text-white bg-black/50 rounded-full p-2"
|
||||||
|
@click="nextCarouselItem"
|
||||||
|
>
|
||||||
|
<v-icon>mdi-chevron-right</v-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Message -->
|
||||||
<v-textarea
|
<v-textarea
|
||||||
v-model="message"
|
v-model="message"
|
||||||
label="Message"
|
label="Message"
|
||||||
density="comfortable"
|
density="comfortable"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
clearable
|
clearable
|
||||||
|
class="mb-4 w-[400px]"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Ajout des URLs externes -->
|
<!-- Ajout des URLs externes -->
|
||||||
<div v-for="(url, index) in externalUrls" :key="index" class="d-flex align-center">
|
<div v-for="(url, index) in externalUrls" :key="index" class="flex items-center mb-2 w-[400px]">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="externalUrls[index]"
|
v-model="externalUrls[index]"
|
||||||
label="URL externe"
|
label="Lien URL"
|
||||||
density="comfortable"
|
density="comfortable"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
class="flex-1"
|
||||||
/>
|
/>
|
||||||
<v-btn icon @click="removeUrl(index)">
|
<v-btn icon @click="removeUrl(index)">
|
||||||
<v-icon>mdi-minus</v-icon>
|
<v-icon>mdi-minus</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</div>
|
</div>
|
||||||
<v-btn icon @click="addUrl">
|
<v-btn icon @click="addUrl" class="mb-6 w-[400px]">
|
||||||
<v-icon>mdi-plus</v-icon>
|
<v-icon>mdi-plus</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
|
||||||
<!-- Ajout du thumbnail -->
|
<!-- Upload Images -->
|
||||||
<v-file-input
|
|
||||||
v-model="Thumbnail"
|
|
||||||
label="Thumbnail (obligatoire)"
|
|
||||||
variant="outlined"
|
|
||||||
dropzone
|
|
||||||
clearable
|
|
||||||
:rules="[value => !!value || 'Un thumbnail est obligatoire']"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Ajout des fichiers -->
|
|
||||||
<v-file-input
|
<v-file-input
|
||||||
v-model="files"
|
v-model="files"
|
||||||
label="Images"
|
label="Télécharger des Images"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
multiple
|
multiple
|
||||||
dropzone
|
dropzone
|
||||||
|
class="mb-6 w-[400px]"
|
||||||
/>
|
/>
|
||||||
</v-card-text>
|
|
||||||
|
|
||||||
<v-card-actions>
|
<!-- Boutons de navigation -->
|
||||||
<v-btn
|
<div class="flex w-[400px] justify-between">
|
||||||
variant="flat"
|
<v-btn variant="outlined" @click="step = 1">
|
||||||
@click="cancelPost"
|
Back
|
||||||
>
|
|
||||||
Annuler
|
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
<v-btn color="primary" variant="contained" @click="publishPost">
|
||||||
<v-btn
|
|
||||||
variant="flat"
|
|
||||||
color="primary"
|
|
||||||
@click="publishPost"
|
|
||||||
>
|
|
||||||
Publier
|
Publier
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-card-actions>
|
</div>
|
||||||
</v-card>
|
</div>
|
||||||
</v-form>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, watch } from "vue";
|
||||||
|
import { useClient } from "@/plugins/api.js";
|
||||||
|
import { v7 } from "uuid";
|
||||||
|
import { useCreatorProfileStore } from "@/stores/creatorProfileStore.js";
|
||||||
|
|
||||||
|
const client = useClient();
|
||||||
|
const step = ref(1); // Étape actuelle (1 = Thumbnail Editor, 2 = Content Editor)
|
||||||
|
const title = ref('');
|
||||||
|
const message = ref('');
|
||||||
|
const files = ref([]);
|
||||||
|
const Thumbnail = ref();
|
||||||
|
const ThumbnailPreview = ref(null);
|
||||||
|
const externalUrls = ref([]);
|
||||||
|
const warningMessage = ref(''); // Message d'avertissement pour le "Next"
|
||||||
|
const creatorProfileStore = useCreatorProfileStore();
|
||||||
|
const carouselIndex = ref(0); // Index actif dans le carrousel
|
||||||
|
|
||||||
|
// Préparer les données du carrousel
|
||||||
|
const carouselItems = computed(() => {
|
||||||
|
const images = files.value.map(file => URL.createObjectURL(file));
|
||||||
|
const videos = externalUrls.value.filter(url => url.trim() !== '');
|
||||||
|
return [...images, ...videos];
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(Thumbnail, (newFile) => {
|
||||||
|
if (newFile) {
|
||||||
|
ThumbnailPreview.value = URL.createObjectURL(newFile);
|
||||||
|
} else {
|
||||||
|
ThumbnailPreview.value = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Gestion des URLs externes
|
||||||
|
const addUrl = () => externalUrls.value.push('');
|
||||||
|
const removeUrl = (index) => externalUrls.value.splice(index, 1);
|
||||||
|
|
||||||
|
// Navigation dans le carrousel
|
||||||
|
const nextCarouselItem = () => {
|
||||||
|
if (carouselItems.value.length > 0) {
|
||||||
|
carouselIndex.value = (carouselIndex.value + 1) % carouselItems.value.length;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const previousCarouselItem = () => {
|
||||||
|
if (carouselItems.value.length > 0) {
|
||||||
|
carouselIndex.value =
|
||||||
|
(carouselIndex.value - 1 + carouselItems.value.length) %
|
||||||
|
carouselItems.value.length;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Passe à l'étape suivante avec validation
|
||||||
|
const goToContentEditor = () => {
|
||||||
|
if (!title.value || !Thumbnail.value) {
|
||||||
|
warningMessage.value = 'Veuillez sélectionner un thumbnail et entrer un titre avant de continuer.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
warningMessage.value = '';
|
||||||
|
step.value = 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Réinitialise le formulaire
|
||||||
|
const resetForm = () => {
|
||||||
|
title.value = '';
|
||||||
|
message.value = '';
|
||||||
|
files.value = [];
|
||||||
|
Thumbnail.value = null;
|
||||||
|
ThumbnailPreview.value = null;
|
||||||
|
externalUrls.value = [];
|
||||||
|
step.value = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
const publishPost = async () => {
|
||||||
|
if (!Thumbnail.value) {
|
||||||
|
alert("Veuillez ajouter un thumbnail avant de publier.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('id', v7());
|
||||||
|
formData.append('creatorId', creatorProfileStore.creator.id);
|
||||||
|
formData.append('title', title.value);
|
||||||
|
formData.append('description', message.value);
|
||||||
|
formData.append('Thumbnail', Thumbnail.value);
|
||||||
|
|
||||||
|
files.value.forEach(file => formData.append('files', file));
|
||||||
|
externalUrls.value.forEach(url => formData.append('externalUrls', url));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await client.post(`/api/contents`, formData, {
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data' },
|
||||||
|
});
|
||||||
|
console.log('Content published:', response.data);
|
||||||
|
resetForm();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error publishing content:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.relative {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.absolute {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
.transform {
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
.bg-black\/50 {
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
.rounded-md {
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
}
|
||||||
|
.text-white {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.p-2 {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
.text-red-500 {
|
||||||
|
color: #f56565;
|
||||||
|
}
|
||||||
|
.custom-border {
|
||||||
|
border-color: #eaebec;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user