Merged PR 96: CreatorPage into main

This commit is contained in:
Dominic Villemure
2024-07-09 16:21:13 +00:00
29 changed files with 788 additions and 653 deletions

View File

@@ -1,5 +1,9 @@
# Hutopia
## To Do Link :
https://docs.google.com/document/d/1xY9KtOGBx80ICbwUJWReO4ZEOIvw_D4quMcRzZ-nMWo/edit?usp=sharing
Hutopia frontEnd. Using vue3 and vuetify3.
## Recommended IDE Setup

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

View File

@@ -36,7 +36,7 @@ import {eventBus} from '@/eventBus.js';
import {useUserStore} from "@/stores/user.js";
import {useClient} from "@/plugins/api.js";
const hideSideBar = ref(false);
const hideSideBar = ref(true);
const isUserLoaded = ref(false);
const showPopup = ref(false);
const popup = ref(null);

View File

@@ -1,5 +1,5 @@
import {createRouter, createWebHistory} from 'vue-router'
import ARPS from '@/views/manualusers/ARPS.vue'
import { createRouter, createWebHistory } from 'vue-router'
import { useUserStore } from "@/stores/user.js";
import GuillaumeAime from '@/views/manualusers/GuillaumeAime.vue'
import About from '@/views/documentation/About.vue'
import ContentPolicy from '@/views/documentation/ContentPolicy.vue'
@@ -13,26 +13,43 @@ import PaymentCompleted from '../views/PayementCompleted.vue'
import SignupView from '../views/SignupView.vue'
import Join from '../views/main/Join.vue'
import Home from '../views/main/Home.vue'
import Browse from '../views/main/Browse.vue'
import Wallet from '../views/main/Wallet.vue'
import Profile from '../views/main/Profile.vue'
import ChloeBeaugrand from '../views/manualusers/ChloeProfile.vue'
import Leffet from '../views/manualusers/LeffetProfile.vue'
import MathieuCaron from '../views/manualusers/MathieuCaron.vue'
import Creator from "@/views/main/Creator.vue"
import Feed from '../views/main/Feed.vue';
import CreatorList from '../views/creators/CreatorList.vue'
import CreatorPage from "@/views/creators/CreatorPage.vue";
import ContentPage from "@/views/contents/ContentPage.vue";
import PostContent from "@/views/contents/PostContent.vue";
const routes = [
{
path: '/',
component: Home,
meta: {hideSideBar: true}
meta: { hideSideBar: true }
},
{
path: '/browse',
component: Browse
component: CreatorList
},
{
path: '/content/:contentId',
component: ContentPage
},
{
path: '/@:creator',
component: CreatorPage
},
{
path: '/creators/@:creator',
component: Profile
},
{
path: '/content/post',
component: PostContent,
},
{
path: '/@leffet',
component: Leffet
@@ -49,62 +66,53 @@ const routes = [
path: '/@mathieucaron',
component: MathieuCaron
},
{
path: '/@arps',
component: ARPS
},
{
path: '/@:creator',
component: Creator
},
{
path: '/helpandcontact',
component: HelpAndContact,
meta: {hideSideBar: true}
meta: { hideSideBar: true }
},
{
path: '/termsandconditions',
name: 'termsandconditions',
component: TermsAndConditions,
meta: {hideSideBar: true}
meta: { hideSideBar: true }
},
{
path: '/contentpolicy',
name: 'contentpolicy',
component: ContentPolicy,
meta: {hideSideBar: true}
meta: { hideSideBar: true }
},
{
path: '/faq',
name: 'FAQ',
component: FAQ,
meta: {hideSideBar: true}
meta: { hideSideBar: true }
},
{
path: '/guideforcreators',
name: 'guideforcreators',
component: CreatorGuide,
meta: {hideSideBar: true}
meta: { hideSideBar: true }
},
{
path: '/about',
name: 'about',
component: About,
meta: {hideSideBar: true}
meta: { hideSideBar: true }
},
{
path: '/pricing',
name: 'pricing',
component: Pricing,
meta: {hideSideBar: true}
meta: { hideSideBar: true }
},
{
path: '/join',
name: 'join',
component: Join,
meta: {hideSideBar: true}
meta: { hideSideBar: true }
},
{
@@ -115,7 +123,8 @@ const routes = [
{
path: '/profile',
name: 'profile',
component: Profile
component: Profile,
meta: { requiresAuth: true }
},
{
path: '/signup',
@@ -130,16 +139,9 @@ const routes = [
{
path: '/wallet',
name: 'wallet',
component: Wallet
},
{
path: '/feed',
name: 'feed',
component: Feed
},
component: Wallet,
meta: { requiresAuth: true }
}
]
const router = createRouter({
@@ -147,4 +149,20 @@ const router = createRouter({
routes
})
export default router
// Navigation gards
router.beforeEach((to, from, next) => {
const authStore = useUserStore();
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!authStore.user.value || !Object.keys(authStore.user.value).length) {
next('/');
} else {
next();
}
} else {
next();
}
})
export default router;

View File

@@ -69,7 +69,7 @@
</div>
<selected-footer class="py-15"></selected-footer>
<selected-footer></selected-footer>
</template>

View File

@@ -0,0 +1,45 @@
<template>
<div class=" shadow-lg rounded-lg max-w-sm">
<div class="h-48 object-cover bg-purple">
<v-img :src="props.content.url"
v-if="!isHttpUrl">
</v-img>
<iframe v-if="isHttpUrl"
:src="props?.content?.uri"
title="YouTube video player"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen>
</iframe>
</div>
<router-link :to="'content/' + props?.content?.id">
<div class="text-lg font-bold">{{ props.content.title }}</div>
<div class="text-sm text-gray-500">{{ props.content.description }}</div>
</router-link>
</div>
</template>
<script setup>
import {defineProps, computed} from 'vue';
const isHttpUrl = computed(() => props.content?.uri?.startsWith('http'))
const props = defineProps({
content: {
type: Object,
required: true,
}
});
</script>

View File

@@ -0,0 +1,41 @@
<template>
<div class="flex">
<ContentCard v-for="content in contents"
:content="content"
class="m-2 bg-red w-full">
</ContentCard>
</div>
</template>
<script setup>
import {useClient} from '@/plugins/api.js';
import {defineProps, onBeforeMount, ref} from 'vue';
import ContentCard from "./ContentCard.vue";
const props = defineProps({
creatorId: {
type: String,
required: true
}
});
const client = useClient();
const contents = ref();
onBeforeMount(async () => {
if (props.creatorId == null) return
try {
const response = await client.get(`/api/contents/user/${props.creatorId}`)
if (response.status >= 200 && response.status < 300) {
contents.value = response.data
console.table(contents.value)
}
} catch (error) {
console.error("Failed to fetch posts", error);
}
})
</script>

View File

@@ -0,0 +1,74 @@
<template>
<div class="flex flex-column w-full">
<div class="border-b-2 p-6 font-sans space-y-2">
<div v-if="data && data.uri">
Uri: {{ data?.uri }}
</div>
<div v-if="data && data.title" class="font-semibold">
Title: {{ data?.title }}
</div>
<div v-if="data && data.description">
Description: {{ data?.description }}
</div>
<div v-if="data && data.createdAt">
Date: {{ data?.createdAt }}
</div>
<div v-if="data && data.createdBy">
Creator: {{ data?.createdBy }}
</div>
</div>
<div class="border-b-2 p-6">
<PostMessage :content-id="contentId">
</PostMessage>
</div>
<div class="border-b-2 p-6">
<h2 class="font-sans font-semibold">Commentaires</h2>
<MessageList :content-id="contentId">
</MessageList>
</div>
</div>
</template>
<script setup async>
import PostMessage from "@/views/messages/PostMessage.vue";
import MessageList from "@/views/messages/MessageList.vue";
import {useClient} from "@/plugins/api.js";
import {onMounted, watch, ref, computed} from 'vue';
import {useRoute} from 'vue-router';
const data = ref()
const route = useRoute()
const client = useClient()
const contentId = computed(() => {
return route.params.contentId;
});
const fetchContentData = async (contentId) => {
try {
const response = await client.get(`/api/contents/${contentId}`)
data.value = response.data
} catch (error) {
console.error(`Error fetching content: ${error}`)
}
}
onMounted(() => {
fetchContentData(contentId.value);
});
watch(contentId, (newContentId) => {
fetchContentData(newContentId);
});
</script>

View File

@@ -0,0 +1,106 @@
<template>
<div class="flex flex-column">
<div class="h-full bg-yellow p-2 rounded-2xl m-4">
<post-content-menu></post-content-menu>
</div>
<div class="flex flex-column m-4 gap-4">
<v-form>
<v-file-input
v-model="selectedFile"
label="Select Image"
accept="image/*"
prepend-icon="mdi-camera"
@change="onFileSelected">
</v-file-input>
<v-img
v-if="url"
:src="url"
max-height="375"
contain
></v-img>
<v-text-field
v-model="title"
density="comfortable"
variant="outlined"
label="Titre"
hide-details
clearable>
</v-text-field>
<v-text-field
v-model="description"
density="comfortable"
variant="outlined"
label="Description"
hide-details
clearable>
</v-text-field>
</v-form>
</div>
<div class="flex flex-row gap-2 p-2 justify-end">
<v-btn style="border-radius: 20px" variant="text">Canceller</v-btn>
<v-btn style="border-radius: 20px" @click="publish">Publier</v-btn>
</div>
</div>
</template>
<script setup>
// import posts from "@/views/posts/posts.json";
import {useClient} from '@/plugins/api.js';
import {defineProps, ref} from 'vue';
import PostContentMenu from "@/views/contents/PostContentMenu.vue";
const props = defineProps({
contentId: {
type: String,
required: true
}
});
const client = useClient();
const selectedFile = ref("");
const url = ref("");
const title = ref("");
const description = ref("");
const onFileSelected = () => {
if (selectedFile.value) {
const fileReader = new FileReader();
fileReader.readAsDataURL(selectedFile.value);
fileReader.onload = () => {
url.value = fileReader.result;
};
}
};
const publish = async () => {
const response = await client.post(
`/api/contents/`,
{
"url": url.value,
"title": title.value,
"description": description.value
})
if (response.status !== 200) {
console.info(`Content created!`)
} else {
console.error(`Failed to create content ${response.data}`)
}
}
</script>

View File

@@ -0,0 +1,148 @@
<template>
<div class="flow">
<button @click="selectType('title')">
<v-icon>mdi-format-title</v-icon>
</button>
<button @click="selectType('text')">
<v-icon>mdi-text</v-icon>
</button>
<button @click="selectType('image')">
<v-icon>mdi-image</v-icon>
</button>
<button @click="selectType('video')">
<v-icon>mdi-video</v-icon>
</button>
<button @click="selectType('audio')">
<v-icon>mdi-volume-high</v-icon>
</button>
<button @click="selectType('comments')">
<v-icon>mdi-comment</v-icon>
</button>
</div>
<!-- Affichage du contenu en fonction du type sélectionné -->
<!-- <v-card-text>-->
<!-- <v-row v-for="(content, index) in contents" :key="index" class="draggable-row"-->
<!-- @dragstart="dragStart(index)" @dragover.prevent @drop="drop(index)" draggable="true">-->
<!-- <v-col cols="10">-->
<!-- <template v-if="content.type === 'title'">-->
<!-- <v-text-field v-model="content.value" label="Titre"></v-text-field>-->
<!-- </template>-->
<!-- <template v-else-if="content.type === 'text'">-->
<!-- <v-textarea v-model="content.value" label="Texte"></v-textarea>-->
<!-- </template>-->
<!-- <template v-else-if="content.type === 'image'">-->
<!-- <v-row>-->
<!-- <v-col cols="12">-->
<!-- <v-file-input v-model="content.value" label="Image"></v-file-input>-->
<!-- </v-col>-->
<!-- </v-row>-->
<!-- </template>-->
<!-- <template v-else-if="content.type === 'video'">-->
<!-- <v-text-field v-model="content.value" label="URL de la vidéo"></v-text-field>-->
<!-- </template>-->
<!-- <template v-else-if="content.type === 'audio'">-->
<!-- <v-row>-->
<!-- <v-col cols="2">-->
<!-- <v-icon>mdi-volume-high</v-icon>-->
<!-- </v-col>-->
<!-- <v-col cols="10">-->
<!-- <v-file-input v-model="content.value" label="Audio"></v-file-input>-->
<!-- </v-col>-->
<!-- </v-row>-->
<!-- </template>-->
<!-- <template v-else-if="content.type === 'comments'">-->
<!-- <v-text-field v-model="content.value" label="Commentaires"></v-text-field>-->
<!-- </template>-->
<!-- </v-col>-->
<!-- <v-col cols="2" class="d-flex justify-center align-center">-->
<!-- <button icon @click="removeContent(index)" class="remove-button">-->
<!-- <v-icon>mdi-close</v-icon>-->
<!-- </button>-->
<!-- </v-col>-->
<!-- </v-row>-->
<!-- </v-card-text>-->
<!-- &lt;!&ndash; Boutons Post, Preview et Cancel &ndash;&gt;-->
<!-- <v-row v-if="contents.length > 0" justify="end" style="margin-bottom: 10px;">-->
<!-- <v-col class="d-flex justify-end" style="margin-right: 4%;">-->
<!-- <button style="margin-right: 15px;" @click="postContent" color="white" dark-->
<!-- elevation="4">Post-->
<!-- </button>-->
<!-- <button style="margin-right: 15px;" @click="previewContent" color="white" dark-->
<!-- elevation="5">Preview-->
<!-- </button>-->
<!-- <button @click="cancelPost" color="white" dark elevation="5">Cancel</button>-->
<!-- </v-col>-->
<!-- </v-row>-->
</template>
<script setup>
import {ref} from 'vue';
const contents = ref([]);
let dragIndex = null;
const selectType = (type) => {
console.log("Type sélectionné:", type);
contents.value.push({type: type, value: ''});
};
const removeContent = (index) => {
contents.value.splice(index, 1);
};
const postContent = () => {
// Implémenter la logique pour poster le contenu
};
const previewContent = () => {
// Implémenter la logique pour prévisualiser le contenu
};
const cancelPost = () => {
if (contents.value.length > 0) {
// Réinitialiser le tableau contents pour supprimer tous les contenus
contents.value = [];
}
};
const dragStart = (index) => {
dragIndex = index;
};
const drop = (index) => {
if (dragIndex !== null && index !== null) {
const draggedItem = contents.value[dragIndex];
contents.value.splice(dragIndex, 1);
contents.value.splice(index, 0, draggedItem);
dragIndex = null;
}
};
</script>
<style scoped>
.remove-button {
display: flex;
justify-content: center;
align-items: center;
margin-top: -20%;
}
.toolbar-button {
margin-top: 10px;
margin-bottom: -15px;
}
.draggable-row {
cursor: grab;
}
</style>

View File

@@ -0,0 +1,40 @@
[
{
"id": "00000002-0000-0000-0000-000000000001",
"url": "https://www.youtube.com/embed/neKWqjE0eSs?si=Bo7xbYaw9-56w3lU",
"description": "Bonjour, Nous sommes fiers de vous présenter la version 0.10 d'Hutopy. Nous sommes au début d'une aventure visant à transformer la sphère des médias sociaux. Notre objectif est d'apporter un souffle de fraîcheur en favorisant des interactions plus constructives entre les individus. Dans cette première version, seuls nos utilisateurs testeurs ont accès à la plateforme comme créateur. Dans un futur proche, avec le déploiement de la version 0.2, nous contacterons les personnes qui se sont inscrites via l'onglet d'inscription et ont rempli quelques questions. Si vous souhaitez soutenir le développement de la plateforme, deux options s'offrent à vous. La première consiste à nous apporter un soutien financier, tandis que la seconde est de nous contacter pour faire partie de l'équipe de développement. Nous recherchons actuellement un programmeur supplémentaire pour faire progresser certaines fonctionnalités. Merci de visiter Hutopy.",
"created_by": {
"id": "00000001-0000-0000-0000-000000000001",
"alias": "@marchy"
},
"created_at": "",
"thumb_up_count": 0,
"thumb_down_count": 0
},
{
"id": "00000002-0000-0000-0000-000000000002",
"url": "/images/usersmedia/HutopyProfile/pictures/version.png",
"title": "Déploiement de la version 0.10 d'Hutopy",
"description": "Bonjour, Nous sommes fiers de vous présenter la version 0.10 d'Hutopy. Nous sommes au début d'une aventure visant à transformer la sphère des médias sociaux. Notre objectif est d'apporter un souffle de fraîcheur en favorisant des interactions plus constructives entre les individus. Dans cette première version, seuls nos utilisateurs testeurs ont accès à la plateforme comme créateur. Dans un futur proche, avec le déploiement de la version 0.2, nous contacterons les personnes qui se sont inscrites via l'onglet d'inscription et ont rempli quelques questions. Si vous souhaitez soutenir le développement de la plateforme, deux options s'offrent à vous. La première consiste à nous apporter un soutien financier, tandis que la seconde est de nous contacter pour faire partie de l'équipe de développement. Nous recherchons actuellement un programmeur supplémentaire pour faire progresser certaines fonctionnalités. Merci de visiter Hutopy.",
"created_by": {
"id": "00000001-0000-0000-0000-000000000001",
"alias": "@marchy",
"portrait_url": ""
},
"created_at": "",
"thumb_up_count": 0,
"thumb_down_count": 0
},
{
"id": "00000002-0000-0000-0000-000000000003",
"url": "/images/usersmedia/HutopyProfile/pictures/version.png",
"description": "Bonjour, Nous sommes fiers de vous présenter la version 0.10 d'Hutopy. Nous sommes au début d'une aventure visant à transformer la sphère des médias sociaux. Notre objectif est d'apporter un souffle de fraîcheur en favorisant des interactions plus constructives entre les individus. Dans cette première version, seuls nos utilisateurs testeurs ont accès à la plateforme comme créateur. Dans un futur proche, avec le déploiement de la version 0.2, nous contacterons les personnes qui se sont inscrites via l'onglet d'inscription et ont rempli quelques questions. Si vous souhaitez soutenir le développement de la plateforme, deux options s'offrent à vous. La première consiste à nous apporter un soutien financier, tandis que la seconde est de nous contacter pour faire partie de l'équipe de développement. Nous recherchons actuellement un programmeur supplémentaire pour faire progresser certaines fonctionnalités. Merci de visiter Hutopy.",
"created_by": {
"id": "00000001-0000-0000-0000-000000000001",
"alias": "@marchy"
},
"created_at": "",
"thumb_up_count": 0,
"thumb_down_count": 0
}
]

View File

@@ -1,37 +1,20 @@
<template>
<template>
<!-- Bannière-->
<div class="relative bottom-4 z-20 m">
<div class="relative flex flex-col">
<!-- Social Network-->
<div class="bg-black bg-opacity-50 flex flex-col md:flex-row items-center justify-between py-2 px-4 min-h-24">
<div class="bg-opacity-50 flex flex-col md:flex-row items-center justify-between py-2 px-4 min-h-24" :style="{ backgroundColor: creator.profileColors.bannerTop || '#6B0065' }">
<div class="text-white mb-2 md:mb-0 w-full md:w-auto flex justify-between md:block">
<div class="text-lg">1000+ Abonnés</div>
<div class="text-sm">500 Contacts en commun</div>
</div>
<div class="grid grid-cols-6 md:flex flex-wrap space-x-0 md:space-x-10 gap-6 ">
<a href="https://facebook.com" target="_blank"
<a
v-for="socialNetwork in GetActiveSocialNetworkUrls()"
:href="socialNetwork.url" target="_blank"
class="text-white text-2xl transform transition-transform duration-200 hover:scale-125 hover:text-blue-500">
<v-icon>mdi-facebook</v-icon>
</a>
<a href="https://twitter.com" target="_blank"
class="text-white text-2xl transform transition-transform duration-200 hover:scale-125 hover:text-blue-400">
<v-icon>mdi-twitter</v-icon>
</a>
<a href="https://instagram.com" target="_blank"
class="text-white text-2xl transform transition-transform duration-200 hover:scale-125 hover:text-pink-500">
<v-icon>mdi-instagram</v-icon>
</a>
<a href="https://tiktok.com" target="_blank"
class="transform transition-transform duration-200 hover:scale-110">
<img src="/images/hutopymedia/icons/white/tiktokwhite.png" alt="TikTok" class="w-9 h-9">
</a>
<a href="https://youtube.com" target="_blank"
class="text-white text-2xl transform transition-transform duration-200 hover:scale-125 hover:text-red-500">
<v-icon>mdi-youtube</v-icon>
</a>
<a href="https://yoursite.com" target="_blank"
class="text-white text-2xl transform transition-transform duration-200 hover:scale-125 hover:text-gray-500">
<v-icon>mdi-web</v-icon>
<v-icon v-if="socialNetwork.icon.includes('mdi')">{{ socialNetwork.icon }}</v-icon>
<img v-if="socialNetwork.icon.includes('tiktok')" src="/images/hutopymedia/icons/white/tiktokwhite.png" alt="TikTok" class="w-9 h-9">
</a>
</div>
</div>
@@ -42,14 +25,20 @@
<div class="relative">
<!--Banner-->
<div>
<img class=" w-full drop-shadow-[0_15px_10px_rgba(0,0,0,0.7)]" :src="imageSrc" alt="Profile Banner">
<img class=" w-full drop-shadow-[0_15px_10px_rgba(0,0,0,0.7)]" :src="creator.storedDataUrls.bannerPictureUrl || '/images/hutopymedia/banners/tutorialbanner.png'" alt="Profile Banner" style="max-height: 550px">
</div>
<!--User info -->
<div class="absolute top-1/2 right-10 text-white z-30 transform -translate-y-1/2">
<div class="text-white">
<div class="text-2xl sm:text-3xl md:text-2xl lg:text-4xl xl:text-6xl font-bold">Guillaume</div>
<div class="text-2xl sm:text-3xl md:text-2xl lg:text-4xl xl:text-6xl font-bold">Mousseau</div>
<div class="text-1xl sm:text-xl md:text-lg lg:text-2xl xl:text-3xl">Agence Créative</div>
<div class="text-2xl sm:text-3xl md:text-2xl lg:text-4xl xl:text-6xl font-bold">
<p :style="{ color: creator.profileColors.accent }">{{ creator.firstName }}</p>
</div>
<div class="text-2xl sm:text-3xl md:text-2xl lg:text-4xl xl:text-6xl font-bold">
<p :style="{ color: creator.profileColors.accent }">{{ creator.lastName }}</p>
</div>
<div class="text-1xl sm:text-xl md:text-lg lg:text-2xl xl:text-3xl">
<p :style="{ color: creator.profileColors.accent }">{{ creator.occupation }}</p>
</div>
</div>
</div>
</div>
@@ -57,14 +46,15 @@
<!-- Actions Button & image profile -->
<div class="relative bottom-4 w-full">
<div class="bg-gray-800 py-4 relative shadow-lg" style="background-color: #24120E">
<div class="bg-gray-800 py-4 relative shadow-lg min-h-24" :style="{ backgroundColor: creator.profileColors.bannerBottom || '#A30E79' }">
<div class="flex flex-col sm:flex-row items-center sm:justify-between">
<img
class="left-5 rounded-full border-solid border-2 sm:absolute sm:top-1/2 sm:transform sm:-translate-y-1/2 md:left-20 z-20"
:src="profilePicture"
:src="creator.storedDataUrls.profilePictureUrl || '/images/usersmedia/HutopyProfile/profilepictures/profileHutopyProfile01.png'"
alt="Profile Picture"
style="border-color: rgb(70, 37, 24); max-width: 250px; width: 100%;">
<div class="flex flex-wrap sm:flex-nowrap items-center mt-4 sm:mt-0 sm:ml-auto space-x-2 sm:space-x-4">
:style="{ borderColor: creator.profileColors.accent || '#A30E79', maxWidth: '250px', width: '100%' }"
>
<div v-if="creator.id === userStore.getCurrentUser().id" class="flex flex-wrap sm:flex-nowrap items-center mt-4 sm:mt-0 sm:ml-auto space-x-2 sm:space-x-4">
<button
class="flex items-center text-white transform transition-transform duration-200 hover:text-gray-300 hover:scale-125"
@click="isDialogActive = true">
@@ -88,13 +78,11 @@
</div>
</div>
<!--Post-modale-->
<v-dialog v-model="isDialogActive" max-width="500">
<template v-slot:default="{ isActive }">
<v-card class="text-center rounded-xl">
<div class="text-white p-4 rounded-t" style="background-color: #110E0F">
<div class="text-white p-4 rounded-t" :style="{backgroundColor: creator.profileColors.menu || '#A30E79'}">
<h2>Publication</h2>
</div>
<v-card-text class="bg">
@@ -123,25 +111,25 @@
</v-card>
</template>
</v-dialog>
</template>
<script setup>
import CreatorFeed from "@/views/main/CreatorFeed.vue";
import {ref} from 'vue';
import DonationPopup from "@/views/main/DonationPopup.vue";
let imageSrc = '/images/usersmedia/ARPS/banners/bannerARPS01.png';
let profilePicture = '/images/usersmedia/ARPS/profilepictures/profileARPS.png';
let isDialogActive = ref(false);
let message = ref('');
let files = ref([]);
let isArticle = ref(false);
import {defineProps, ref} from "vue";
import {useUserStore} from "@/stores/user.js";
const props = defineProps({
creator: { type: Object, required: true },
});
const userStore = useUserStore();
const isDialogActive = ref(false);
const message = ref('');
const files = ref([]);
const isArticle = ref(false);
const publishPost = () => {
// Logic to publish post
@@ -163,15 +151,30 @@ const resetPostForm = () => {
const togglePostType = (article) => {
isArticle.value = article;
};
</script>
<style scoped>
.shadow-lg {
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.8);
function GetActiveSocialNetworkUrls(){
const socialNetworks = [];
const userSocialNetworks = props.creator.socialNetworks;
if (userSocialNetworks.facebookUrl !== ''){
socialNetworks.push({icon: "mdi-facebook", url: props.creator.socialNetworks.facebookUrl})
}
if (userSocialNetworks.facebookUrl !== ''){
socialNetworks.push({icon: "mdi-twitter", url: props.creator.socialNetworks.xUrl})
}
if (userSocialNetworks.instagramUrl !== ''){
socialNetworks.push({icon: "mdi-instagram", url: props.creator.socialNetworks.instagramUrl})
}
if (userSocialNetworks.tiktokUrl !== ''){
socialNetworks.push({icon: "/images/hutopymedia/icons/white/tiktokwhite.png", url: props.creator.socialNetworks.tikTokUrl})
}
if (userSocialNetworks.youtubeUrl !== ''){
socialNetworks.push({icon: "mdi-youtube", url: props.creator.socialNetworks.youtubeUrl})
}
if (userSocialNetworks.yourWebsiteUrl !== ''){
socialNetworks.push({icon: "mdi-web", url: props.creator.socialNetworks.yourWebsiteUrl})
}
return socialNetworks;
}
.v-btn--active {
background-color: #1976D2;
color: white;
}
</style>
</script>

View File

@@ -1,4 +1,4 @@
<template>
<template>
<div>
@@ -27,10 +27,7 @@
<script setup>
import CreatorCard from "@/views/main/CreatorCard.vue";
import creators from "@/views/main/creators.json"; // Import the JSON file
import CreatorCard from "@/views/creators/CreatorCard.vue";
import creators from "@/views/creators/creators.json";
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,122 @@
<template>
<div v-if="creator && creator.id">
<creator-banner :creator="creator"></creator-banner>
<DonationPopup :creator-id="creator.id"></DonationPopup>
<div class="max-w-[1000px] mx-auto flex flex-row justify-center border-l-2 border-r-2 -mt-6 ">
<div class="w-full mt-20">
<v-card-text>
<v-tabs-window v-model="tab">
<v-tabs-window-item value="content">
<div class="w-full h-full p-6">
<ContentList v-if="creator.id"
:creator-id="creator.id">
</ContentList>
</div>
</v-tabs-window-item>
<v-tabs-window-item value="community">
<div>
<div class="border-b-2 p-6">
<PostMessage v-if="creator.id"
:content-id="creator.id">
</PostMessage>
</div>
<div class="border-b p-6">
<h2 class="font-sans font-semibold">Commentaires</h2>
<MessageList v-if="creator.id"
:content-id="creator.id">
</MessageList>
</div>
</div>
</v-tabs-window-item>
</v-tabs-window>
</v-card-text>
</div>
</div>
</div>
<!-- Fallback when user try to access a non-existing creator -->
<div v-else>
<div v-if="loading">
<v-progress-linear indeterminate></v-progress-linear>
</div>
<div v-else>
<v-card>
<v-card-text style="text-align: center;">
Aucun créateur au nom de {{ route.params.creator }}
</v-card-text>
</v-card>
</div>
</div>
</template>
<script async setup>
import {watch, ref, onBeforeMount} from 'vue';
import {useRoute} from 'vue-router';
import {useClient} from "@/plugins/api.js";
import CreatorBanner from "@/views/creators/CreatorBanner.vue";
import MessageList from "@/views/messages/MessageList.vue";
import ContentList from "@/views/contents/ContentList.vue";
import PostMessage from "@/views/messages/PostMessage.vue";
import DonationPopup from "@/views/main/DonationPopup.vue";
const client = useClient();
const route = useRoute();
const creator = ref(null);
const loading = ref(true);
const tab = ref();
onBeforeMount(async() => {
setTimeout(() => {
loading.value = false;
}, 1500);
await fetchCreatorData(route.params.creator);
});
watch(
() => route.params.creator,
async () => {
loading.value = true;
setTimeout(() => {
loading.value = false;
}, 1000);
await fetchCreatorData(route.params.creator);
}
);
const fetchCreatorData = async (creatorAlias) => {
try {
const response = await client.get(`/api/creators/@${creatorAlias}`)
creator.value = response.data;
console.log('loaded the following creator from api');
console.table(creator.value.id);
} catch (error) {
console.error(`Error fetching content: ${error}`);
}
}
</script>
<style scoped>
.shadow-lg {
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.8);
}
.v-btn--active {
background-color: #1976D2;
color: white;
}
</style>

View File

@@ -1,93 +0,0 @@
<template>
<div v-if="user && user.id">
<CreatorBanner :user="user"></CreatorBanner>
<div class="flex flex-column w-max-[100] ">
<!-- <div class="w-full border-b-2 p-6">-->
<!-- <PostMessage content-id="00000001-0000-0000-0000-000000000001"></PostMessage>-->
<!-- </div>-->
<!-- -->
<!-- <div class="w-full border-b-2 p-6">-->
<!-- <h2 class="font-sans font-semibold">Commentaires</h2>-->
<!-- <MessageList content-id="00000001-0000-0000-0000-000000000001"></MessageList>-->
<!-- </div>-->
<PostContentMenu v-if="user.id === userStore.getCurrentUser().id"></PostContentMenu>
<StripePayment :creator-id="user.id"></StripePayment>
<div class="flex flex-col items-center">
<div class="max-w-[800px] border-l-2 border-r-2 border-gray-200 px-4 ">
<PostCard v-for="post in posts"
:key="post.id"
:post="post"
class=" bg-white w-full content-center rounded-3xl mt-2">
</PostCard>
</div>
</div>
</div>
</div>
<!-- Fallback when user try to access a non-existing creator -->
<div v-else>
<div v-if="loading">
<v-progress-linear indeterminate></v-progress-linear>
</div>
<div v-else>
<v-card>
<v-card-text style="text-align: center;">
Aucun créateur au nom de {{ route.params.creator }}
</v-card-text>
</v-card>
</div>
</div>
</template>
<script async setup>
import PostContentMenu from '@/views/main/PostContentMenu.vue'
import PostCard from "@/views/main/PostCard.vue"
import posts from "@/views/main/posts.json"
import PostMessage from "@/views/messages/PostMessage.vue";
import MessageList from "@/views/messages/MessageList.vue";
import {onBeforeMount, ref, watch} from "vue";
import {useClient} from "@/plugins/api.js";
import {useRoute} from "vue-router";
import CreatorBanner from "@/views/main/CreatorBanner.vue";
import StripePayment from "@/views/StripePayment.vue";
import {useUserStore} from "@/stores/user.js";
const client = useClient();
const route = useRoute();
const userStore = useUserStore();
const user = ref(null);
const loading = ref(true);
onBeforeMount(async() => {
setTimeout(() => {
loading.value = false;
}, 1500);
const response = await client.get(`/api/Users?UserName=${route.params.creator}`)
user.value = response.data
})
watch(
() => route.params.creator,
async () => {
loading.value = true;
setTimeout(() => {
loading.value = false;
}, 1000);
const response = await client.get(`/api/Users?UserName=${route.params.creator}`)
user.value = response.data
}
);
</script>
<style scoped>
</style>

View File

@@ -1,118 +0,0 @@
<template>
<!-- Bannière-->
<div class="relative bottom-4 z-20 m">
<div class="relative flex flex-col">
<!-- Social Network-->
<div class="bg-opacity-50 flex flex-col md:flex-row items-center justify-between py-2 px-4 min-h-24" :style="{ backgroundColor: user.profileColors.bannerTop }">
<div class="text-white mb-2 md:mb-0 w-full md:w-auto flex justify-between md:block">
<div class="text-lg">1000+ Abonnés</div>
<div class="text-sm">500 Contacts en commun</div>
</div>
<div class="grid grid-cols-6 md:flex flex-wrap space-x-0 md:space-x-10 gap-6 ">
<a
v-for="socialNetwork in GetActiveSocialNetworkUrls()"
:href="socialNetwork.url" target="_blank"
class="text-white text-2xl transform transition-transform duration-200 hover:scale-125 hover:text-blue-500">
<v-icon v-if="socialNetwork.icon.includes('mdi')">{{ socialNetwork.icon }}</v-icon>
<img v-if="socialNetwork.icon.includes('tiktok')" src="/images/hutopymedia/icons/white/tiktokwhite.png" alt="TikTok" class="w-9 h-9">
</a>
</div>
</div>
</div>
<!--Banner & user info-->
<div class="relative">
<!--Banner-->
<div>
<img class=" w-full drop-shadow-[0_15px_10px_rgba(0,0,0,0.7)]" :src="user.storedDataUrls.bannerPictureUrl" alt="Profile Banner" style="max-height: 550px">
</div>
<!--User info -->
<div class="absolute top-1/2 right-10 text-white z-30 transform -translate-y-1/2">
<div class="text-white">
<div class="text-2xl sm:text-3xl md:text-2xl lg:text-4xl xl:text-6xl font-bold">
<p :style="{ color: user.profileColors.accent }">{{ user.firstName }}</p>
</div>
<div class="text-2xl sm:text-3xl md:text-2xl lg:text-4xl xl:text-6xl font-bold">
<p :style="{ color: user.profileColors.accent }">{{ user.lastName }}</p>
</div>
<div class="text-1xl sm:text-xl md:text-lg lg:text-2xl xl:text-3xl">
<p :style="{ color: user.profileColors.accent }">{{ user.occupation }}</p>
</div>
</div>
</div>
</div>
</div>
<!-- Actions Button & image profile -->
<div class="relative bottom-4 w-full">
<div class="bg-gray-800 py-4 relative shadow-lg" :style="{ backgroundColor: user.profileColors.bannerBottom }">
<div class="flex flex-col sm:flex-row items-center sm:justify-between">
<img
class="left-5 rounded-full border-solid border-2 sm:absolute sm:top-1/2 sm:transform sm:-translate-y-1/2 md:left-20 z-20"
:src="user.storedDataUrls.profilePictureUrl"
alt="Profile Picture"
:style="{ borderColor: user.profileColors.accent, maxWidth: '250px', width: '100%' }"
>
<div class="flex flex-wrap sm:flex-nowrap items-center mt-4 sm:mt-0 sm:ml-auto space-x-2 sm:space-x-4">
<button
class="flex items-center text-white transform transition-transform duration-200 hover:text-gray-300 hover:scale-125"
@click="isDialogActive = true">
<v-icon style="font-size: 35px; height: 35px; width: 55px;">mdi-text-box-plus-outline</v-icon>
</button>
<button class="text-white py-2 px-4 rounded"
style="background-color: #333; transition: background-color 0.3s ease;"
onmouseover="this.style.backgroundColor='#555';" onmouseout="this.style.backgroundColor='#333';">
S'ABONNER
</button>
<button
class="flex items-center text-white transform transition-transform duration-200 hover:text-white hover:scale-125">
<v-icon style="font-size: 35px; height: 35px; width: 55px;">mdi-comment-text</v-icon>
</button>
<button
class="flex items-center text-white transform transition-transform duration-200 hover:text-gray-300 hover:scale-125">
<v-icon style="font-size: 35px; height: 35px; width: 55px;">mdi-information</v-icon>
</button>
</div>
</div>
</div>
</div>
</template>
<script setup>
import {defineProps, ref} from "vue";
const props = defineProps({
user: { type: Object, required: true },
});
const isDialogActive = ref(false);
function GetActiveSocialNetworkUrls(){
const socialNetworks = [];
const userSocialNetworks = props.user.socialNetworks;
if (userSocialNetworks.facebookUrl !== ''){
socialNetworks.push({icon: "mdi-facebook", url: props.user.socialNetworks.facebookUrl})
}
if (userSocialNetworks.facebookUrl !== ''){
socialNetworks.push({icon: "mdi-twitter", url: props.user.socialNetworks.xUrl})
}
if (userSocialNetworks.instagramUrl !== ''){
socialNetworks.push({icon: "mdi-instagram", url: props.user.socialNetworks.instagramUrl})
}
if (userSocialNetworks.tiktokUrl !== ''){
socialNetworks.push({icon: "/images/hutopymedia/icons/white/tiktokwhite.png", url: props.user.socialNetworks.tikTokUrl})
}
if (userSocialNetworks.youtubeUrl !== ''){
socialNetworks.push({icon: "mdi-youtube", url: props.user.socialNetworks.youtubeUrl})
}
if (userSocialNetworks.yourWebsiteUrl !== ''){
socialNetworks.push({icon: "mdi-web", url: props.user.socialNetworks.yourWebsiteUrl})
}
return socialNetworks;
}
</script>

View File

@@ -10,7 +10,7 @@
</div>
<div class="bg-gray-100 rounded-b-2xl p-4">
<div class="mx-2">
<StripePayment></StripePayment>
<StripePayment :creator-id="creatorId"></StripePayment>
</div>
</div>
</div>
@@ -27,13 +27,18 @@
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import {ref, onMounted, onUnmounted, defineProps} from 'vue';
import StripePayment from "@/views/StripePayment.vue";
const showPopup = ref(false);
const popup = ref(null);
const popupButton = ref(null);
const props = defineProps({
creatorId: { type: String, required: true },
});
const togglePopup = () => {
showPopup.value = !showPopup.value;
};

View File

@@ -1,41 +0,0 @@
<template>
<div>
<div class="flex flex-column w-max-[100] ">
</div>
<div class="flex flex-col items-center">
<div class="max-w-[800px] border-l-2 border-r-2 border-gray-200 px-4 ">
<PostCard v-for="post in posts"
:key="post.id"
:post="post"
class=" bg-white w-full content-center rounded-3xl mt-2">
</PostCard>
</div>
</div>
</div>
</template>
<script setup>
import PostContentMenu from '@/views/main/PostContentMenu.vue'
import PostCard from "@/views/main/PostCard.vue"
import posts from "@/views/main/posts.json"
import PostMessage from "@/views/messages/PostMessage.vue";
import MessageList from "@/views/messages/MessageList.vue";
let imageSrc = '/images/usersmedia/guillaumeMousseau/banners/bannerGuillaumeMousseau01.png';
</script>
<style scoped>
</style>

View File

@@ -1,10 +1,9 @@
<template>
<footer>
<div class="bg-black/5 p-6 text-center">
{{ new Date().getFullYear() }} <strong>Hutopy v.01</strong>
<div class="p-4 text-center font-sans">
Hutopy &copy;{{ new Date().getFullYear() }} - Tout droits réservés
</div>
</footer>
</template>
<script setup lang="ts">
</script>

View File

@@ -124,6 +124,7 @@ import SelectedFooter from "@/views/main/SelectedFooter.vue";
</script>
<style scoped>
.overlay p {
color: white;
font-size: 1.5rem;
@@ -134,6 +135,4 @@ body {
background-color: #F4F4F4;
}
</style>

View File

@@ -1,44 +0,0 @@
<template>
<div class="border-2 shadow-2xl mb-4 ">
<div class="mt-1">
<v-img class="mt-1 rounded-t-2xl " :src="props.post.banner"
v-if="!isHttpUrl">
</v-img>
<iframe style="min-height: 400px" class="w-full rounded-t-2xl"
v-if="isHttpUrl"
:src="props.post.banner"
title="YouTube video player"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen>
</iframe>
</div>
<div class="text-lg font-bold m-4">{{ props.post.title }}</div>
<div class="text-sm text-gray-500 m-4">{{ props.post.title }}</div>
<div class="text-base text-gray-700 m-4 text-justify">{{ props.post.content }}</div>
</div>
</template>
<script setup>
import {defineProps, computed} from 'vue';
const isHttpUrl = computed(() => props.post.banner.startsWith('http'))
const props = defineProps({
post: {
type: Object,
required: true,
}
});
</script>

View File

@@ -1,155 +0,0 @@
<template>
<v-col cols="12">
<v-container>
<v-card style="border-radius: 30px" elevation="10">
<v-card-text>
<v-row>
<!-- Boutons de sélection -->
<v-col cols="2" class="d-flex justify-center align-center toolbar-btn">
<v-btn elevation="0" icon @click="selectType('title')">
<v-icon>mdi-format-title</v-icon>
</v-btn>
</v-col>
<v-col cols="2" class="d-flex justify-center align-center toolbar-btn">
<v-btn elevation="0" icon @click="selectType('text')">
<v-icon>mdi-text</v-icon>
</v-btn>
</v-col>
<v-col cols="2" class="d-flex justify-center align-center toolbar-btn">
<v-btn elevation="0" icon @click="selectType('image')">
<v-icon>mdi-image</v-icon>
</v-btn>
</v-col>
<v-col cols="2" class="d-flex justify-center align-center toolbar-btn">
<v-btn elevation="0" icon @click="selectType('video')">
<v-icon>mdi-video</v-icon>
</v-btn>
</v-col>
<v-col cols="2" class="d-flex justify-center align-center toolbar-btn">
<v-btn elevation="0" icon @click="selectType('audio')">
<v-icon>mdi-volume-high</v-icon>
</v-btn>
</v-col>
<v-col cols="2" class="d-flex justify-center align-center toolbar-btn">
<v-btn elevation="0" icon @click="selectType('comments')">
<v-icon>mdi-comment</v-icon>
</v-btn>
</v-col>
</v-row>
</v-card-text>
<!-- Affichage du contenu en fonction du type sélectionné -->
<v-card-text>
<v-row v-for="(content, index) in contents" :key="index" class="draggable-row"
@dragstart="dragStart(index)" @dragover.prevent @drop="drop(index)" draggable="true">
<v-col cols="10">
<template v-if="content.type === 'title'">
<v-text-field v-model="content.value" label="Titre"></v-text-field>
</template>
<template v-else-if="content.type === 'text'">
<v-textarea v-model="content.value" label="Texte"></v-textarea>
</template>
<template v-else-if="content.type === 'image'">
<v-row>
<v-col cols="12">
<v-file-input v-model="content.value" label="Image"></v-file-input>
</v-col>
</v-row>
</template>
<template v-else-if="content.type === 'video'">
<v-text-field v-model="content.value" label="URL de la vidéo"></v-text-field>
</template>
<template v-else-if="content.type === 'audio'">
<v-row>
<v-col cols="2">
<v-icon>mdi-volume-high</v-icon>
</v-col>
<v-col cols="10">
<v-file-input v-model="content.value" label="Audio"></v-file-input>
</v-col>
</v-row>
</template>
<template v-else-if="content.type === 'comments'">
<v-text-field v-model="content.value" label="Commentaires"></v-text-field>
</template>
</v-col>
<v-col cols="2" class="d-flex justify-center align-center">
<v-btn icon @click="removeContent(index)" class="remove-button">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-col>
</v-row>
</v-card-text>
<!-- Boutons Post, Preview et Cancel -->
<v-row v-if="contents.length > 0" justify="end" style="margin-bottom: 10px;">
<v-col class="d-flex justify-end" style="margin-right: 4%;">
<v-btn style="margin-right: 15px;" @click="postContent" color="white" dark
elevation="4">Post</v-btn>
<v-btn style="margin-right: 15px;" @click="previewContent" color="white" dark
elevation="5">Preview</v-btn>
<v-btn @click="cancelPost" color="white" dark elevation="5">Cancel</v-btn>
</v-col>
</v-row>
</v-card>
</v-container>
</v-col>
</template>
<script setup>
import { ref } from 'vue';
const contents = ref([]);
let dragIndex = null;
const selectType = (type) => {
console.log("Type sélectionné:", type);
contents.value.push({ type: type, value: '' });
};
const removeContent = (index) => {
contents.value.splice(index, 1);
};
const cancelPost = () => {
if (contents.value.length > 0) {
// Réinitialiser le tableau contents pour supprimer tous les contenus
contents.value = [];
}
};
const dragStart = (index) => {
dragIndex = index;
};
const drop = (index) => {
if (dragIndex !== null && index !== null) {
const draggedItem = contents.value[dragIndex];
contents.value.splice(dragIndex, 1);
contents.value.splice(index, 0, draggedItem);
dragIndex = null;
}
};
</script>
<style scoped>
.remove-button {
display: flex;
justify-content: center;
align-items: center;
margin-top: -20%;
}
.toolbar-btn {
margin-top: 10px;
margin-bottom: -15px;
}
.draggable-row {
cursor: grab;
}
</style>

View File

@@ -5,24 +5,28 @@
<div class="flex justify-center text-2xl">Photo de couverture</div>
</div>
<div>
<div :style="{ backgroundColor: user.profileColors.bannerTop }" class="flex h-4"></div>
<div :style="{ backgroundColor: user.profileColors.bannerTop || '#6B0065' }" class="flex h-4"></div>
<img :src="bannerImageUrl" alt="Banner Image" @error="handleBannerImageError" class="w-full object-cover">
<div :style="{ backgroundColor: user.profileColors.bannerBottom }" class="h-4">
<div :style="{ backgroundColor: user.profileColors.bannerBottom || '#A30E79' }" class="h-4">
</div>
<div>
<div class="space-x-4 flex justify-center py-2">
<v-btn @click="openColorPicker('top')"
:style="{ backgroundColor: user.profileColors.bannerTop, color: getTextColor(user.profileColors.bannerTop) }">Haut
:style="{ backgroundColor: user.profileColors.bannerTop || '#6B0065', color: getTextColor(user.profileColors.bannerTop || '#6B0065') }">
Haut
</v-btn>
<v-btn @click="openColorPicker('bottom')"
:style="{ backgroundColor: user.profileColors.bannerBottom, color: getTextColor(user.profileColors.bannerBottom) }">Bas
:style="{ backgroundColor: user.profileColors.bannerBottom || '#A30E79', color: getTextColor(user.profileColors.bannerBottom || '#A30E79') }">
Bas
</v-btn>
<v-btn @click="openColorPicker('accent')"
:style="{ backgroundColor: user.profileColors.accent, color: getTextColor(user.profileColors.accent) }">Accent1
:style="{ backgroundColor: user.profileColors.accent || '#23393B', color: getTextColor(user.profileColors.accent || '#23393B') }">
Accent1
</v-btn>
<v-btn @click="openColorPicker('menu')"
:style="{ backgroundColor: user.profileColors.menu, color: getTextColor(user.profileColors.menu) }">Menu
:style="{ backgroundColor: user.profileColors.menu || '#800080', color: getTextColor(user.profileColors.menu || '#800080') }">
Menu
</v-btn>
</div>
</div>
@@ -45,7 +49,7 @@
</div>
<div class="flex justify-center py-4">
<img :src="profilePictureUrl"
:style="{ borderColor: user.profileColors.accent, borderWidth: '3px', borderStyle: 'solid' }"
:style="{ borderColor: user.profileColors.accent || '#23393B' , borderWidth: '3px', borderStyle: 'solid' }"
class="rounded-full max-w-48 max-w-48"
@error="handleProfileImageError"
alt="Profile Image">

View File

@@ -1,34 +0,0 @@
[
{
"id": 1,
"creatorId": 1,
"title": "Déploiement de la version 0.10 d'Hutopy",
"date": "24-04-2024",
"banner": "https://www.youtube.com/embed/neKWqjE0eSs?si=Bo7xbYaw9-56w3lU",
"content": "Bonjour, Nous sommes fiers de vous présenter la version 0.10 d'Hutopy. Nous sommes au début d'une aventure visant à transformer la sphère des médias sociaux. Notre objectif est d'apporter un souffle de fraîcheur en favorisant des interactions plus constructives entre les individus. Dans cette première version, seuls nos utilisateurs testeurs ont accès à la plateforme comme créateur. Dans un futur proche, avec le déploiement de la version 0.2, nous contacterons les personnes qui se sont inscrites via l'onglet d'inscription et ont rempli quelques questions. Si vous souhaitez soutenir le développement de la plateforme, deux options s'offrent à vous. La première consiste à nous apporter un soutien financier, tandis que la seconde est de nous contacter pour faire partie de l'équipe de développement. Nous recherchons actuellement un programmeur supplémentaire pour faire progresser certaines fonctionnalités. Merci de visiter Hutopy."
},
{
"id": 1,
"creatorId": 1,
"title": "Déploiement de la version 0.10 d'Hutopy",
"date": "24-04-2024",
"banner": "/images/usersmedia/HutopyProfile/pictures/version.png",
"content": "Bonjour, Nous sommes fiers de vous présenter la version 0.10 d'Hutopy. Nous sommes au début d'une aventure visant à transformer la sphère des médias sociaux. Notre objectif est d'apporter un souffle de fraîcheur en favorisant des interactions plus constructives entre les individus. Dans cette première version, seuls nos utilisateurs testeurs ont accès à la plateforme comme créateur. Dans un futur proche, avec le déploiement de la version 0.2, nous contacterons les personnes qui se sont inscrites via l'onglet d'inscription et ont rempli quelques questions. Si vous souhaitez soutenir le développement de la plateforme, deux options s'offrent à vous. La première consiste à nous apporter un soutien financier, tandis que la seconde est de nous contacter pour faire partie de l'équipe de développement. Nous recherchons actuellement un programmeur supplémentaire pour faire progresser certaines fonctionnalités. Merci de visiter Hutopy."
},
{
"id": 1,
"creatorId": 1,
"title": "Déploiement de la version 0.10 d'Hutopy",
"date": "24-04-2024",
"banner": "/images/usersmedia/HutopyProfile/pictures/version.png",
"content": "Bonjour, Nous sommes fiers de vous présenter la version 0.10 d'Hutopy. Nous sommes au début d'une aventure visant à transformer la sphère des médias sociaux. Notre objectif est d'apporter un souffle de fraîcheur en favorisant des interactions plus constructives entre les individus. Dans cette première version, seuls nos utilisateurs testeurs ont accès à la plateforme comme créateur. Dans un futur proche, avec le déploiement de la version 0.2, nous contacterons les personnes qui se sont inscrites via l'onglet d'inscription et ont rempli quelques questions. Si vous souhaitez soutenir le développement de la plateforme, deux options s'offrent à vous. La première consiste à nous apporter un soutien financier, tandis que la seconde est de nous contacter pour faire partie de l'équipe de développement. Nous recherchons actuellement un programmeur supplémentaire pour faire progresser certaines fonctionnalités. Merci de visiter Hutopy."
},
{
"id": 1,
"creatorId": 1,
"title": "Déploiement de la version 0.10 d'Hutopy",
"date": "24-04-2024",
"banner": "/images/usersmedia/HutopyProfile/pictures/version.png",
"content": "Bonjour, Nous sommes fiers de vous présenter la version 0.10 d'Hutopy. Nous sommes au début d'une aventure visant à transformer la sphère des médias sociaux. Notre objectif est d'apporter un souffle de fraîcheur en favorisant des interactions plus constructives entre les individus. Dans cette première version, seuls nos utilisateurs testeurs ont accès à la plateforme comme créateur. Dans un futur proche, avec le déploiement de la version 0.2, nous contacterons les personnes qui se sont inscrites via l'onglet d'inscription et ont rempli quelques questions. Si vous souhaitez soutenir le développement de la plateforme, deux options s'offrent à vous. La première consiste à nous apporter un soutien financier, tandis que la seconde est de nous contacter pour faire partie de l'équipe de développement. Nous recherchons actuellement un programmeur supplémentaire pour faire progresser certaines fonctionnalités. Merci de visiter Hutopy."
}
]

View File

@@ -1,9 +1,6 @@
<template>
<div class="flex flex-column">
<div class="flex flex-row p-1">
<div class="flex flex-row p-1 items-center">
<div class="px-2 content-center">
<img :src="message.portrait"
alt="Profile Image"
@@ -12,54 +9,68 @@
height="32px">
</div>
<div class="p-2">
<div>
<span class="font-semibold font-sans mr-2">{{ message.created_by }}</span>
<span class="text-sm font-sans text-gray-700">il y a 3 heures</span>
<div class="p-2 flex-1">
<div class="flex justify-between items-center">
<div>
<span class="font-semibold font-sans mr-2">{{ message.createdBy }}</span>
<span class="text-sm font-sans text-gray-700">il y a 3 heures</span>
</div>
<v-menu :location="location">
<template v-slot:activator="{ props }">
<v-btn variant="plain" icon v-bind="props">
<v-icon>mdi-dots-vertical</v-icon>
</v-btn>
</template>
<v-list>
<v-list-item @click="editMessage(message)">
<v-list-item-title>Edit</v-list-item-title>
</v-list-item>
<v-list-item @click="deleteMessage(message)">
<v-list-item-title>Delete</v-list-item-title>
</v-list-item>
<v-list-item @click="reportMessage(message)">
<v-list-item-title>Report</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</div>
<div class="font-sans">
{{ message.value }}
</div>
<div class="flex px-4">
<div>
<v-icon>mdi-thumb-up-outline</v-icon>
{{ message.thumb_up_count }}
</div>
<div>
<v-icon>mdi-thumb-down-outline</v-icon>
{{ message.thumb_down_count }}
</div>
<div>
<v-btn variant="flat">Reply</v-btn>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import {defineProps} from 'vue';
defineProps({
message: {
type: Object,
required: true
<script>
export default {
props: {
message: {
type: Object,
required: true
}
},
methods: {
editMessage(message) {
// Logic for editing the message
console.log('Edit message', message);
},
deleteMessage(message) {
// Logic for deleting the message
console.log('Delete message', message);
},
reportMessage(message) {
// Logic for reporting the message
console.log('Report message', message);
}
}
});
}
</script>
<style scoped>
.content-center {
display: flex;
align-items: center;
}
</style>

View File

@@ -25,11 +25,15 @@ const client = useClient();
const messages = ref();
onBeforeMount(async () => {
if (props.contentId == null) return
try {
const response = await client.get(`/api/messages/${props.contentId}`)
messages.value = response.data
if (response.status >= 200 && response.status < 300) {
messages.value = response.data
}
} catch (error) {
console.error("Failed to fetch posts", error);
console.error("Failed to fetch messages", error);
}
})

View File

@@ -24,8 +24,8 @@
</div>
<div class="flex flex-row gap-2 p-2 justify-end">
<v-btn style="border-radius: 20px" variant="text">Canceller</v-btn>
<v-btn style="border-radius: 20px" @click="publish">Publier</v-btn>
<v-btn style="border-radius: 20px" variant="text">Annuler</v-btn>
<v-btn style="border-radius: 20px" @click="publish">Ajouter un commentaire</v-btn>
</div>
</div>