Merged PR 96: CreatorPage into main
This commit is contained in:
@@ -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
|
||||
|
||||
BIN
public/images/hutopymedia/banners/tutorialbanner.png
Normal file
BIN
public/images/hutopymedia/banners/tutorialbanner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.6 MiB |
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
</div>
|
||||
|
||||
|
||||
<selected-footer class="py-15"></selected-footer>
|
||||
<selected-footer></selected-footer>
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
45
src/views/contents/ContentCard.vue
Normal file
45
src/views/contents/ContentCard.vue
Normal 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>
|
||||
41
src/views/contents/ContentList.vue
Normal file
41
src/views/contents/ContentList.vue
Normal 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>
|
||||
74
src/views/contents/ContentPage.vue
Normal file
74
src/views/contents/ContentPage.vue
Normal 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>
|
||||
|
||||
|
||||
|
||||
106
src/views/contents/PostContent.vue
Normal file
106
src/views/contents/PostContent.vue
Normal 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>
|
||||
148
src/views/contents/PostContentMenu.vue
Normal file
148
src/views/contents/PostContentMenu.vue
Normal 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>-->
|
||||
|
||||
<!-- <!– 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%;">-->
|
||||
<!-- <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>
|
||||
40
src/views/contents/contents.json
Normal file
40
src/views/contents/contents.json
Normal 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
|
||||
}
|
||||
]
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
122
src/views/creators/CreatorPage.vue
Normal file
122
src/views/creators/CreatorPage.vue
Normal 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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
@@ -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 ©{{ new Date().getFullYear() }} - Tout droits réservés
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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">
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
]
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user