Merge remote-tracking branch 'origin/main'

# Conflicts:
#	src/App.vue
#	src/router/router.js
This commit is contained in:
PascalMarchesseault
2024-10-01 18:15:32 -04:00
75 changed files with 1407 additions and 1561 deletions

208
package-lock.json generated
View File

@@ -30,7 +30,8 @@
"eslint-plugin-vue": "^9.22.0",
"postcss": "^8.4.35",
"tailwindcss": "^3.4.1",
"vite": "^5.2.11"
"vite": "^5.2.11",
"vite-svg-loader": "^5.1.0"
}
},
"node_modules/@alloc/quick-lru": {
@@ -994,6 +995,16 @@
"tailwindcss": ">=2.0.0"
}
},
"node_modules/@trysound/sax": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
"integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/@types/estree": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
@@ -1692,6 +1703,50 @@
"node": ">= 8"
}
},
"node_modules/css-select": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"boolbase": "^1.0.0",
"css-what": "^6.1.0",
"domhandler": "^5.0.2",
"domutils": "^3.0.1",
"nth-check": "^2.0.1"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/css-tree": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
"dev": true,
"license": "MIT",
"dependencies": {
"mdn-data": "2.0.30",
"source-map-js": "^1.0.1"
},
"engines": {
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
}
},
"node_modules/css-what": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
"integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">= 6"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -1703,6 +1758,42 @@
"node": ">=4"
}
},
"node_modules/csso": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
"integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"css-tree": "~2.2.0"
},
"engines": {
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/csso/node_modules/css-tree": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
"integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
"dev": true,
"license": "MIT",
"dependencies": {
"mdn-data": "2.0.28",
"source-map-js": "^1.0.1"
},
"engines": {
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/csso/node_modules/mdn-data": {
"version": "2.0.28",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
"integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
"dev": true,
"license": "CC0-1.0"
},
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
@@ -1776,6 +1867,65 @@
"node": ">=6.0.0"
}
},
"node_modules/dom-serializer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
"dev": true,
"license": "MIT",
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.2",
"entities": "^4.2.0"
},
"funding": {
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
"node_modules/domelementtype": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"license": "BSD-2-Clause"
},
"node_modules/domhandler": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"domelementtype": "^2.3.0"
},
"engines": {
"node": ">= 4"
},
"funding": {
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/domutils": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"dom-serializer": "^2.0.0",
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3"
},
"funding": {
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
"node_modules/eastasianwidth": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
@@ -2887,6 +3037,13 @@
"node": ">=0.10.0"
}
},
"node_modules/mdn-data": {
"version": "2.0.30",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
"dev": true,
"license": "CC0-1.0"
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -3931,6 +4088,42 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/svgo": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz",
"integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@trysound/sax": "0.2.0",
"commander": "^7.2.0",
"css-select": "^5.1.0",
"css-tree": "^2.3.1",
"css-what": "^6.1.0",
"csso": "^5.0.5",
"picocolors": "^1.0.0"
},
"bin": {
"svgo": "bin/svgo"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/svgo"
}
},
"node_modules/svgo/node_modules/commander": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 10"
}
},
"node_modules/tailwindcss": {
"version": "3.4.10",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.10.tgz",
@@ -4193,6 +4386,19 @@
}
}
},
"node_modules/vite-svg-loader": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/vite-svg-loader/-/vite-svg-loader-5.1.0.tgz",
"integrity": "sha512-M/wqwtOEjgb956/+m5ZrYT/Iq6Hax0OakWbokj8+9PXOnB7b/4AxESHieEtnNEy7ZpjsjYW1/5nK8fATQMmRxw==",
"dev": true,
"license": "MIT",
"dependencies": {
"svgo": "^3.0.2"
},
"peerDependencies": {
"vue": ">=3.2.13"
}
},
"node_modules/vue": {
"version": "3.4.38",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.38.tgz",

View File

@@ -31,6 +31,7 @@
"eslint-plugin-vue": "^9.22.0",
"postcss": "^8.4.35",
"tailwindcss": "^3.4.1",
"vite": "^5.2.11"
"vite": "^5.2.11",
"vite-svg-loader": "^5.1.0"
}
}

View File

@@ -1,13 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="1000" viewBox="0 0 1000 1000">
<defs>
<style>
.cls-1 {
stroke: #000;
stroke-width: 1px;
stroke-dasharray: 4 2;
fill-rule: evenodd;
}
</style>
</defs>
<path id="Social_x_-2_1" data-name="Social x -2 1" class="cls-1" d="M208.568,8c149.2,0.331,300.422,2.648,449.617,2.979,86.663,0,162.7-4.391,218.357,25.818,41.312,22.424,77.08,62.95,94.291,109.23,20.963,56.368,12.9,149.956,12.9,225.411V674.3c0,48.187,7.128,109.475-3.97,150.936-13.034,48.693-38.509,94.2-74.44,120.153-24.954,18.023-55.528,34.237-90.321,41.706-23.029,4.944-51.493,2.979-78.41,2.979H318.739c-46.8,0-103.154,5.907-144.909-2.979-60.19-12.809-108.811-49.038-136.97-94.335C2.042,836.752,11.055,767.026,11.055,675.3c0-117.493.992-362.125,0.992-479.618C12.047,63.677,151.59,8,208.568,8Zm10.918,201.579-2.977,1.986L436.851,500.528c-4.6,17.548-37.928,43.94-49.627,58.586L271.1,694.162c-11.979,15.019-36.369,30.637-42.679,49.65l89.328-.993,158.805-183.7c5.547,0.3,5.794,1.012,8.933,2.979,9.391,25.049,36.577,46.289,51.611,67.524,19.634,27.733,42.32,53.1,61.537,80.433,7.99,11.363,14.845,25.453,25.806,33.762H754.461c12.721,0,40.162,4,48.634-1.986L633.372,516.416c-12.208-17.35-56.689-63.437-60.544-81.426L767.364,211.565v-0.993l-3.97-.993-84.365.993L576.8,326.753c-13.733,17.111-28.64,40.051-46.649,52.629h-1.985L400.127,210.572Zm126.052,49.65,29.776,1.986L695.9,685.225l-0.993,5.958-46.649-.993q-30.765-40.212-61.537-80.433l-184.611-241.3L343.553,292c-5.976-8.018-22.454-20.757-20.843-31.776Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,26 +1,23 @@
<template>
<v-app>
<div class="m-0 flex flex-column h-screen">
<!-- <Header class="fixed w-full z-50 top-0 p-2"></Header>-->
<div class="flex flex-row relative">
<div
class="fixed h-full w-2 z-20"
style="background: transparent;"
></div>
<Header class="fixed w-full z-50 p-2 h-16"></Header>
<div class="flex flex-row mt-16">
<transition name="slide-fade">
<div v-show="sideBarStore.visible"
class=" fixed h-full min-w-60 border-r-2 bg-white z-30 transition-transform duration-700">
<SideBar></SideBar>
class="fixed h-full min-w-60 border-r-2 z-30 ">
<side-bar></side-bar>
</div>
</transition>
<div class="flex flex-col w-full" style="background-color: #f4f4f4">
<div class="flex flex-col w-full min-h-screen bg-amber">
<RouterView></RouterView>
<!-- <Footer></Footer>-->
</div>
</div>
</div>
</v-app>
<size-indicator></size-indicator>

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64h98.2V334.2H109.4V256h52.8V222.3c0-87.1 39.4-127.5 125-127.5c16.2 0 44.2 3.2 55.7 6.4V172c-6-.6-16.5-1-29.6-1c-42 0-58.2 15.9-58.2 57.2V256h83.6l-14.4 78.2H255V480H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64z"/></svg>

After

Width:  |  Height:  |  Size: 498 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M194.4 211.7a53.3 53.3 0 1 0 59.3 88.7 53.3 53.3 0 1 0 -59.3-88.7zm142.3-68.4c-5.2-5.2-11.5-9.3-18.4-12c-18.1-7.1-57.6-6.8-83.1-6.5c-4.1 0-7.9 .1-11.2 .1c-3.3 0-7.2 0-11.4-.1c-25.5-.3-64.8-.7-82.9 6.5c-6.9 2.7-13.1 6.8-18.4 12s-9.3 11.5-12 18.4c-7.1 18.1-6.7 57.7-6.5 83.2c0 4.1 .1 7.9 .1 11.1s0 7-.1 11.1c-.2 25.5-.6 65.1 6.5 83.2c2.7 6.9 6.8 13.1 12 18.4s11.5 9.3 18.4 12c18.1 7.1 57.6 6.8 83.1 6.5c4.1 0 7.9-.1 11.2-.1c3.3 0 7.2 0 11.4 .1c25.5 .3 64.8 .7 82.9-6.5c6.9-2.7 13.1-6.8 18.4-12s9.3-11.5 12-18.4c7.2-18 6.8-57.4 6.5-83c0-4.2-.1-8.1-.1-11.4s0-7.1 .1-11.4c.3-25.5 .7-64.9-6.5-83l0 0c-2.7-6.9-6.8-13.1-12-18.4zm-67.1 44.5A82 82 0 1 1 178.4 324.2a82 82 0 1 1 91.1-136.4zm29.2-1.3c-3.1-2.1-5.6-5.1-7.1-8.6s-1.8-7.3-1.1-11.1s2.6-7.1 5.2-9.8s6.1-4.5 9.8-5.2s7.6-.4 11.1 1.1s6.5 3.9 8.6 7s3.2 6.8 3.2 10.6c0 2.5-.5 5-1.4 7.3s-2.4 4.4-4.1 6.2s-3.9 3.2-6.2 4.2s-4.8 1.5-7.3 1.5l0 0c-3.8 0-7.5-1.1-10.6-3.2zM448 96c0-35.3-28.7-64-64-64H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96zM357 389c-18.7 18.7-41.4 24.6-67 25.9c-26.4 1.5-105.6 1.5-132 0c-25.6-1.3-48.3-7.2-67-25.9s-24.6-41.4-25.8-67c-1.5-26.4-1.5-105.6 0-132c1.3-25.6 7.1-48.3 25.8-67s41.5-24.6 67-25.8c26.4-1.5 105.6-1.5 132 0c25.6 1.3 48.3 7.1 67 25.8s24.6 41.4 25.8 67c1.5 26.3 1.5 105.4 0 131.9c-1.3 25.6-7.1 48.3-25.8 67z"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

1
src/assets/icons/x.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zm297.1 84L257.3 234.6 379.4 396H283.8L209 298.1 123.3 396H75.8l111-126.9L69.7 116h98l67.7 89.5L313.6 116h47.5zM323.3 367.6L153.4 142.9H125.1L296.9 367.6h26.3z"/></svg>

After

Width:  |  Height:  |  Size: 493 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

Before

Width:  |  Height:  |  Size: 276 B

View File

@@ -9,16 +9,6 @@
"footer": {
"allRightsReserved": "All rights reserved"
},
"sitemenu": {
"HelpUs": "Help us",
"About": "About",
"ContentPolicy": "Content Policy",
"CreatorGuide": "Creator Guide",
"FAQ": "FAQ",
"HelpAndContact": "Help and Contact",
"Pricing": "Pricing",
"TermsAndConditions": "Terms and conditions"
},
"sidebar": {
"subscriptionTitle": "Subscription"
},

View File

@@ -9,16 +9,6 @@
"footer": {
"allRightsReserved": "Tout droits réservés"
},
"sitemenu": {
"HelpUs": "Aidez-nous",
"About": "À propos",
"ContentPolicy": "Politique de contenu",
"CreatorGuide": "Guide pour les créateurs",
"FAQ": "FAQ",
"HelpAndContact": "Aide & Contact",
"Pricing": "Frais",
"TermsAndConditions": "Conditions générales"
},
"sidebar": {
"subscriptionTitle": "Abonnements"
},

View File

@@ -11,8 +11,9 @@ import * as directives from 'vuetify/directives'
import vueGoogleOauth from 'vue3-google-login'
import {useSubscriptionStore} from "@/stores/subscriptionStore.js";
import {useAuthStore} from "@/stores/authStore.js";
import {useUserStore} from "@/stores/userStore.js";
import i18n from './i18n.js';
import {useUserProfileStore} from "@/stores/userProfileStore.js";
import {useCreatorProfileStore} from "@/stores/creatorProfileStore.js";
const vuetify = createVuetify({
components,
@@ -31,9 +32,10 @@ const app = createApp(App)
// Make $t globally available
app.config.globalProperties.$t = i18n.global.t;
// this force the creation of the stores
const subscriptionStore = useSubscriptionStore()
const authStore = useAuthStore()
const userStore = useUserStore()
// this force the creation and initialization of the stores
useSubscriptionStore()
useAuthStore()
useUserProfileStore()
useCreatorProfileStore()
app.mount('#app');

View File

@@ -14,34 +14,25 @@ import Register from '../views/main/Register.vue'
import Home from '../views/main/Home.vue'
import Wallet from '../views/main/Wallet.vue'
import ProfilePage from '@/views/profile/ProfilePage.vue'
import CreatorList from '@/views/creators/CreatorList.vue'
import CreatorPage from "@/views/creators/CreatorPage.vue";
import ContentPage from "@/views/contents/ContentPage.vue";
import CreatorList from '@/views/browser/CreatorList.vue'
import PostContent from "@/views/contents/PostContent.vue";
import Explorer from "@/views/explorer/explorer.vue";
import {useAuthStore} from "@/stores/authStore.js";
import ForYouPage from "@/views/profile/ForYouPage.vue";
import CreatorPresentation from "@/views/creators/CreatorPresentation.vue";
import CreatorExclusiveContent from "@/views/creators/CreatorExclusiveContent.vue";
import CTA01 from "@/views/CTA01.vue";
import CreatorLayout from "@/views/creators/CreatorLayout.vue";
import ContentPage from "@/views/contents/ContentPage.vue";
import CreatorContent from "@/views/creators/CreatorContent.vue";
import CreatorNews from "@/views/creators/CreatorNews.vue";
import CreatorHome from "@/views/creators/CreatorHome.vue";
const routes = [
{
path: '/CTA01',
component: CTA01,
meta: { hideSideBar: true }
},
{
path: '/',
component: Home,
meta: {hideSideBar: true}
},
{
path: '/presentation/@:creator',
component: CreatorPresentation,
},
{
path: '/browse',
component: CreatorList
@@ -52,17 +43,29 @@ const routes = [
},
{
path: '/@:creator',
component: CreatorPage
component: CreatorLayout,
meta: {
requiresAuth: true,
},
children: [
{
path: '',
component: CreatorHome,
},
{
path: 'news',
component: CreatorNews,
},
{
path: 'content',
component: CreatorContent
},
]
},
{
path: '/content/post',
component: PostContent,
},
{
path: '/@:creator/exclusivecontent',
component: CreatorExclusiveContent
},
{
path: '/helpandcontact',
component: HelpAndContact,
@@ -128,7 +131,7 @@ const routes = [
},
{
path: '/paymentcompleted',
name: 'PayementCompleted',
name: 'PaymentCompleted',
component: PaymentCompleted,
},
{
@@ -153,7 +156,6 @@ const routes = [
path: '/feed',
name: 'feed',
component: ForYouPage,
},
]
@@ -162,7 +164,7 @@ const router = createRouter({
routes
})
// Navigation gards
// Navigation guards
router.beforeEach((to, from, next) => {
const authStore = useAuthStore();

View File

@@ -63,12 +63,12 @@ export const useAuthStore = defineStore(
}
}
async function loginGoogle(accessToken) {
async function loginWithGoogle(accessToken) {
try {
const response = await clientApi.post(
'api/google/sign-in',
'api/users/login-with-google',
{
accessToken: accessToken
token: accessToken
})
updateTokens(response.data)
} catch (error) {
@@ -95,6 +95,6 @@ export const useAuthStore = defineStore(
}
}
return {accessToken, refreshToken, isAuthenticated, userId, login, loginGoogle, logout}
return {accessToken, refreshToken, isAuthenticated, userId, login, loginWithGoogle, logout}
})

View File

@@ -0,0 +1,53 @@
import {defineStore} from 'pinia'
import {useClient} from "@/plugins/api.js";
import {useSessionStorage} from "@vueuse/core";
import {onBeforeMount, ref, watch} from "vue";
import {useRoute} from "vue-router";
export const useBrandingStore = defineStore(
'branding',
() => {
const currentBrand = ref('')
const value = useSessionStorage(
'branding',
{},
{writeDefaults: false})
const loading = ref(true)
const route = useRoute()
onBeforeMount(async () => await fetchCreatorData(route.params.creator))
watch(
() => route.params.creator,
async () => {
console.log(`creator: ${route.params.creator}`)
console.log(`currentBrand: ${currentBrand.value}`)
if (route.params.creator !== currentBrand.value) {
await fetchCreatorData(route.params.creator);
currentBrand.value = route.params.creator
}
}
)
const fetchCreatorData = async (creatorAlias) => {
try {
loading.value = true
const client = useClient()
const response = await client.get(`/api/creators/@${creatorAlias}`)
value.value = response.data
} catch (error) {
console.error(`Error fetching content: ${error}`)
} finally {
loading.value = false
}
}
return {
value,
loading
}
})

View File

@@ -0,0 +1,47 @@
import {computed, watch} from 'vue'
import {defineStore} from 'pinia'
import {useAuthStore} from "@/stores/authStore.js";
import {useClient} from "@/plugins/api.js";
import {useSessionStorage} from "@vueuse/core";
export const useCreatorProfileStore = defineStore(
'creator-profile',
() => {
const authStore = useAuthStore()
const authWatcher = watch(
() => authStore.isAuthenticated,
async (newValue) => {
if (newValue) {
await fetchCurrentCreatorProfile()
} else {
value.value = undefined
}
})
const value = useSessionStorage(
'creator-profile',
{},
{writeDefaults: false})
const hasCreator = computed(() =>
value.value
&& Object.getOwnPropertyNames(value.value).length >= 1)
const client = useClient()
async function fetchCurrentCreatorProfile() {
try {
const creatorResponse = await client.get(`/api/creators/profile`)
value.value = creatorResponse.data
// TODO: no cache-busting ???
} catch (error) {
value.value = undefined
}
}
return {
creator: value,
hasCreator
}
})

View File

@@ -4,38 +4,30 @@ import {useAuthStore} from "@/stores/authStore.js";
import {useClient} from "@/plugins/api.js";
import {useSessionStorage} from "@vueuse/core";
export const useUserStore = defineStore(
'user',
export const useUserProfileStore = defineStore(
'user-profile',
() => {
const authStore = useAuthStore()
const authWatcher = watch(
() => authStore.isAuthenticated,
async (newValue) => {
if (newValue) {
await fetchCurrentUserProfile()
await fetchCurrentCreatorProfile()
} else {
user.value = undefined
creator.value = undefined
value.value = undefined
}
})
const user = useSessionStorage(
'user-user',
const value = useSessionStorage(
'user-profile',
{},
{writeDefaults: false})
const creator = useSessionStorage(
'user-creator',
{},
{writeDefaults: false})
const hasCreator = computed(() =>
creator.value
&& Object.getOwnPropertyNames(creator.value).length >= 1)
const fullname = computed(() => {
if (user.value) {
const {firstname, lastname} = user.value;
if (value.value) {
const {firstname, lastname} = value.value;
if (firstname && lastname) {
return `${lastname}, ${firstname}`;
@@ -49,38 +41,29 @@ export const useUserStore = defineStore(
})
const alias = computed(() => {
if (user.value) {
return user.value.alias || `${user.value.firstname || ''} ${user.value.lastname || ''}`.trim() || 'Anonyme'
if (value.value) {
return value.value.alias || `${value.value.firstname || ''} ${value.value.lastname || ''}`.trim() || 'Anonyme'
}
return 'Anonyme';
})
const portraitUrl = computed(() => {
return user.value && user.value.portraitUrl
? user.value.portraitUrl
return value.value && value.value.portraitUrl
? value.value.portraitUrl
: '/images/usersmedia/anonyme/profilepictures/profileAnonymeSquare.png'
})
const client = useClient()
async function fetchCurrentUserProfile() {
try {
const client = useClient()
const userResponse = await client.get("/api/users/profile");
user.value = userResponse.data
value.value = userResponse.data
// Cache-busting only if portraitUrl exists
if (user.value.portraitUrl) {
user.value.portraitUrl = `${user.value.portraitUrl}?${Date.now()}`;
if (value.value.portraitUrl) {
value.value.portraitUrl = `${value.value.portraitUrl}?${Date.now()}`;
}
} catch (error) {
user.value = undefined;
}
}
async function fetchCurrentCreatorProfile() {
try {
const creatorResponse = await client.get(`/api/creators/profile`)
creator.value = creatorResponse.data
} catch (error) {
creator.value = undefined
value.value = undefined;
}
}
@@ -92,8 +75,8 @@ export const useUserStore = defineStore(
firstname: firstname,
lastname: lastname
})
user.value.firstname = firstname;
user.value.lastname = lastname;
value.value.firstname = firstname;
value.value.lastname = lastname;
} catch (error) {
console.error(error)
}
@@ -106,7 +89,7 @@ export const useUserStore = defineStore(
{
alias: alias
})
user.value.alias = alias;
value.value.alias = alias;
} catch (error) {
console.error(error)
}
@@ -119,7 +102,7 @@ export const useUserStore = defineStore(
{
birthdate: birthdate
})
user.value.birthDate = birthdate;
value.value.birthDate = birthdate;
} catch (error) {
console.error(error)
}
@@ -132,7 +115,7 @@ export const useUserStore = defineStore(
{
phoneNumber: phoneNumber
})
user.value.phoneNumber = phoneNumber;
value.value.phoneNumber = phoneNumber;
} catch (error) {
console.error(error)
}
@@ -145,7 +128,7 @@ export const useUserStore = defineStore(
{
email: email
})
user.value.email = email;
value.value.email = email;
} catch (error) {
console.error(error)
}
@@ -158,7 +141,7 @@ export const useUserStore = defineStore(
{
address: address
})
user.value.address = address;
value.value.address = address;
} catch (error) {
console.error(error)
}
@@ -173,17 +156,15 @@ export const useUserStore = defineStore(
`/api/users/portrait`,
formData)
user.value.portraitUrl = `${response.data.blobUrl}?${Date.now()}` // the Date.now() is for cache-busting
value.value.portraitUrl = `${response.data.blobUrl}?${Date.now()}` // the Date.now() is for cache-busting
} catch (error) {
console.error(error)
}
}
return {
user,
creator,
user: value,
alias,
hasCreator,
fullname,
portraitUrl,
changeFullname,
@@ -192,7 +173,6 @@ export const useUserStore = defineStore(
changePhone,
changeEmail,
changeAddress,
changePortrait,
fetchCurrentCreatorProfile
changePortrait
}
})

View File

@@ -14,11 +14,9 @@
</div>
</div>
</div>
<selected-footer></selected-footer>
</template>
<script setup>
import SelectedFooter from "@/views/main/SelectedFooter.vue";
import LoginForm from "@/views/main/LoginForm.vue";
import { useRouter } from 'vue-router';

View File

@@ -15,9 +15,9 @@
<RouterLink v-for="(creator, index) in creators"
:key="index"
:to="creator.routerLink">
<CreatorCard :creator="creator"
<creator-card :creator="creator"
class="m-2">
</CreatorCard>
</creator-card>
</RouterLink>
</div>
@@ -27,6 +27,6 @@
<script setup>
import CreatorCard from "@/views/creators/CreatorCard.vue";
import CreatorCard from "@/views/browser/CreatorCard.vue";
</script>

View File

@@ -72,13 +72,7 @@
{{ messageCount }}
</v-btn>
<donation-button :color-border="colorMenu"
:color-accent="colorAccent"
:creator-id="creatorId"
:creator-name="creatorName"
:creator-logo="creatorLogo"
iconColorClass="text-black"
></donation-button>
<donation-button></donation-button>
</div>

View File

@@ -1,9 +1,12 @@
<template>
<v-infinite-scroll :items="contents"
:onLoad="fetchContents"
class="md:gap-2">
<div>
<v-infinite-scroll :items="contents"
:onLoad="fetchContents">
<!-- TODO: the -mt-4 is necessary because the v-infinite-scroll has some 'top' panel offsetting the list -->
<div class="flex flex-column gap-4 -mt-4">
<template v-for="content in contents" :key="content.id">
<component
:is="isSmallScreen ? ContentCardSm : ContentCardNormal"
@@ -11,6 +14,7 @@
@content-deleted="onContentDeleted"
></component>
</template>
</div>
<template v-slot:empty>
Il n'y a pas plus de contenus
@@ -22,8 +26,13 @@
</v-infinite-scroll>
</div>
</template>
<style>
</style>
<script setup>
import {useClient} from '@/plugins/api.js';

View File

@@ -1,8 +1,9 @@
<script setup>
import {useClient} from '@/plugins/api.js';
import {ref} from 'vue';
import {useUserStore} from '@/stores/userStore.js';
import {v7} from 'uuid';
import {useCreatorProfileStore} from "@/stores/creatorProfileStore.js";
import {useUserProfileStore} from "@/stores/userProfileStore.js";
const props = defineProps({
creator: {type: Object, required: true},
@@ -10,7 +11,8 @@ const props = defineProps({
const emits = defineEmits(['content-posted'])
const userStore = useUserStore();
const userProfileStore = useUserProfileStore()
const creatorProfileStore = useCreatorProfileStore()
const isDialogActive = ref(false);
@@ -31,7 +33,7 @@ const removeUrl = (index) => {
async function publishPost() {
const formData = new FormData();
formData.append('id', v7());
formData.append('creatorId', userStore.user.id);
formData.append('creatorId', creatorProfileStore.value.id);
formData.append('title', title.value);
formData.append('description', message.value);
files.value.forEach(file => {
@@ -73,7 +75,7 @@ const closeDialog = () => {
<template>
<button
v-if="creator && userStore.user && creator.id === userStore.user.id"
v-if="creator && userProfileStore.value && creator.id === userProfileStore.value.id"
class="flex items-center text-white transform transition-transform duration-200 hover:text-gray-300 hover:scale-125 px-4"
@click="isDialogActive = true">
<v-icon style="font-size: 35px; height: 35px; width: 55px;">mdi-text-box-plus-outline</v-icon>

View File

@@ -1,12 +1,12 @@
<script setup>
import { useUserStore } from "@/stores/userStore.js";
import { REACTIONS } from "@/Constants/Reactions.js";
import { computed, ref } from "vue";
import { useClient } from "@/plugins/api.js";
import {useAuthStore} from "@/stores/authStore.js"
import MustBeLogged from "@/views/MustBeLogged.vue";
import {useUserProfileStore} from "@/stores/userProfileStore.js";
const userStore = useUserStore();
const userProfileStore = useUserProfileStore();
const authStore = useAuthStore()
const props = defineProps({
@@ -49,8 +49,8 @@ async function reactToContent(reaction) {
const request = {
ContentId: contentId.value,
reaction: reaction,
userId: userStore.user.id,
userName: `${userStore.user.firstName} ${userStore.user.lastName}`,
userId: userProfileStore.value.id,
userName: `${userProfileStore.value.firstName} ${userProfileStore.value.lastName}`,
};
adjustReactionCount(reaction);
await client.post("/api/content/reaction/", request);
@@ -61,8 +61,8 @@ async function reactToContent(reaction) {
const requestAdd = {
ContentId: contentId.value,
reaction: reaction,
userId: userStore.user.id,
userName: `${userStore.user.firstName} ${userStore.user.lastName}`,
userId: userProfileStore.value.id,
userName: `${userProfileStore.value.firstName} ${userProfileStore.value.lastName}`,
};
adjustReactionCount(reaction);
await client.post("/api/content/reaction/", requestAdd);
@@ -71,7 +71,7 @@ async function reactToContent(reaction) {
} else {
const requestRemove = {
ContentId: contentId.value,
userId: userStore.user.id,
userId: userProfileStore.value.id,
};
adjustReactionCount(reaction);
await client.post("/api/content/reaction/remove", requestRemove);
@@ -168,7 +168,7 @@ function adjustReactionCount(newReaction) {
}
function initializeReactions() {
const userReaction = props.content.reactions.find((x) => x.userId === userStore.user.id);
const userReaction = props.content.reactions.find((x) => x.userId === userProfileStore.value.id);
if (userReaction) {
currentReaction.value = userReaction.reaction;
hasReacted.value = true;

View File

@@ -1,5 +1,5 @@
<template>
<div class="shadow-md rounded-2xl bg-gray-50 border custom-border w-full mb-2 ">
<div class="shadow-md rounded-2xl bg-gray-50 border custom-border w-full">
<div>
<v-card-title>
<div class="flex flex-row justify-between items-center">
@@ -72,13 +72,7 @@
{{ messageCount }}
</v-btn>
<donation-button :color-border="colorMenu"
:color-accent="colorAccent"
:creator-id="creatorId"
:creator-name="creatorName"
:creator-logo="creatorLogo"
iconColorClass="text-black"
></donation-button>
<donation-button></donation-button>
</div>
@@ -161,10 +155,6 @@ const emits = defineEmits(['content-deleted'])
const contentId = computed(() => props.content.id)
const creatorId = computed(() => props.content.createdBy)
const creatorName = computed(() => props.content.createdByName)
const creatorLogo = computed(() => props.content.createdByPortraitUrl)
const colorMenu = computed(() => props.content.colorMenu)
const colorAccent = computed(() => props.content.colorAccent)
const authStore = useAuthStore();
const messageStore = useMessageStore();

View File

@@ -72,13 +72,7 @@
{{ messageCount }}
</v-btn>
<donation-button :color-border="colorMenu"
:color-accent="colorAccent"
:creator-id="creatorId"
:creator-name="creatorName"
:creator-logo="creatorLogo"
iconColorClass="text-black"
></donation-button>
<donation-button></donation-button>
</div>
@@ -161,10 +155,6 @@ const emits = defineEmits(['content-deleted'])
const contentId = computed(() => props.content.id)
const creatorId = computed(() => props.content.createdBy)
const creatorName = computed(() => props.content.createdByName)
const creatorLogo = computed(() => props.content.createdByPortraitUrl)
const colorMenu = computed(() => props.content.colorMenu)
const colorAccent = computed(() => props.content.colorAccent)
const authStore = useAuthStore()
const creatorIsCurrentUser = computed(() => authStore.isAuthenticated && authStore.userId === creatorId.value)

View File

@@ -53,14 +53,7 @@
<div class="flex justify-around py-2">
<Reaction v-if="data" :content="data"></Reaction>
<donation-button v-if="data"
:color-border="data.colorMenu"
:color-accent="data.colorAccent"
:creator-id="data.createdBy"
:creator-name="data.createdByName"
:creator-logo="data.createdByPortraitUrl"
iconColorClass="text-black"
></donation-button>
<donation-button v-if="data"></donation-button>
</div>
</div>

View File

@@ -45,27 +45,21 @@
<div class="flex flex-col p-6 bg-white overflow-y-auto max-h-screen">
<div class="border-b-2 p-6 font-sans space-y-2">
<div class="flex flex-row align-center" v-if="data && data.createdByName">
<img :src="data.createdByPortraitUrl" class="rounded-full w-9" alt="">
<p class="ml-2 capitalize ">{{ data.createdByName }}</p>
</div>
<div v-if="data && data.description">
Description: {{ data.description }}
</div>
<div class="flex justify-around py-2">
<Reaction v-if="data" :content="data"></Reaction>
<donation-button v-if="data"
:color-border="data.colorMenu"
:color-accent="data.colorAccent"
:creator-id="data.createdBy"
:creator-name="data.createdByName"
:creator-logo="data.createdByPortraitUrl"
iconColorClass="text-black"
></donation-button>
<div v-if="data" class="flex justify-around py-2">
<reaction :content="data"></reaction>
<donation-button></donation-button>
</div>
</div>
<div class="border-b-2 p-6">

View File

@@ -1,65 +0,0 @@
<template>
<div>
<button
class="flex items-center text-white transform transition-transform duration-200 hover:text-gray-300 hover:scale-125 "
@click="AboutUs = true"
>
<v-icon style="font-size: 30px; height: 30px; width: 55px;">
mdi-information
</v-icon>
</button>
<v-dialog v-model="AboutUs" max-width="500px">
<v-form>
<v-card class="text-center rounded-xl"
:style="{
border: `3px solid ${creator.colors.menu}`
}">
<div class="flex items-center justify-between py-4 text-2xl font-bold border-b mb-2">
<div class="flex-1 text-center">
{{ creator.about.title }}
</div>
<v-btn icon @click="AboutUs = false" class="ml-auto mr-2" variant="text">
<v-icon>mdi-close</v-icon>
</v-btn>
</div>
<v-card-text class="scrollable-content">
{{ creator.about.description }}
</v-card-text>
<div class="p-4">
<v-btn variant="outlined" text class=" w-full" @click="AboutUs = false" :style="{ borderColor: creator.colors.menu, color: creator.colors.menu }">Fermer</v-btn>
</div>
</v-card>
</v-form>
</v-dialog>
</div>
</template>
<script setup>
import {ref} from 'vue';
const AboutUs = ref(false);
const props = defineProps({
creator: {type: Object, required: true},
});
</script>
<style scoped>
.scrollable-content {
max-height: 600px;
overflow-y: auto;
}
.scrollable-content::-webkit-scrollbar {
width: 6px;
}
</style>

View File

@@ -1,17 +1,14 @@
<template>
<div class="shadow-lg rounded-2xl" :style="{ backgroundColor: creator.colors.bannerBottom}">
<div class="shadow-lg rounded-2xl"
:style="{ backgroundColor: branding.value.colors.secondary}">
<div class="relative z-20">
<div class="py-4 min-h-9 md:rounded-t-2xl"
:style="{ backgroundColor: creator.colors.bannerTop || '#6B0065' }">
</div>
<div class="h-1" :style="{ backgroundColor: creator.colors.accent || '#6B0065' }"> </div>
<!--Banner-->
<div class="relative">
<div>
<img
class="w-full drop-shadow-[0_10px_6px_rgba(0,0,0,0.25)]"
:src="creator.images.banner ? creator.images.banner : '/images/placeholders/banner.png'"
:src="branding.value.images.banner ? branding.value.images.banner : '/images/placeholders/banner.png'"
alt="Profile Banner"
style="max-height: 425px"
>
@@ -19,24 +16,16 @@
</div>
</div>
<div class="h-1" :style="{ backgroundColor: creator.colors.accent || '#6B0065' }"></div>
<!--actions - Lowerpart-->
<banner-actions :creator="creator" @content-posted="addContent"></banner-actions>
<banner-actions></banner-actions>
</div>
</template>
<script setup>
import BannerActions from "@/views/creators/banner/bannerlower/BannerActions.vue";
import {useBrandingStore} from "@/stores/brandingStore.js";
const props = defineProps({
creator: {type: Object, required: true},
});
const emits = defineEmits(['content-posted'])
function addContent(content) {
emits('content-posted', content)
}
const branding = useBrandingStore()
</script>

View File

@@ -0,0 +1,29 @@
<template>
<div v-if="brandingStore.value.loading">
<v-progress-linear indeterminate></v-progress-linear>
</div>
<div v-else>
<div class="w-full h-full pr-4">
<content-list :creator-id="brandingStore.value.id"
:contents="news"
></content-list>
</div>
</div>
</template>
<script async setup>
import { ref} from 'vue';
import {useBrandingStore} from "@/stores/brandingStore.js";
import ContentList from "@/views/contents/ContentList.vue";
const brandingStore = useBrandingStore()
const contents = ref([])
function contentPosted(content) {
contents.value.unshift(content)
}
</script>

View File

@@ -1,80 +0,0 @@
<template>
<div :style="{ backgroundColor: '#2D2728' }">
<div class="mt-10" v-if="creator && creator.id">
<div class="max-w-[1500px] mx-auto">
<creator-banner :creator="creator"
@content-posted="contentPosted"
></creator-banner>
</div>
</div>
<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>
<div class="flex flex-row max-w-[1500px] mx-auto justify-center mt-8">
<div>
<div class="flex flex-column px-2 max-w-[1200px]">
<exclusive-content-container :creator="creator"></exclusive-content-container>
</div>
</div>
<div class="max-w-80 hidden xl:block">
<!-- Rewards component will be visible only on xl screens -->
<rewards :creator="creator"></rewards>
</div>
</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 Creatornewssummary from "@/views/creators/CreatorNewsSummary.vue";
import Rewards from "@/views/creators/Rewards.vue";
import ExclusiveContentContainer from "@/views/creators/ExclusiveContentContainer.vue";
const creator = ref(null)
const loading = ref(true)
const contents = ref([])
const client = useClient()
const route = useRoute()
function contentPosted(content) {
contents.value.unshift(content)
}
onBeforeMount(async () => await fetchCreatorData(route.params.creator))
watch(
() => route.params.creator,
async () => await fetchCreatorData(route.params.creator)
)
const fetchCreatorData = async (creatorAlias) => {
try {
loading.value = true
const response = await client.get(`/api/creators/@${creatorAlias}`)
creator.value = response.data
} catch (error) {
console.error(`Error fetching content: ${error}`)
} finally {
loading.value = false
}
}
</script>

View File

@@ -0,0 +1,27 @@
<template>
<div class="flex flex-row">
<div class="max-w-80">
<creator-news-summary></creator-news-summary>
</div>
<div class="px-4" :style="{ color: brandingStore.value.colors.onBackground}">
<h1>TEST</h1>
<p>GET ME AN EDITOR NOW!</p>
</div>
</div>
</template>
<script setup>
import CreatorNewsSummary from "@/views/creators/CreatorNewsSummary.vue"
import {useBrandingStore} from "@/stores/brandingStore.js";
const brandingStore = useBrandingStore()
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,49 @@
<template>
<div :style="{ backgroundColor: brandingStore.value.colors.background }">
<div class="max-w-[1500px] mx-auto">
<div v-if="brandingStore.value && brandingStore.value.id">
<creator-banner></creator-banner>
</div>
<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>
<div class="flex flex-row justify-center py-10">
<div class="w-full">
<router-view></router-view>
</div>
<div class="max-w-80">
<rewards></rewards>
</div>
</div>
</div>
<Footer></Footer>
</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 Rewards from "@/views/creators/Rewards.vue";
import Footer from "@/views/main/Footer.vue";
import {useBrandingStore} from "@/stores/brandingStore.js";
const brandingStore = useBrandingStore()
</script>

View File

@@ -0,0 +1,29 @@
<template>
<div v-if="brandingStore.value.loading">
<v-progress-linear indeterminate></v-progress-linear>
</div>
<div v-else>
<div class="w-full h-full pr-4">
<news-list :creator-id="brandingStore.value.id"
:news="news"
></news-list>
</div>
</div>
</template>
<script async setup>
import {ref} from 'vue';
import NewsList from "@/views/news/NewsList.vue";
import {useBrandingStore} from "@/stores/brandingStore.js";
const brandingStore = useBrandingStore()
const news = ref([])
function newsPosted(content) {
news.value.unshift(content)
}
</script>

View File

@@ -1,30 +1,49 @@
<template>
<div class="overflow-hidden relative" @wheel="handleScroll">
<div class="text-center text-white rounded-t-lg mb-1 py-1"
:style="{ backgroundColor: creator.colors.bannerTop, letterSpacing: '5px' }">
<div class="text-center rounded-t-lg p-4 tracking-widest uppercase"
:style="{ color: brandingStore.value.colors.onPrimary, backgroundColor: brandingStore.value.colors.primary}">
Actualité
</div>
<!-- Container that holds all the posts and allows dynamic scrolling -->
<div class="relative max-h-[1000px] overflow-hidden">
<div class="transition-transform duration-500" :style="{ transform: `translateY(-${scrollPosition}px)` }">
<div v-for="(item, index) in actualites" :key="index" class="my-1 text-white"
:style="{ backgroundColor: creator.colors.bannerTop }">
<div class="flex justify-between items-center border-b-2 border-white p-3 mx-2">
<p v-if="item.type === 'nouvelle'" class="text-xl" :style="{ letterSpacing: '8px' }">Nouvelle</p>
<p v-if="item.type === 'contenu'" class="text-xl" :style="{ letterSpacing: '8px' }">Contenu</p>
<div v-for="(item, index) in articles"
class="my-1 text-white"
:key="index"
:style="{ backgroundColor: brandingStore.value.colors.primary }">
<div class="flex justify-between items-center border-b-2 border-white p-2 mx-2">
<p class="text-xl tracking-[8px]">
<span v-if="item.type === 'nouvelle'">
Nouvelle
</span>
<span v-if="item.type === 'contenu'">
Contenu
</span>
</p>
<p class="text-xs">{{ item.date }}</p>
</div>
<div class="p-3">
<div class="text-lg" style="letter-spacing: 2px">{{ item.title }}</div>
<p v-if="item.description" class="text-gray-300 text-sm text-justify mt-1 py-1">{{ item.description }}</p>
<div class="p-4">
<div class="text-lg tracking-[2px]">
{{ item.title }}
</div>
<p v-if="item.description"
class="text-gray-300 text-sm text-justify mt-1 py-1">
{{ item.description }}
</p>
<div v-if="item.rating" class="stars flex justify-end">
{{ item.rating }}
</div>
<img v-if="item.photo" :src="item.photo" class="w-full h-auto my-2"/>
<img v-if="item.photo"
:src="item.photo"
class="w-full h-auto my-2"/>
<video v-if="item.video"
controls
:src="item.video"
class="w-full h-auto my-2">
</video>
<div class="flex justify-evenly">
<v-btn icon variant="plain">
@@ -37,35 +56,19 @@
<v-icon>mdi-gift-outline</v-icon>
</v-btn>
</div>
<video v-if="item.video" controls :src="item.video" class="w-full h-auto my-2"></video>
</div>
</div>
</div>
</div>
<!-- Navigation Buttons directly below the content -->
<div class="flex justify-center mt-2 space-x-2" v-if="showNavigationButtons">
<v-btn variant="text" class="text-white" height="42" @click="scrollUp">
<v-icon size="40">mdi-arrow-up-drop-circle-outline</v-icon>
</v-btn>
<v-btn variant="text" class="text-white" height="42" @click="scrollDown">
<v-icon size="40">mdi-arrow-down-drop-circle-outline</v-icon>
</v-btn>
</div>
</div>
</template>
<script setup>
import { ref, computed, defineProps } from 'vue';
import {ref} from 'vue';
import {useBrandingStore} from "@/stores/brandingStore.js";
const props = defineProps({
creator: {
type: Object,
required: true,
},
});
const brandingStore = useBrandingStore()
const actualites = ref([
const articles = ref([
{
type: 'nouvelle',
title: 'La visite du studio',
@@ -113,45 +116,4 @@ const actualites = ref([
},
]);
const startIndex = ref(0);
const cardsPerPage = 3;
// Gestion de la position du scroll
const scrollPosition = ref(0);
const cardHeight = 250; // Hauteur approximative d'une carte en pixels
// Calculer l'index final
const endIndex = computed(() => startIndex.value + cardsPerPage);
const visibleCards = computed(() => actualites.value.slice(startIndex.value, endIndex.value));
// Calculer si les boutons doivent être affichés
const showNavigationButtons = computed(() => {
const totalHeight = actualites.value.length * cardHeight;
const containerHeight = cardsPerPage * cardHeight;
return totalHeight > containerHeight; // Afficher les boutons seulement si le contenu dépasse la hauteur du conteneur
});
function scrollDown() {
if (endIndex.value < actualites.value.length) {
startIndex.value += 1; // Défiler d'une carte à la fois
scrollPosition.value += cardHeight; // Augmenter la position de défilement
}
}
function scrollUp() {
if (startIndex.value > 0) {
startIndex.value -= 1; // Défiler d'une carte à la fois
scrollPosition.value -= cardHeight; // Diminuer la position de défilement
}
}
// Handle scroll wheel event to scroll up or down
function handleScroll(event) {
event.preventDefault(); // Empêche le défilement de la page
if (event.deltaY < 0) {
scrollUp(); // Défilement vers le haut
} else if (event.deltaY > 0) {
scrollDown(); // Défilement vers le bas
}
}
</script>

View File

@@ -1,71 +0,0 @@
<template>
<div :style="{ backgroundColor: '#2D2728' }">
<div class="mt-10" v-if="creator && creator.id">
<div class="max-w-[1500px] mx-auto">
<creator-banner :creator="creator"
@content-posted="contentPosted"
></creator-banner>
</div>
<div class="max-w-[800px] mx-auto">
<div class="w-full h-full ">
<content-list :creator-id="creator.id"
:contents="contents"
></content-list>
</div>
</div>
</div>
<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>
</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 ContentList from "@/views/contents/ContentList.vue";
const creator = ref(null)
const loading = ref(true)
const contents = ref([])
const client = useClient()
const route = useRoute()
function contentPosted(content) {
contents.value.unshift(content)
}
onBeforeMount(async () => await fetchCreatorData(route.params.creator))
watch(
() => route.params.creator,
async () => await fetchCreatorData(route.params.creator)
)
const fetchCreatorData = async (creatorAlias) => {
try {
loading.value = true
const response = await client.get(`/api/creators/@${creatorAlias}`)
creator.value = response.data
} catch (error) {
console.error(`Error fetching content: ${error}`)
} finally {
loading.value = false
}
}
</script>

View File

@@ -1,78 +0,0 @@
<template>
<div :style="{ backgroundColor: '#2D2728' }">
<div class="mt-10" v-if="creator && creator.id">
<div class="max-w-[1500px] mx-auto">
<creator-banner :creator="creator"
@content-posted="contentPosted"
></creator-banner>
</div>
</div>
<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>
<div class="flex flex-row max-w-[1500px] mx-auto justify-center mt-8">
<div>
<!-- Actualité feed-->
<div class="flex flex-column px-2 max-w-80">
<Creatornewssummary :creator="creator"></Creatornewssummary>
</div>
</div>
<div class="min-w-[875px]"></div>
<div class="max-w-80">
<rewards :creator="creator"></rewards>
</div>
</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 Creatornewssummary from "@/views/creators/CreatorNewsSummary.vue";
import Rewards from "@/views/creators/Rewards.vue";
const creator = ref(null)
const loading = ref(true)
const contents = ref([])
const client = useClient()
const route = useRoute()
function contentPosted(content) {
contents.value.unshift(content)
}
onBeforeMount(async () => await fetchCreatorData(route.params.creator))
watch(
() => route.params.creator,
async () => await fetchCreatorData(route.params.creator)
)
const fetchCreatorData = async (creatorAlias) => {
try {
loading.value = true
const response = await client.get(`/api/creators/@${creatorAlias}`)
creator.value = response.data
} catch (error) {
console.error(`Error fetching content: ${error}`)
} finally {
loading.value = false
}
}
</script>

View File

@@ -6,20 +6,20 @@
<v-dialog v-model="donationModal" max-width="500">
<v-form>
<v-card class="text-center rounded-xl" :style="{ border: `3px solid ${colorBorder}` }">
<v-card class="text-center rounded-xl" :style="{ border: `3px solid ${brandingStore.value.colors.primary}` }">
<div class="py-4 text-2xl font-bold border-b mb-2">
Je Soutiens!
</div>
<div class="flex flex-row align-center px-3">
<img
:src="creatorLogo"
:src="brandingStore.value.images.logo"
alt="Profile Image"
class="rounded-full"
width="40"
height="40"
:style="{ border: `2px solid ${colorAccent}` }">
<div class="capitalize px-2 text-2xl">{{ creatorName }}</div>
:style="{ border: `2px solid ${brandingStore.value.colors.secondary}` }">
<div class="capitalize px-2 text-2xl">{{ brandingStore.value.name }}</div>
<v-btn icon @click="closeDonationDialog()" class="ml-auto" variant="text">
<v-icon>mdi-close</v-icon>
</v-btn>
@@ -50,7 +50,8 @@
clearable
></v-textarea>
<v-btn variant="outlined" :style="{ borderColor: colorBorder, color: colorBorder }"
<v-btn variant="outlined"
:style="{ borderColor: brandingStore.value.colors.primary, color: brandingStore.value.colors.primary }"
@click="goPay()" class="w-full mt-5">
Envoyez
</v-btn>
@@ -68,7 +69,10 @@
<v-card-actions>
<v-spacer></v-spacer>
<v-btn block class="ma-auto" style="width: 200px;" text @click="closeDialog()">Annuler</v-btn>
<v-btn block class="ma-auto"
style="width: 200px;"
@click="closeDialog()">Annuler
</v-btn>
</v-card-actions>
</v-card>
</template>
@@ -79,23 +83,15 @@
<script setup>
import {useClient} from '@/plugins/api.js';
import {loadStripe} from '@stripe/stripe-js';
import {computed, onMounted, ref} from 'vue';
import {onMounted, ref} from 'vue';
import {useBrandingStore} from "@/stores/brandingStore.js";
const brandingStore = useBrandingStore()
const props = defineProps({
colorBorder: {required: true},
colorAccent: {required: true},
creatorId: {type: String, required: true},
creatorName: {type: String, required: true},
creatorLogo: {required: true},
iconColorClass: {default: 'text-black'}
});
const colorBorder = computed(() => props.colorBorder)
const colorAccent = computed(() => props.colorAccent)
const creatorId = computed(() => props.creatorId)
const creatorName = computed(() => props.creatorName)
const creatorLogo = computed(() => props.creatorLogo)
const donationModal = ref(false);
function openDonationDialog() {
@@ -145,6 +141,7 @@ function closeDialog() {
checkout.destroy();
}
}
async function goPay() {
isPaymentDialogActive.value = true;

View File

@@ -1,33 +1,32 @@
<template>
<v-btn :style="{
backgroundColor: colorBorder, color: 'white',
borderRadius: '8px', padding:'20px 24px',
display: 'flex', alignItems: 'center',
justifyContent: 'center' }" @click="openDonationDialog()">
backgroundColor: brandingStore.value.colors.primary,
color: 'white',
borderRadius: '8px',
padding:'20px 24px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center' }"
@click="openDonationDialog()">
<div class="font-bold"> Je soutiens</div>
</v-btn>
<v-dialog v-model="donationModal" max-width="500">
<v-form>
<v-card class="text-center rounded-xl" :style="{ border: `3px solid ${colorBorder}` }">
<v-card class="text-center rounded-xl" :style="{ border: `3px solid ${brandingStore.value.colors.primary}` }">
<div class="py-4 text-2xl font-bold border-b mb-2">
Je Soutiens!
</div>
<div class="flex flex-row align-center px-3">
<img
:src="creatorLogo"
:src="brandingStore.value.images.logo"
alt="Profile Image"
class="rounded-full"
width="40"
height="40"
:style="{ border: `2px solid ${colorAccent}` }">
<div class="capitalize px-2 text-2xl">{{ creatorName }}</div>
:style="{ border: `2px solid ${brandingStore.value.colors.secondary}` }">
<div class="capitalize px-2 text-2xl">{{ brandingStore.value.name }}</div>
<v-btn icon @click="closeDonationDialog()" class="ml-auto" variant="text">
<v-icon>mdi-close</v-icon>
</v-btn>
@@ -58,7 +57,7 @@
clearable
></v-textarea>
<v-btn variant="outlined" :style="{ borderColor: colorBorder, color: colorBorder }"
<v-btn variant="outlined" :style="{ borderColor: brandingStore.value.colors.primary, color: brandingStore.value.colors.primary }"
@click="goPay()" class="w-full mt-5">
Envoyez
</v-btn>
@@ -87,22 +86,10 @@
<script setup>
import {useClient} from '@/plugins/api.js';
import {loadStripe} from '@stripe/stripe-js';
import {computed, onMounted, ref} from 'vue';
import {onMounted, ref} from 'vue';
import {useBrandingStore} from "@/stores/brandingStore.js";
const props = defineProps({
colorBorder: {required: true},
colorAccent: {required: true},
creatorId: {type: String, required: true},
creatorName: {type: String, required: true},
creatorLogo: {required: true},
iconColorClass: {default: 'text-black'}
});
const colorBorder = computed(() => props.colorBorder)
const colorAccent = computed(() => props.colorAccent)
const creatorId = computed(() => props.creatorId)
const creatorName = computed(() => props.creatorName)
const creatorLogo = computed(() => props.creatorLogo)
const brandingStore = useBrandingStore()
const donationModal = ref(false);
@@ -114,7 +101,6 @@ function closeDonationDialog() {
donationModal.value = false
}
const isPaymentDialogActive = ref(false);
const tipAmount = ref(0);
@@ -153,6 +139,7 @@ function closeDialog() {
checkout.destroy();
}
}
async function goPay() {
isPaymentDialogActive.value = true;

View File

@@ -1,33 +1,32 @@
<template>
<v-btn :style="{
backgroundColor: colorBorder, color: 'white',
borderRadius: '8px', padding:'0px 24px',
display: 'flex', alignItems: 'center',
justifyContent: 'center' }" @click="openDonationDialog()">
backgroundColor: brandingStore.value.colors.primary,
color: 'white',
borderRadius: '8px',
padding:'0px 24px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center' }"
@click="openDonationDialog()">
<div class="font-bold">Je soutiens</div>
</v-btn>
<v-dialog v-model="donationModal" max-width="500">
<v-form>
<v-card class="text-center rounded-xl" :style="{ border: `3px solid ${colorBorder}` }">
<v-card class="text-center rounded-xl" :style="{ border: `3px solid ${brandingStore.value.colors.primary}` }">
<div class="py-4 text-2xl font-bold border-b mb-2">
Je Soutiens!
</div>
<div class="flex flex-row align-center px-3">
<img
:src="creatorLogo"
:src="brandingStore.value.images.logo"
alt="Profile Image"
class="rounded-full"
width="40"
height="40"
:style="{ border: `2px solid ${colorAccent}` }">
<div class="capitalize px-2 text-2xl">{{ creatorName }}</div>
:style="{ border: `2px solid ${brandingStore.value.colors.secondary}` }">
<div class="capitalize px-2 text-2xl">{{ brandingStore.value.name }}</div>
<v-btn icon @click="closeDonationDialog()" class="ml-auto" variant="text">
<v-icon>mdi-close</v-icon>
</v-btn>
@@ -58,7 +57,8 @@
clearable
></v-textarea>
<v-btn variant="outlined" :style="{ borderColor: colorBorder, color: colorBorder }"
<v-btn variant="outlined"
:style="{ borderColor: brandingStore.value.colors.primary, color: brandingStore.value.colors.primary }"
@click="goPay()" class="w-full mt-5">
Envoyez
</v-btn>
@@ -76,7 +76,7 @@
<v-card-actions>
<v-spacer></v-spacer>
<v-btn block class="ma-auto" style="width: 200px;" text @click="closeDialog()">Annuler</v-btn>
<v-btn block class="ma-auto" style="width: 200px;" @click="closeDialog()">Annuler</v-btn>
</v-card-actions>
</v-card>
</template>
@@ -87,22 +87,10 @@
<script setup>
import {useClient} from '@/plugins/api.js';
import {loadStripe} from '@stripe/stripe-js';
import {computed, onMounted, ref} from 'vue';
import {onMounted, ref} from 'vue';
import {useBrandingStore} from "@/stores/brandingStore.js";
const props = defineProps({
colorBorder: {required: true},
colorAccent: {required: true},
creatorId: {type: String, required: true},
creatorName: {type: String, required: true},
creatorLogo: {required: true},
iconColorClass: {default: 'text-black'}
});
const colorBorder = computed(() => props.colorBorder)
const colorAccent = computed(() => props.colorAccent)
const creatorId = computed(() => props.creatorId)
const creatorName = computed(() => props.creatorName)
const creatorLogo = computed(() => props.creatorLogo)
const brandingStore = useBrandingStore()
const donationModal = ref(false);
@@ -153,6 +141,7 @@ function closeDialog() {
checkout.destroy();
}
}
async function goPay() {
isPaymentDialogActive.value = true;

View File

@@ -1,27 +1,22 @@
<script setup>
import {useSubscriptionStore} from "@/stores/subscriptionStore.js";
import {computed, ref} from "vue";
import {useSubscriptionStore} from "@/stores/subscriptionStore.js";
import {useBrandingStore} from "@/stores/brandingStore.js";
const props = defineProps({
creator: {type: Object, required: true},
colorBorder: {required: true},
});
const colorBorder = computed(() => props.colorBorder);
const brandingStore = useBrandingStore()
const subscriptionStore = useSubscriptionStore();
const isSubscribe = computed(() => !subscriptionStore.isSubscribeTo(props.creator.id));
const isSubscribe = computed(() => !subscriptionStore.isSubscribeTo(brandingStore.value.id));
function subscribeToCreator() {
subscriptionStore.subscribeTo(props.creator.id);
subscriptionStore.subscribeTo(brandingStore.value.id);
}
// Référence pour contrôler l'affichage du modal
const showUnsubscribeModal = ref(false);
function unsubscribeFromCreator() {
subscriptionStore.unsubscribeFrom(props.creator.id);
subscriptionStore.unsubscribeFrom(brandingStore.value.id);
// Fermer le modal après désabonnement
showUnsubscribeModal.value = false;
}
@@ -33,7 +28,7 @@ function unsubscribeFromCreator() {
:style="{
width: '150px',
height: '28px',
backgroundColor: colorBorder,
backgroundColor: brandingStore.value.colors.secondary,
color: 'white',
borderRadius: '8px 0 0 8px',
padding: '10px 24px',
@@ -53,7 +48,7 @@ function unsubscribeFromCreator() {
:style="{
width: '150px',
height: '28px',
backgroundColor: colorBorder,
backgroundColor: brandingStore.value.colors.secondary,
color: 'white',
borderRadius: '8px 0 0 8px',
padding: '10px 24px',
@@ -70,7 +65,7 @@ function unsubscribeFromCreator() {
<v-dialog v-model="showUnsubscribeModal" max-width="500">
<v-card class="text-center rounded-xl"
:style="{ border: `3px solid ${creator.colors.menu}` }">
:style="{ border: `3px solid ${brandingStore.value.colors.secondary}` }">
<div class="flex items-center justify-between py-4 text-2xl font-bold border-b mb-2">
<div class="flex-1 text-center">
@@ -87,9 +82,10 @@ function unsubscribeFromCreator() {
</v-btn>
<v-btn class="flex-grow-1"
:style="{ borderColor: creator.colors.menu, color: creator.colors.menu }" variant="outlined"
:style="{ borderColor: brandingStore.value.colors.secondary, color: brandingStore.value.colors.secondary }"
variant="outlined"
@click="showUnsubscribeModal = false">
<div :style="{ color: creator.colors.menu }">Non</div>
<div :style="{ color: brandingStore.value.colors.secondary }">Non</div>
</v-btn>
</v-card-actions>
</v-card>

View File

@@ -1,52 +1,41 @@
<template>
<div class="overflow-hidden relative" @wheel="handleScroll">
<div class="text-center rounded-t-lg p-4 tracking-widest uppercase"
:style="{ color: brandingStore.value.colors.onPrimary, backgroundColor: brandingStore.value.colors.primary}">
Récompenses
</div>
<div class="text-center text-white rounded-t-lg py-1 mb-1" :style="{ backgroundColor: creator.colors.bannerTop, letterSpacing: '5px' }">Récompenses</div>
<div v-for="(item, index) in rewards"
class="my-1 text-white"
:key="index"
:style="{ backgroundColor: brandingStore.value.colors.primary }">
<!-- Container that holds all the posts and allows dynamic scrolling -->
<div class="relative h-[1000px] max-h-[1000px] overflow-hidden">
<div class="transition-transform duration-500" :style="{ transform: `translateY(-${scrollPosition}px)` }">
<div v-for="(item, index) in actualites" :key="index" class="my-1 text-white p-4" :style="{ backgroundColor: creator.colors.bannerTop }">
<div class="flex justify-center items-center border-b-2 border-white p-3 mx-2">
<div class="text-xl align-center" style="letter-spacing: 4px">{{ item.title }}</div>
<div class="flex justify-center border-b-2 border-white p-3 mx-2 text-xl tracking-[4px]">
{{ item.title }}
</div>
<div class="p-3">
<p v-if="item.description" class="text-gray-300 text-sm text-justify mt-1 py-1">{{ item.description }}</p>
<p v-if="item.description" class="text-gray-300 text-sm text-justify mt-1 py-1">
{{ item.description }}
</p>
<div v-if="item.amount" class="flex flex-row justify-end space-x-2">
<div class="text-right text-white">{{ item.amount }}$</div>
<div>|</div>
<div v-if="item.quantity"> Quantité: {{ item.quantity }}</div>
</div>
</div>
</div>
</div>
</div>
<!-- Navigation Buttons directly below the content -->
<div v-if="showNavigationButtons" class="flex justify-center mt-2 py-2">
<v-btn variant="text" class="text-white" height="42" @click="scrollUp">
<v-icon size="40">mdi-arrow-up-drop-circle-outline</v-icon>
</v-btn>
<v-btn variant="text" class="text-white" height="42" @click="scrollDown">
<v-icon size="40">mdi-arrow-down-drop-circle-outline</v-icon>
</v-btn>
</div>
</div>
</template>
<script setup>
import { ref, computed, defineProps } from 'vue';
import {ref} from 'vue';
import {useBrandingStore} from "@/stores/brandingStore.js";
const props = defineProps({
creator: {
type: Object,
required: true,
},
});
const brandingStore = useBrandingStore()
const actualites = ref([
const rewards = ref([
{
title: 'Hoodie',
description: 'Je suis fier de vous montrer les installations sur lesquelles nous avons travaillé si fort.',
@@ -91,45 +80,5 @@ const actualites = ref([
},
]);
const startIndex = ref(0);
const cardsPerPage = 3;
const scrollPosition = ref(0);
const cardHeight = 250;
const endIndex = computed(() => startIndex.value + cardsPerPage);
const visibleCards = computed(() => actualites.value.slice(startIndex.value, endIndex.value));
// Calculate whether navigation buttons should be shown
const showNavigationButtons = computed(() => {
const totalHeight = actualites.value.length * cardHeight;
const containerHeight = cardsPerPage * cardHeight;
return totalHeight > containerHeight; // Only show buttons if content exceeds the container
});
function scrollUp() {
if (startIndex.value > 0) {
startIndex.value -= 1;
scrollPosition.value -= cardHeight;
}
}
function scrollDown() {
const totalHeight = actualites.value.length * cardHeight;
const containerHeight = cardsPerPage * cardHeight;
if (scrollPosition.value + containerHeight < totalHeight) {
startIndex.value += 1;
scrollPosition.value += cardHeight;
}
}
// Handle scroll wheel event to scroll up or down
function handleScroll(event) {
event.preventDefault(); // Empêche le défilement de la page
if (event.deltaY < 0) {
scrollUp(); // Défilement vers le haut
} else if (event.deltaY > 0) {
scrollDown(); // Défilement vers le bas
}
}
</script>

View File

@@ -1,27 +1,22 @@
<script setup>
import {useSubscriptionStore} from "@/stores/subscriptionStore.js";
import {computed, ref} from "vue";
import {useBrandingStore} from "@/stores/brandingStore.js";
const props = defineProps({
creator: {type: Object, required: true},
colorBorder: {required: true},
});
const brandingStore = useBrandingStore()
const subscriptionStore = useSubscriptionStore()
const colorBorder = computed(() => props.colorBorder);
const subscriptionStore = useSubscriptionStore();
const isSubscribe = computed(() => !subscriptionStore.isSubscribeTo(props.creator.id));
const isSubscribe = computed(() => !subscriptionStore.isSubscribeTo(brandingStore.value.id));
function subscribeToCreator() {
subscriptionStore.subscribeTo(props.creator.id);
subscriptionStore.subscribeTo(brandingStore.value.id);
}
// Référence pour contrôler l'affichage du modal
const showUnsubscribeModal = ref(false);
function unsubscribeFromCreator() {
subscriptionStore.unsubscribeFrom(props.creator.id);
subscriptionStore.unsubscribeFrom(brandingStore.value.id);
// Fermer le modal après désabonnement
showUnsubscribeModal.value = false;
}
@@ -33,7 +28,7 @@ function unsubscribeFromCreator() {
:style="{
width: '150px',
height: '28px',
backgroundColor: colorBorder,
backgroundColor: brandingStore.value.colors.secondary,
color: 'white',
borderRadius: '0 8px 8px 0',
padding: '10px 24px',
@@ -44,7 +39,7 @@ function unsubscribeFromCreator() {
}"
@click="subscribeToCreator"
>
<div>{{ $t('subscribebutton.subscribe') }}</div>
{{ $t('subscribebutton.subscribe') }}
</v-btn>
</template>
@@ -53,7 +48,7 @@ function unsubscribeFromCreator() {
:style="{
width: '150px',
height: '28px',
backgroundColor: colorBorder,
backgroundColor: brandingStore.value.colors.secondary,
color: 'white',
borderRadius: '0 8px 8px 0',
padding: '10px 24px',
@@ -70,7 +65,7 @@ function unsubscribeFromCreator() {
<v-dialog v-model="showUnsubscribeModal" max-width="500">
<v-card class="text-center rounded-xl"
:style="{ border: `3px solid ${creator.colors.menu}` }">
:style="{ border: `3px solid ${brandingStore.value.colors.secondary}` }">
<div class="flex items-center justify-between py-4 text-2xl font-bold border-b mb-2">
<div class="flex-1 text-center">
@@ -87,9 +82,10 @@ function unsubscribeFromCreator() {
</v-btn>
<v-btn class="flex-grow-1"
:style="{ borderColor: creator.colors.menu, color: creator.colors.menu }" variant="outlined"
:style="{ borderColor: brandingStore.value.colors.secondary, color: brandingStore.value.colors.secondary }"
variant="outlined"
@click="showUnsubscribeModal = false">
<div :style="{ color: creator.colors.menu }">Non</div>
<div :style="{ color: brandingStore.value.colors.secondary }">Non</div>
</v-btn>
</v-card-actions>
</v-card>

View File

@@ -1,28 +1,21 @@
<script setup>
import {useSubscriptionStore} from "@/stores/subscriptionStore.js";
import {computed, ref} from "vue";
import {useSubscriptionStore} from "@/stores/subscriptionStore.js";
import {useBrandingStore} from "@/stores/brandingStore.js";
const props = defineProps({
creator: {type: Object, required: true},
colorBorder: {required: true},
});
const colorBorder = computed(() => props.colorBorder);
const brandingStore = useBrandingStore()
const subscriptionStore = useSubscriptionStore();
const isSubscribe = computed(() => !subscriptionStore.isSubscribeTo(props.creator.id));
const isSubscribe = computed(() => !subscriptionStore.isSubscribeTo(brandingStore.value.id));
function subscribeToCreator() {
subscriptionStore.subscribeTo(props.creator.id);
subscriptionStore.subscribeTo(brandingStore.value.id);
}
const showUnsubscribeModal = ref(false);
function unsubscribeFromCreator() {
subscriptionStore.unsubscribeFrom(props.creator.id);
subscriptionStore.unsubscribeFrom(brandingStore.value.id);
showUnsubscribeModal.value = false;
}
</script>
@@ -32,7 +25,7 @@ function unsubscribeFromCreator() {
<v-btn
class="mr-4"
:style="{
backgroundColor: colorBorder,
backgroundColor: brandingStore.value.colors.secondary,
color: 'white',
borderRadius: '8px',
padding: '0px 24px',
@@ -51,7 +44,7 @@ function unsubscribeFromCreator() {
<v-btn
class="mr-4"
:style="{
backgroundColor: colorBorder,
backgroundColor: brandingStore.value.colors.secondary,
color: 'white',
borderRadius: '8px',
padding: '0px 24px',
@@ -68,7 +61,7 @@ function unsubscribeFromCreator() {
<v-dialog v-model="showUnsubscribeModal" max-width="500">
<v-card class="text-center rounded-xl"
:style="{ border: `3px solid ${creator.colors.menu}` }">
:style="{ border: `3px solid ${brandingStore.value.colors.primary}` }">
<div class="flex items-center justify-between py-4 text-2xl font-bold border-b mb-2">
<div class="flex-1 text-center">
@@ -85,9 +78,10 @@ function unsubscribeFromCreator() {
</v-btn>
<v-btn class="flex-grow-1"
:style="{ borderColor: creator.colors.menu, color: creator.colors.menu }" variant="outlined"
:style="{ borderColor: brandingStore.value.colors.primary, color: brandingStore.value.colors.primary }"
variant="outlined"
@click="showUnsubscribeModal = false">
<div :style="{ color: creator.colors.menu }">Non</div>
<div :style="{ color: brandingStore.value.colors.primary }">Non</div>
</v-btn>
</v-card-actions>
</v-card>

View File

@@ -12,7 +12,8 @@ const subscriptionStore = useSubscriptionStore()
<RouterLink class="capitalize" :to="`/@${subscription.creatorName}`">
<div class="flex items-center content-center font-sans font-semibold pt-2 ">
<img :src="subscription.creatorPortraitUrl ? subscription.creatorPortraitUrl: '/images/usersmedia/anonyme/profilepictures/profileAnonymeSquare.png' "
<img
:src="subscription.creatorPortraitUrl ? subscription.creatorPortraitUrl: '/images/usersmedia/anonyme/profilepictures/profileAnonymeSquare.png' "
alt="Profile Image"
class="rounded-full mx-2"
width="32px"
@@ -26,9 +27,7 @@ const subscriptionStore = useSubscriptionStore()
</template>
<template v-else>
<slot>
<span>No subscriptions</span>
</slot>
</template>
</template>

View File

@@ -4,31 +4,21 @@ import BannerActionsSm from "@/views/creators/banner/bannerlower/BannerActionsSm
import BannerActionsLg from "@/views/creators/banner/bannerlower/BannerActionsLg.vue";
import BannerActionsXl from "@/views/creators/banner/bannerlower/BannerActionsXl.vue";
const props = defineProps({
creator: { type: Object, required: true }
});
const emits = defineEmits(['content-posted']);
function addContent(content) {
emits('content-posted', content);
}
</script>
<template>
<div>
<banner-actions-sm class="d-sm-none" :creator="creator" @content-posted="addContent"></banner-actions-sm>
<banner-actions-sm class="d-sm-none"
></banner-actions-sm>
<div class="d-none d-sm-flex d-md-none">
<banner-actions-md :creator="creator" @content-posted="addContent"></banner-actions-md>
</div>
<banner-actions-md class="d-none d-sm-flex d-md-none"
></banner-actions-md>
<div class="d-none d-md-flex d-lg-none">
<banner-actions-lg :creator="creator" @content-posted="addContent"></banner-actions-lg>
</div>
<banner-actions-lg class="d-none d-md-flex d-lg-none"
></banner-actions-lg>
<banner-actions-xl class="d-none d-lg-flex"
></banner-actions-xl>
<div class="d-none d-lg-flex">
<banner-actions-xl :creator="creator" @content-posted="addContent"></banner-actions-xl>
</div>
</div>
</template>

View File

@@ -1,7 +1,7 @@
<template>
<div class="relative w-full">
<div ref="mainContainer" class="rounded-b-2xl pt-2 pb-1"
:style="{ backgroundColor: creator.colors.bannerBottom || '#A30E79', borderBottom: '5px inset' + (creator.colors.menu || '#000') }">
:style="{ backgroundColor: brandingStore.value.colors.bannerBottom, borderBottom: '5px inset' + (brandingStore.value.colors.menu || '#000') }">
<!-- Logo & User Info -->
<div class="relative z-20">
@@ -9,36 +9,21 @@
<div>
<img
class="shadow-2xl rounded-full border-solid border-4 absolute z-20 max-w-[190px] ml-15 -mt-32"
:src="creator.images.logo ? creator.images.logo : '/images/placeholders/logo.png'"
:src="brandingStore.value.images.logo ? brandingStore.value.images.logo : '/images/placeholders/logo.png'"
alt="Profile Picture"
:style="{ borderColor: creator.colors.accent || '#A30E79', height: '190px'}"
:style="{ borderColor: brandingStore.value.colors.accent, height: '190px'}"
/>
</div>
<div class="ml-72 text-white mr-10">
<p class="capitalize text-4xl font-bold">{{ creator.name }}</p>
<div class="text-2xl text-white flex flex-row align-center">
{{ creator.about.title }}
<creator-about class="px-2" :creator="creator"></creator-about>
</div>
</div>
<div class="flex flex-row ml-auto space-x-2.5">
<donation-button-banner
:color-border="creator.colors.menu"
:color-accent="creator.colors.accent"
:creator-id="creator.id"
:creator-name="creator.name"
:creator-logo="creator.images.logo"
iconColorClass="text-white">
</donation-button-banner>
<donation-button-banner></donation-button-banner>
<div class="flex flex-column">
<!-- Bouton abonnement affiché seulement si non abonné -->
<subscribe-button
<subscribe-button></subscribe-button>
:creator="creator"
:color-border="creator.colors.menu">
</subscribe-button>
<div class="font-bold text-white flex justify-end mr-5 py-1.5">
{{ creator.subscriberCount }} {{ $t('banner.subscription') }}
{{ brandingStore.value.subscriberCount }} {{ $t('banner.subscription') }}
</div>
</div>
</div>
@@ -48,36 +33,27 @@
<!-- Conteneur sticky -->
<div v-show="isSticky" class=" sticky-header fixed top-14 left-0 right-0 w-full z-20"
:style="{ backgroundColor: creator.colors.bannerBottom || '#A30E79', borderBottom: '5px inset' + (creator.colors.menu || '#000') }">
:style="{ backgroundColor: brandingStore.value.colors.bannerBottom , borderBottom: '5px inset' + (brandingStore.value.colors.menu || '#000') }">
<div class="shadow-3xl flex flex-row items-center py-2 px-2">
<div>
<img
class="max-w-[40px] max-h-[40px] ml-5 rounded-full"
:src="creator.images.logo ? creator.images.logo : '/images/placeholders/logo.png'"
:src="brandingStore.value.images.logo ? brandingStore.value.images.logo : '/images/placeholders/logo.png'"
alt="Profile Picture"
:style="{ borderColor: creator.colors.accent || '#A30E79', height: '190px'}"
:style="{ borderColor: brandingStore.value.colors.accent, height: '190px'}"
/>
</div>
<div class="ml-5 text-white">
<p class="capitalize text-2xl font-bold">{{ creator.name }}</p>
<p class="capitalize text-2xl font-bold">{{ brandingStore.value.name }}</p>
<p class="capitalize text-xl mt-1">{{ brandingStore.value.title }}</p>
</div>
<div class="ml-auto flex flex-row space-x-2.5 mr-3 ">
<donation-button-banner-slim
class=""
:color-border="creator.colors.menu"
:color-accent="creator.colors.accent"
:creator-id="creator.id"
:creator-name="creator.name"
:creator-logo="creator.images.logo"
>
</donation-button-banner-slim>
<donation-button-banner-slim></donation-button-banner-slim>
<!-- Afficher le bouton d'abonnement seulement si l'utilisateur n'est pas abonné -->
<subscribe-button-slim
v-if="!isSubscribed"
:creator="creator"
:color-border="creator.colors.menu">
v-if="!isSubscribed">
</subscribe-button-slim>
</div>
@@ -89,21 +65,18 @@
<script setup>
import {ref, onMounted, computed} from 'vue';
import { useSubscriptionStore } from "@/stores/subscriptionStore.js";
import SubscribeButton from "@/views/creators/SubscribeButton.vue";
import DonationButtonBanner from "@/views/creators/DonationButtonBanner.vue";
import CreatorAbout from "@/views/creators/CreatorAbout.vue";
import SubscribeButtonSlim from "@/views/creators/SubscribeButtonSlim.vue";
import DonationButtonBannerSlim from "@/views/creators/DonationButtonBannerSlim.vue";
import {useSubscriptionStore} from "@/stores/subscriptionStore.js";
import {useBrandingStore} from "@/stores/brandingStore.js";
const props = defineProps({
creator: { type: Object, required: true }
});
const subscriptionStore = useSubscriptionStore();
const brandingStore = useBrandingStore()
const subscriptionStore = useSubscriptionStore()
// Calculer si l'utilisateur est abonné
const isSubscribed = computed(() => subscriptionStore.isSubscribeTo(props.creator.id));
const isSubscribed = computed(() => subscriptionStore.isSubscribeTo(brandingStore.value.id));
const isSticky = ref(false);
const mainContainer = ref(null);

View File

@@ -1,7 +1,7 @@
<template>
<div class="w-full">
<div class="rounded-b-2xl"
:style="{ backgroundColor: creator.colors.bannerBottom || '#A30E79' }">
:style="{ backgroundColor: brandingStore.value.colors.bannerBottom }">
<div>
<!-- Logo-Name-Followers -->
@@ -9,37 +9,27 @@
<div>
<img
class="absolute rounded-full border-solid border-2 max-w-[140px] h-auto ml-3 -mt-3"
:src="creator.images.logo ? creator.images.logo : '/images/placeholders/logo.png'"
:src="brandingStore.value.images.logo ? brandingStore.value.images.logo : '/images/placeholders/logo.png'"
alt="Profile Picture"
:style="{ borderColor: creator.colors.accent || '#A30E79', height: '150px'}"
:style="{ borderColor: brandingStore.value.colors.accent, height: '150px'}"
/>
</div>
<div class="flex flex-column text-white cap px-2 mt-1 w-full ml-40">
<div class="flex justify-between">
<div>
<p class="capitalize text-2xl font-bold">{{ creator.name }}</p>
<div>{{ creator.subscriberCount }} {{ $t('banner.subscription') }}</div>
<p class="capitalize text-2xl font-bold">{{ brandingStore.value.name }}</p>
<p class="capitalize text-xl mt-1">{{ brandingStore.value.title }}</p>
<div>{{ brandingStore.value.subscriberCount }} {{ $t('banner.subscription') }}</div>
</div>
<div class="flex items-center">
<div class="mt-1">{{ creator.about.title }}</div>
<creator-about :creator="creator"></creator-about>
</div>
</div>
<div class="flex justify-between mt-2">
<subscribe-button :creator="creator"></subscribe-button>
<subscribe-button></subscribe-button>
<div class="flex space-x-2">
<publish-content-button :creator="creator"
@content-posted="addContent"
></publish-content-button>
<donation-button :color-border="creator.colors.menu"
:color-accent="creator.colors.accent"
:creator-id="creator.id"
:creator-name="creator.name"
:creator-logo="creator.images.logo"
iconColorClass="text-white"
<donation-button iconColorClass="text-white"
></donation-button>
</div>
</div>
@@ -54,17 +44,9 @@
<script setup>
import SubscribeButton from "@/views/creators/SubscribeButton.vue";
import PublishContentButton from "@/views/contents/PublishContentButton.vue";
import DonationButton from "@/views/creators/DonationButton.vue";
import CreatorAbout from "@/views/creators/CreatorAbout.vue";
import {useBrandingStore} from "@/stores/brandingStore.js";
const props = defineProps({
creator: {type: Object, required: true}
});
const brandingStore = useBrandingStore()
const emits = defineEmits(['content-posted']);
function addContent(content) {
emits('content-posted', content);
}
</script>

View File

@@ -1,32 +1,14 @@
<template>
<div class="w-full">
<div
:style="{
backgroundColor: creator.colors.bannerBottom || '#A30E79',
borderBottom: `2px solid ${creator.colors.accent || '#000000'}`
}">
:style="{backgroundColor: brandingStore.value.colors.bannerBottom, borderBottom: `2px solid ${brandingStore.value.colors.accent}`}">
<div>
<!-- Logo-Name-Followers-->
<div class="flex flex-row relative z-20">
<div>
<!-- bug space-->
<!-- <img-->
<!-- -->
<!-- class="rounded-full border-solid border-2 -mt-4 max-w-[80px] h-auto ml-2"-->
<!-- :src="creator.images.logo ? creator.images.logo : '/images/placeholders/logo.png'"-->
<!-- alt="Profile Picture"-->
<!-- :style="{ borderColor: creator.colors.accent || '#A30E79', height: '150px'}"-->
<!-- />-->
</div>
<div class="flex flex-column text-white capitalize px-2 mt-1">
<p class="capitalize text-2xl font-bold">{{ creator.name }}</p>
<div>{{ creator.subscriberCount }} {{ $t('banner.subscription')}}</div>
</div>
<div class="flex ml-auto text-right text-white text-lg px-3 mt-2">
<div class="mt-1">{{ creator.about.title }}</div>
<creator-about :creator="creator"></creator-about>
<p class="capitalize text-2xl font-bold">{{ brandingStore.value.name }}</p>
<div>{{ brandingStore.value.subscriberCount }} {{ $t('banner.subscription') }}</div>
</div>
</div>
@@ -35,20 +17,11 @@
<div class="flex flex-row items-center justify-between w-full px-4">
<div>
<subscribe-button :creator="creator"></subscribe-button>
<subscribe-button></subscribe-button>
</div>
<div class="flex ml-auto space-x-4">
<publish-content-button :creator="creator"
@content-posted="addContent"
></publish-content-button>
<donation-button :color-border="creator.colors.menu"
:color-accent="creator.colors.accent"
:creator-id="creator.id"
:creator-name="creator.name"
:creator-logo="creator.images.logo"
iconColorClass="text-white"
<donation-button iconColorClass="text-white"
></donation-button>
</div>
@@ -62,17 +35,9 @@
<script setup>
import SubscribeButton from "@/views/creators/SubscribeButton.vue";
import PublishContentButton from "@/views/contents/PublishContentButton.vue";
import DonationButton from "@/views/creators/DonationButton.vue";
import CreatorAbout from "@/views/creators/CreatorAbout.vue";
import {useBrandingStore} from "@/stores/brandingStore.js";
const props = defineProps({
creator: {type: Object, required: true}
});
const brandingStore = useBrandingStore()
const emits = defineEmits(['content-posted']);
function addContent(content) {
emits('content-posted', content);
}
</script>

View File

@@ -2,8 +2,9 @@
<div class="flex flex-column w-full">
<div class="relative w-full shadow-xl rounded-2xl">
<div ref="mainContainer" class="rounded-b-2xl shadow-2xl"
:style="{ backgroundColor: creator.colors.bannerTop || '#A30E79', boxShadow: '0 5px 10px rgba(0, 0, 0, 0.3)' }">
:style="{ backgroundColor: brandingStore.value.colors.primary, boxShadow: '0 5px 10px rgba(0, 0, 0, 0.3)' }">
<div>
<div>
@@ -12,19 +13,17 @@
<div class="absolute">
<div>
<img
class="shadow-2xl rounded-full border-solid border-2 absolute z-20 max-w-[190px] ml-10 -mt-10"
:src="creator.images.logo ? creator.images.logo : '/images/placeholders/logo.png'"
class="shadow-2xl rounded-full border-solid border-102 absolute z-20 max-w-[190px] ml-10 -mt-10"
:src="brandingStore.value.images.logo ? brandingStore.value.images.logo : '/images/placeholders/logo.png'"
alt="Profile Picture"
:style="{ borderColor: creator.colors.accent || '#A30E79', height: '190px'}"
:style="{ borderColor: brandingStore.value.colors.secondary, height: '190px'}"
/>
</div>
<div class="ml-64 text-white w-25 min-w-60">
<p class="capitalize text-2xl mt-1">{{ creator.name }}</p>
<div class="text-md py-1">
{{ creator.about.title }}
</div>
<p class="capitalize text-2xl mt-1">{{ brandingStore.value.name }}</p>
<p class="capitalize text-xl mt-1">{{ brandingStore.value.title }}</p>
<div class="text-xs">
105 Followers - {{ creator.subscriberCount }} {{ $t('banner.subscription') }}
105 Followers - {{ brandingStore.value.subscriberCount }} {{ $t('banner.subscription') }}
</div>
</div>
</div>
@@ -51,34 +50,30 @@
</div>
<!-- Follow and Subscribe Buttons -->
<div class="flex flex-row space-x-1 justify-center mt-3 mb-2">
<follow-button
:creator="creator"
:color-border="creator.colors.menu">
</follow-button>
<subscribe-button
:creator="creator"
:color-border="creator.colors.menu">
</subscribe-button>
<follow-button></follow-button>
<subscribe-button></subscribe-button>
</div>
</div>
</div>
<div class="absolute bottom-6 right-24 z-30 shadow-2xl rounded-md text-white"
:style="{ backgroundColor: creator.colors.bannerTop}">
:style="{ backgroundColor: brandingStore.value.colors.background}">
<div class="w-96 h-28 flex flex-col">
<!-- Section 3 et 4 - Prend 2/3 de la hauteur -->
<div class="flex flex-row flex-grow-[2] min-h-20">
<div class="rounded-tl-md w-1/2 flex items-center justify-center"
:style="{ backgroundColor: creator.colors.bannerBottom, opacity: 0.20 }">
:style="{ backgroundColor: brandingStore.value.colors.primary, opacity: 0.20 }">
</div>
<div class="rounded-tr-md w-1/2 bg-cyan-100 flex items-center justify-center text-xl"
:style="{ backgroundColor: creator.colors.bannerBottom}">
:style="{ backgroundColor: brandingStore.value.colors.secondary}">
<div class="absolute left-1">
<div class="flex flex-row items-center justify-center space-x-5">
<div class="flex flex-row items-center">
<div style="display: flex; align-items: center;">
<textarea class="text-3xl" rows="1" cols="6" style="border: none; resize: none; text-align: right; outline: none;" placeholder="0"></textarea>
<textarea class="text-3xl" rows="1" cols="6"
style="border: none; resize: none; text-align: right; outline: none;"
placeholder="0"></textarea>
<div class="px-1">$</div>
</div>
@@ -86,11 +81,11 @@
<div class="flex flex-col items-center space-y-2">
<v-btn
:style="{ backgroundColor: creator.colors.bannerBottom, fontSize: '20px', height: '30px', width: '30px', padding: '0', minWidth: '25px', minHeight: '25px' }"
:style="{ backgroundColor: brandingStore.value.colors.secondary, fontSize: '20px', height: '30px', width: '30px', padding: '0', minWidth: '25px', minHeight: '25px' }"
variant="tonal">+
</v-btn>
<v-btn
:style="{ backgroundColor: creator.colors.bannerBottom, fontSize: '20px', height: '30px', width: '30px', padding: '0', minWidth: '25px', minHeight: '25px' }"
:style="{ backgroundColor: brandingStore.value.colors.secondary, fontSize: '20px', height: '30px', width: '30px', padding: '0', minWidth: '25px', minHeight: '25px' }"
variant="tonal">-
</v-btn>
</div>
@@ -107,15 +102,15 @@
</div>
<div class="flex-grow bg-gray-300 flex items-center justify-center rounded-b-md"
:style="{ backgroundColor: creator.colors.bannerBottom,opacity: 0.80 }">
:style="{ backgroundColor: brandingStore.value.colors.secondary, opacity: 0.80 }">
<textarea
rows="1"
placeholder="Message facultatif"
class="w-full p-2 border border-gray-300 rounded-b-md resize-none"
style="max-height: 300px; overflow-y: hidden; outline: none;"
oninput="this.style.height = ''; this.style.height = Math.min(this.scrollHeight, 300) + 'px'"></textarea>
oninput="this.style.height = ''; this.style.height = Math.min(this.scrollHeight, 300) + 'px'"
></textarea>
</div>
</div>
@@ -124,12 +119,21 @@
</div>
</div>
<div class="rounded-b-2xl min-h-10 px-36 flex flex-col items-center justify-center"
:style="{ backgroundColor: creator.colors.bannerBottom, boxShadow: '0 5px 20px rgba(0, 0, 0, 0.3)' }">
<div class="rounded-b-2xl h-12 px-36 flex flex-col items-center justify-center"
:style="{ backgroundColor: brandingStore.value.colors.secondary, boxShadow: '0 5px 20px rgba(0, 0, 0, 0.3)' }">
<div class="flex justify-evenly w-full">
<v-btn variant="text" class="text-white">Présentation</v-btn>
<v-btn variant="text" class="text-white">Actualité</v-btn>
<v-btn variant="text" class="text-white">Exclusivité</v-btn>
<RouterLink class="nav-button"
:to="`/@${brandingStore.value.name}`">
Présentation
</RouterLink>
<RouterLink class="nav-button text-white hover:bg-gray-700"
:to="`/@${brandingStore.value.name}/news`">
Actualité
</RouterLink>
<RouterLink class="nav-button text-white hover:bg-gray-700"
:to="`/@${brandingStore.value.name}/content`">
Exclusivité
</RouterLink>
</div>
</div>
@@ -137,82 +141,81 @@
</template>
<script setup>
import {ref, onMounted, computed} from 'vue';
import {useSubscriptionStore} from "@/stores/subscriptionStore.js";
import SubscribeButton from "@/views/creators/SubscribeButton.vue";
import DonationButtonBanner from "@/views/creators/DonationButtonBanner.vue";
import CreatorAbout from "@/views/creators/CreatorAbout.vue";
import SubscribeButtonSlim from "@/views/creators/SubscribeButtonSlim.vue";
import DonationButtonBannerSlim from "@/views/creators/DonationButtonBannerSlim.vue";
import FollowButton from "@/views/creators/FollowButton.vue";
<style scoped>
.nav-button {
@apply rounded flex justify-center font-sans py-1 text-white tracking-widest p-4
}
.nav-button:hover {
@apply bg-purple-800;
}
</style>
<script setup>
import {ref, onMounted} from 'vue';
import SubscribeButton from "@/views/creators/SubscribeButton.vue";
import FollowButton from "@/views/creators/FollowButton.vue";
import {useBrandingStore} from "@/stores/brandingStore.js";
const brandingStore = useBrandingStore()
function GetSocialsUrls() {
const socials = [];
if (props.creator.socials.facebookUrl !== null) {
if (brandingStore.value.socials.facebookUrl !== null) {
socials.push({
icon: "mdi-facebook",
url: props.creator.socials.facebookUrl
url: brandingStore.value.socials.facebookUrl
})
}
if (props.creator.socials.instagramUrl !== null) {
if (brandingStore.value.socials.instagramUrl !== null) {
socials.push({
icon: "mdi-instagram",
url: props.creator.socials.instagramUrl
url: brandingStore.value.socials.instagramUrl
})
}
if (props.creator.socials.xUrl !== null) {
if (brandingStore.value.socials.xUrl !== null) {
socials.push({
icon: "mdi-twitter",
url: props.creator.socials.xUrl
url: brandingStore.value.socials.xUrl
})
}
if (props.creator.socials.linkedInUrl !== null) {
if (brandingStore.value.socials.linkedInUrl !== null) {
socials.push({
icon: 'mdi-linkedin',
url: props.creator.socials.linkedInUrl
url: brandingStore.value.socials.linkedInUrl
})
}
if (props.creator.socials.tikTokUrl !== null) {
if (brandingStore.value.socials.tikTokUrl !== null) {
socials.push({
icon: '/images/socials/tiktok-white.png',
url: props.creator.socials.tikTokUrl
url: brandingStore.value.socials.tikTokUrl
})
}
if (props.creator.socials.youtubeUrl !== null) {
if (brandingStore.value.socials.youtubeUrl !== null) {
socials.push({
icon: 'mdi-youtube',
url: props.creator.socials.youtubeUrl
url: brandingStore.value.socials.youtubeUrl
})
}
if (props.creator.socials.redditUrl !== null) {
if (brandingStore.value.socials.redditUrl !== null) {
socials.push({
icon: 'mdi-reddit',
url: props.creator.socials.redditUrl
url: brandingStore.value.socials.redditUrl
})
}
if (props.creator.socials.websiteUrl !== null) {
if (brandingStore.value.socials.websiteUrl !== null) {
socials.push({
icon: 'mdi-web',
url: props.creator.socials.websiteUrl
url: brandingStore.value.socials.websiteUrl
})
}
return socials;
}
const props = defineProps({
creator: {type: Object, required: true}
});
const subscriptionStore = useSubscriptionStore();
// Calculer si l'utilisateur est abonné
const isSubscribed = computed(() => subscriptionStore.isSubscribeTo(props.creator.id));
const isSticky = ref(false);
const mainContainer = ref(null);

View File

@@ -128,14 +128,8 @@
dHutopy votre utopie. Merci de faire partie de notre histoire.
</p>
</div>
<selected-footer></selected-footer>
</template>
<style>
@import '@/cssstyle/tosstyle.css';
</style>
<script setup lang="ts">
import SelectedFooter from "@/views/main/SelectedFooter.vue";
</script>

View File

@@ -123,14 +123,8 @@
Si vous avez des questions sur cette politique de contenu ou sur la manière dont nous l'appliquons, veuillez contacter notre équipe d'assistance à <a href="mailto:support@hutopy.com">support@hutopy.com</a>.
</p>
</div>
<selected-footer></selected-footer>
</template>
<style>
@import '@/cssstyle/tosstyle.css';
</style>
<script setup lang="ts">
import SelectedFooter from "@/views/main/SelectedFooter.vue";
</script>

View File

@@ -38,14 +38,8 @@
<li class="text-justify p-tos">Continuez à Apprendre : Utilisez le Centre de Ressources Éducatives dHutopy pour améliorer vos compétences et rester à jour sur les tendances du secteur. (À venir)</li>
</ul>
</div>
<selected-footer></selected-footer>
</template>
<style>
@import '@/cssstyle/tosstyle.css';
</style>
<script setup lang="ts">
import SelectedFooter from "@/views/main/SelectedFooter.vue";
</script>

View File

@@ -80,8 +80,6 @@
</p>
</div>
<selected-footer></selected-footer>
</template>
<style>
@@ -91,6 +89,3 @@
@apply m-2 text-red-500 my-4;
}
</style>
<script setup lang="ts">
import SelectedFooter from "@/views/main/SelectedFooter.vue";
</script>

View File

@@ -34,14 +34,8 @@
</p>
</div>
<selected-footer></selected-footer>
</template>
<style>
@import '@/cssstyle/tosstyle.css';
</style>
<script setup lang="ts">
import SelectedFooter from "@/views/main/SelectedFooter.vue";
</script>

View File

@@ -21,14 +21,9 @@
Hutopy est plus qu'une plateforme ; c'est une communauté où la transformation de la passion en profit devient réalité, grâce au soutien indéfectible d'une équipe dévouée à enrichir votre parcours. Nous vous invitons à nous rejoindre pour explorer ensemble les avenues de succès, tout en vous garantissant une part conséquente de vos revenus. Embarquez dans une aventure votre présence en ligne ne connaît pas de limites, soutenue par Hutopy, votre allié dans la quête du succès.
</p>
</div>
<selected-footer></selected-footer>
</template>
<style>
@import '@/cssstyle/tosstyle.css';
</style>
<script setup lang="ts">
import SelectedFooter from "@/views/main/SelectedFooter.vue";
</script>

View File

@@ -63,13 +63,8 @@
</p>
</div>
<selected-footer></selected-footer>
</template>
<style>
@import '@/cssstyle/tosstyle.css';
</style>
<script setup lang="ts">
import SelectedFooter from "@/views/main/SelectedFooter.vue";
</script>

View File

@@ -1,9 +1,85 @@
<template>
<script setup>
import XIcon from '@/assets/icons/x.svg'
import FacebookIcon from '@/assets/icons/facebook.svg'
import InstagramIcon from '@/assets/icons/instagram.svg'
</script>
<footer>
<div class="p-4 text-center font-sans">
<template>
<footer class="py-8 flex flex-col gap-8">
<div class="flex justify-center">
<router-link to="/">
<img src="/images/hutopymedia/banners/hutopy.png" alt="hutopy"
width="300px"
height="64px">
</router-link>
</div>
<div class="flex flex-row justify-center gap-10">
<a href="https://www.facebook.com/profile.php?id=61556819217561">
<facebook-icon class="icon"></facebook-icon>
</a>
<a href="https://www.instagram.com/hutopy.inc/">
<instagram-icon class="icon"></instagram-icon>
</a>
<a href="https://x.com/Hutopyinc/">
<x-icon class="icon"></x-icon>
</a>
</div>
<div class="flex flex-row flex-wrap justify-center gap-4">
<router-link to="/helpandcontact">
Aide & Contact
</router-link>
<router-link to="/faq">
FAQ
</router-link>
<router-link to="/guideforcreators">
Guide pour les créateurs
</router-link>
<router-link to="/termsandconditions">
Termes et Conditions
</router-link>
<router-link to="/contentpolicy">
Politique de Contenu
</router-link>
<router-link to="/about">
À Propos
</router-link>
<router-link to="/pricing">
Frais
</router-link>
</div>
<div class="flex justify-center base-text">
Hutopy &copy;{{ new Date().getFullYear() }} - {{ $t('footer.allRightsReserved') }}
</div>
</footer>
</template>
<style scoped>
.icon {
width: 36px;
height: 36px;
fill: #6a0065;
}
.base-text {
@apply text-gray-600 tracking-widest font-sans text-sm uppercase
}
a {
@apply base-text
}
a:hover {
@apply text-gray-400
}
</style>

View File

@@ -97,10 +97,10 @@
<template v-slot:activator="{ props }">
<div v-bind="props" class="flex align-center font-sans py-1 px-2 rounded-lg hover:bg-gray-100">
<span class="max-w-xs hidden md:block capitalize">
{{ userStore.alias }}
{{ userProfileStore.alias }}
</span>
<img
:src="userStore.portraitUrl"
:src="userProfileStore.portraitUrl"
alt="Profile Image"
class="ml-2 rounded-full"
width="32"
@@ -119,13 +119,13 @@
</template>
<template v-else>
<v-list-item v-if="userStore.creator && Object.keys(userStore.creator).length > 0" class="nav-button">
<router-link :to="`/@${userStore.creator.name}`">
<v-btn class="w-100" variant="plain">@{{ userStore.creator.name }}</v-btn>
<v-list-item v-if="creatorProfileStore.value && Object.keys(creatorProfileStore.value).length > 0" class="nav-button">
<router-link :to="`/@${creatorProfileStore.value.name}`">
<v-btn class="w-100" variant="plain">@{{ creatorProfileStore.value.name }}</v-btn>
</router-link>
</v-list-item>
<v-list-item v-if="!userStore.hasCreator" class="nav-button">
<v-list-item v-if="!creatorProfileStore.hasCreator" class="nav-button">
<router-link to="/profile?target=CreatorPage">
<v-btn class="w-100" variant="plain">Activer votre page</v-btn>
</router-link>
@@ -158,12 +158,16 @@
import { ref, onBeforeUnmount, onBeforeMount, watch } from "vue";
import { useRouter } from 'vue-router';
import { useSideBarStore } from '@/stores/sideBarStore.js';
import { useUserStore } from "@/stores/userStore.js";
import { useAuthStore } from "@/stores/authStore.js";
import { useDisplay } from 'vuetify';
import {useUserProfileStore} from "@/stores/userProfileStore.js";
import {useCreatorProfileStore} from "@/stores/creatorProfileStore.js";
const authStore = useAuthStore();
const userStore = useUserStore();
const userProfileStore = useUserProfileStore();
const creatorProfileStore = useCreatorProfileStore();
const sideBarStore = useSideBarStore();
const { smAndDown } = useDisplay();

View File

@@ -8,34 +8,44 @@
</div>
<div class="mx-auto flex justify-center pt-10 max-w-[980px]">
<img src="/images/hutopymedia/homepage/bannierehomepage.png" alt="Create CallToAction" class="max-w-full block rounded-none md:rounded-2xl">
<img src="/images/hutopymedia/homepage/bannierehomepage.png" alt="Create CallToAction"
class="max-w-full block rounded-none md:rounded-2xl">
</div>
<div>
<div class="mx-auto flex flex-col md:flex-row justify-center max-w-[1000px] space-y-2 md:space-x-4 md:space-y-0 py-5">
<div
class="mx-auto flex flex-col md:flex-row justify-center max-w-[1000px] space-y-2 md:space-x-4 md:space-y-0 py-5">
<div class="relative group w-full max-w-[250px] md:max-w-[306px] rounded-2xl overflow-hidden mx-auto">
<img src="/images/hutopymedia/homepage/creer.png" alt="Create CallToAction" class="w-full rounded-2xl">
<div class="absolute inset-0 bg-fuchsia-600 bg-opacity-0 group-hover:bg-opacity-80 flex items-center justify-center transition duration-300">
<div
class="absolute inset-0 bg-fuchsia-600 bg-opacity-0 group-hover:bg-opacity-80 flex items-center justify-center transition duration-300">
<p class="text-white text-lg opacity-0 group-hover:opacity-100 transition duration-300 m-3 text-justify">
Libérez votre créativité sur Hutopy, chaque idée trouve sa place et chaque créateur détient la clé d'un monde rempli de possibilités infinies. Rejoignez-nous et transformez votre passion en réalité.
Libérez votre créativité sur Hutopy, chaque idée trouve sa place et chaque créateur détient la clé d'un
monde rempli de possibilités infinies. Rejoignez-nous et transformez votre passion en réalité.
</p>
</div>
</div>
<div class="relative group w-full max-w-[250px] md:max-w-[306px] rounded-2xl overflow-hidden mx-auto">
<img src="/images/hutopymedia/homepage/partager.png" alt="Share CallToAction" class="w-full rounded-2xl">
<div class="absolute inset-0 bg-fuchsia-600 bg-opacity-0 group-hover:bg-opacity-80 flex items-center justify-center transition duration-300">
<div
class="absolute inset-0 bg-fuchsia-600 bg-opacity-0 group-hover:bg-opacity-80 flex items-center justify-center transition duration-300">
<p class="text-white text-lg opacity-0 group-hover:opacity-100 transition duration-300 m-3 text-justify">
Plongez dans l'univers Hutopy et découvrez un espace profiter rime avec s'enrichir. Savourez des contenus uniques, des interactions authentiques et une expérience personnalisée conçue pour éveiller vos sens et enrichir votre quotidien.
Plongez dans l'univers Hutopy et découvrez un espace profiter rime avec s'enrichir. Savourez des contenus
uniques, des interactions authentiques et une expérience personnalisée conçue pour éveiller vos sens et
enrichir votre quotidien.
</p>
</div>
</div>
<div class="relative group w-full max-w-[250px] md:max-w-[306px] rounded-2xl overflow-hidden mx-auto">
<img src="/images/hutopymedia/homepage/inspirer.png" alt="Inspire CallToAction" class="w-full rounded-2xl">
<div class="absolute inset-0 bg-fuchsia-600 bg-opacity-0 group-hover:bg-opacity-80 flex items-center justify-center transition duration-300">
<div
class="absolute inset-0 bg-fuchsia-600 bg-opacity-0 group-hover:bg-opacity-80 flex items-center justify-center transition duration-300">
<p class="text-white text-lg opacity-0 group-hover:opacity-100 transition duration-300 m-3 text-justify">
Devenez une source d'inspiration sur Hutopy, en partageant votre vision, votre talent et vos histoires. Influencez positivement la communauté, éveillez la curiosité et inspirez les autres à poursuivre leurs rêves dans un cercle vertueux de créativité et d'inspiration.
Devenez une source d'inspiration sur Hutopy, en partageant votre vision, votre talent et vos histoires.
Influencez positivement la communauté, éveillez la curiosité et inspirez les autres à poursuivre leurs rêves
dans un cercle vertueux de créativité et d'inspiration.
</p>
</div>
</div>
@@ -84,17 +94,20 @@
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="flex justify-center items-center">
<RouterLink to="/@Hutopy">
<img src="/images/usersmedia/HutopyProfile/profilepictures/profileHutopyProfile02.png" alt="Profile Image" class="rounded-2xl shadow-lg">
<img src="/images/usersmedia/HutopyProfile/profilepictures/profileHutopyProfile02.png" alt="Profile Image"
class="rounded-2xl shadow-lg">
</RouterLink>
</div>
<div class="flex justify-center items-center">
<RouterLink to="/@guillaumem">
<img src="/images/usersmedia/guillaumeMousseau/profilepictures/profileGuillaumeMousseau01.jpg" alt="Profile Image" class="rounded-2xl shadow-lg">
<img src="/images/usersmedia/guillaumeMousseau/profilepictures/profileGuillaumeMousseau01.jpg"
alt="Profile Image" class="rounded-2xl shadow-lg">
</RouterLink>
</div>
<div class="flex justify-center items-center">
<RouterLink to="/@chloebeaugrand">
<img src="/images/usersmedia/chloebeaugrand/profilepictures/profileChloeBeaugrand02.png" alt="Profile Image" class="rounded-2xl shadow-lg">
<img src="/images/usersmedia/chloebeaugrand/profilepictures/profileChloeBeaugrand02.png"
alt="Profile Image" class="rounded-2xl shadow-lg">
</RouterLink>
</div>
</div>
@@ -111,17 +124,10 @@
</div>
</div>
<selected-footer></selected-footer>
</div>
</div>
</template>
<script setup>
import SelectedFooter from "@/views/main/SelectedFooter.vue";
</script>
<style scoped>
.overlay p {

View File

@@ -16,11 +16,19 @@
Utilisateur
</v-btn>
<div v-if="showEmailForm" class="w-full mt-2">
<v-text-field v-model="email" label="Courriel" variant="outlined" dense prepend-inner-icon="mdi-email" color="transparent" class="text-black"></v-text-field>
<v-text-field v-model="password" label="Mot de passe" :type="showPassword ? 'text' : 'password'" variant="outlined" dense prepend-inner-icon="mdi-lock" append-inner-icon="mdi-eye" @click:append-inner="showPassword = !showPassword" color="transparent" class="text-black"></v-text-field>
<v-btn class="w-full text-center text-white" :style="{ backgroundColor: '#A30E79' }" @click="login">Connecter</v-btn>
<p class="mt-4 text-sm text-center">Si vous n'avez pas de compte, <a href="/register" class="text-blue-500">cliquez ici</a> pour en créer un.</p>
<div v-if="errorSnackBar" class="mb-4 text-red-600">Nom d'utilisateur ou mot de passe invalide. <button class="text-red-600 ml-4" @click="errorSnackBar = false">Fermer</button></div>
<v-text-field v-model="email" label="Courriel" variant="outlined" dense prepend-inner-icon="mdi-email"
color="transparent" class="text-black"></v-text-field>
<v-text-field v-model="password" label="Mot de passe" :type="showPassword ? 'text' : 'password'"
variant="outlined" dense prepend-inner-icon="mdi-lock" append-inner-icon="mdi-eye"
@click:append-inner="showPassword = !showPassword" color="transparent"
class="text-black"></v-text-field>
<v-btn class="w-full text-center text-white" :style="{ backgroundColor: '#A30E79' }" @click="login">Connecter
</v-btn>
<p class="mt-4 text-sm text-center">Si vous n'avez pas de compte, <a href="/register" class="text-blue-500">cliquez
ici</a> pour en créer un.</p>
<div v-if="errorSnackBar" class="mb-4 text-red-600">Nom d'utilisateur ou mot de passe invalide.
<button class="text-red-600 ml-4" @click="errorSnackBar = false">Fermer</button>
</div>
</div>
</div>
</div>
@@ -40,7 +48,6 @@ const password = ref("");
const errorSnackBar = ref(false);
const showEmailForm = ref(false);
const showPassword = ref(false);
const googleCallback = ref('');
const props = defineProps({
onSuccess: {
@@ -53,9 +60,18 @@ const props = defineProps({
}
});
async function googleCallback(token) {
const response = await authStore.loginWithGoogle(JSON.stringify(token));
handleResponse(response)
}
async function login() {
const result = await authStore.login(email.value, password.value);
if (result === true) {
const response = await authStore.login(email.value, password.value);
handleResponse(response)
}
function handleResponse(response) {
if (response === true) {
props.onSuccess();
} else {
if (props.onFailure) {

View File

@@ -10,14 +10,10 @@
</div>
</div>
</div>
<selected-footer></selected-footer>
</template>
<script setup>
import SelectedFooter from "@/views/main/SelectedFooter.vue";
import RegisterForm from "@/views/main/RegisterForm.vue";
import { useRouter } from 'vue-router';
const router = useRouter();
</script>

View File

@@ -1,55 +0,0 @@
<template>
<div class="flex justify-center items-center max-w-[300px] pt-28 mx-auto">
<router-link to="/">
<img src="/images/hutopymedia/banners/hutopy.png" alt="hutopy">
</router-link>
</div>
<div class="flex flex-row justify-center space-x-10 py-10">
<a href="https://www.facebook.com/profile.php?id=61556819217561">
<v-icon size="40px" class="text-fuchsia-900">mdi-facebook</v-icon>
</a>
<a href="https://www.instagram.com/hutopy.inc/">
<v-icon size="40px" class="text-fuchsia-900">mdi-instagram</v-icon>
</a>
<a href="https://x.com/Hutopyinc/">
<img src="/images/hutopymedia/icons/x.svg" width="34px" height="34px" class="mt-1 filter-fushia ">
</a>
</div>
<div class="flex flex-row flex-wrap justify-center space-x-2 py-2 pb-6">
<router-link to="/helpandcontact">
<v-btn variant="plain"> Aide & Contact</v-btn>
</router-link>
<router-link to="/faq">
<v-btn variant="plain"> FAQ</v-btn>
</router-link>
<router-link to="/guideforcreators">
<v-btn variant="plain"> Guide pour les créateurs</v-btn>
</router-link>
<router-link to="/termsandconditions">
<v-btn variant="plain">Termes et Conditions </v-btn>
</router-link>
<router-link to="/contentpolicy">
<v-btn variant="plain"> Politique de Contenu </v-btn>
</router-link>
<router-link to="/about">
<v-btn variant="plain"> À Propos</v-btn>
</router-link>
<router-link to="/pricing">
<v-btn variant="plain"> Frais</v-btn>
</router-link>
</div>
</template>
<script setup lang="ts">
</script>
<style>
.filter-fushia{
filter: invert(14%) sepia(60%) saturate(4103%) hue-rotate(285deg) brightness(84%) contrast(93%);
}
</style>

View File

@@ -1,5 +1,4 @@
<script setup>
import SiteMenu from "@/views/main/SiteMenu.vue";
import SubscriptionList from "@/views/creators/SubscriptionList.vue";
import {useAuthStore} from "@/stores/authStore.js";
import {useRouter} from 'vue-router';
@@ -37,9 +36,7 @@ initializeLocale();
</script>
<template>
<nav class="flex flex-col h-full overflow-y-auto custom-scrollbar">
<div class="mt-16"></div>
<nav class="flex flex-col h-full overflow-y-auto custom-scrollbar bg-white">
<div class="flex justify-center py-3">
<v-btn v-if="authStore.isAuthenticated" variant="plain" class="p-8" @click="feedHandler">
@@ -66,9 +63,6 @@ initializeLocale();
</div>
<subscription-list>
<template v-slot:default>
<span v-if="subscriptionListIsEmpty">Aucun abonnement</span>
</template>
</subscription-list>
</div>
@@ -76,10 +70,6 @@ initializeLocale();
<span>Connectez-vous</span>
</div>
<div class="border-t w-full py-10 mt-auto">
<SiteMenu></SiteMenu>
</div>
</nav>
</template>

View File

@@ -1,44 +0,0 @@
<template>
<div class="flex flex-col">
<div class="flex flex-row justify-center pb-4 pt-2 py-4">
<!-- Facebook -->
<a href="https://www.facebook.com/profile.php?id=61556819217561" class="social">
<v-icon>mdi-facebook</v-icon>
</a>
<!-- Instagram -->
<a href="https://www.instagram.com/hutopy.inc/" class="social">
<v-icon>mdi-instagram</v-icon>
</a>
<!-- X / Twitter -->
<a href="https://twitter.com/Hutopyinc" class="social">
<img src="/images/hutopymedia/icons/x.svg" width="23px" height="23px" class="mb-5 mr-2">
</a>
</div>
<div class="text-center ">
<RouterLink class="nav-button" to="/helpandcontact">{{ $t('sitemenu.HelpAndContact') }}</RouterLink>
<RouterLink class="nav-button" to="/faq">{{ $t('sitemenu.FAQ') }}</RouterLink>
<RouterLink class="nav-button" to="/guideforcreators">{{ $t('sitemenu.CreatorGuide') }}</RouterLink>
<RouterLink class="nav-button" to="/termsandconditions">{{ $t('sitemenu.TermsAndConditions') }}</RouterLink>
<RouterLink class="nav-button" to="/contentpolicy">{{ $t('sitemenu.ContentPolicy') }}</RouterLink>
<RouterLink class="nav-button" to="/about">{{ $t('sitemenu.About') }}</RouterLink>
<RouterLink class="nav-button" to="/pricing">{{ $t('sitemenu.Pricing') }}</RouterLink>
</div>
</div>
</template>
<style scoped>
.nav-button {
@apply rounded flex justify-center font-sans py-1;
}
.nav-button:hover {
@apply text-purple-800 bg-gray-50;
}
.social {
@apply m-2 w-10 h-10;
}
</style>

View File

@@ -47,9 +47,9 @@
<script async setup>
import { onBeforeMount, ref, computed } from 'vue';
import { useRouter } from 'vue-router';
import {useUserStore} from "@/stores/userStore.js";
import {useUserProfileStore} from "@/stores/userProfileStore.js";
const userStore = useUserStore();
const userProfileStore = useUserProfileStore();
const router = useRouter();
const userTransactions = ref([]);
@@ -72,8 +72,8 @@ const transactionCount = computed(() => userTransactions.value.length);
onBeforeMount( () => {
try {
userTransactions.value = userStore.user.userTransactions;
totalBalance.value = userStore.user.totalBalance;
userTransactions.value = userProfileStore.value.userTransactions;
totalBalance.value = userProfileStore.value.totalBalance;
} catch (error) {
navigateToHome();
}

View File

@@ -6,10 +6,11 @@
class="justify-items-center"
>
<template v-for="message in messages" :key="message">
<div class="border-b">
<message :message="message"
@message-deleted="(messageId) => handleDeleteMessage(messageId)"
class="border-b"
></message>
</div>
</template>
<template v-slot:load-more="{ props }">

View File

@@ -1,7 +1,7 @@
<template>
<div class="flex flex-column">
<div class="flex flex-row items-center ">
<img :src="userStore.portraitUrl" alt="Profile Image" class="rounded-full mr-2" width="32px" height="32px">
<img :src="userProfileStore.portraitUrl" alt="Profile Image" class="rounded-full mr-2" width="32px" height="32px">
<div class="flex-grow">
<div class="flex flex-row bg-gray-100 rounded-2xl">
<v-textarea
@@ -36,14 +36,17 @@
</div>
</div>
<must-be-logged v-model="loginModal" message="Vous devez être connecté pour ajouter un commentaire."></must-be-logged>
<must-be-logged v-model="loginModal"
message="Vous devez être connecté pour ajouter un commentaire."
></must-be-logged>
</template>
<script setup>
import {ref} from 'vue'
import {v7} from 'uuid'
import {useClient} from '@/plugins/api.js'
import {useUserStore} from "@/stores/userStore.js"
import {useUserProfileStore} from "@/stores/userProfileStore.js"
import {useAuthStore} from "@/stores/authStore.js"
import MustBeLogged from "@/views/MustBeLogged.vue";
@@ -59,7 +62,7 @@ const emits = defineEmits(['message-posted'])
const loginModal = ref(false);
const client = useClient()
const value = ref("")
const userStore = useUserStore()
const userProfileStore = useUserProfileStore()
const authStore = useAuthStore()
const publish = async () => {
@@ -76,9 +79,9 @@ const publish = async () => {
emits('message-posted', {
"id": messageId,
"subjectId": props.subjectId,
"createdBy": userStore.user.id,
"createdByName": userStore.alias,
"createdByPortraitUrl": userStore.portraitUrl,
"createdBy": userProfileStore.value.id,
"createdByName": userProfileStore.alias,
"createdByPortraitUrl": userProfileStore.portraitUrl,
"createdAt": new Date(Date.now()).toISOString(),
"value": value.value,
"parentId": null

122
src/views/news/NewsList.vue Normal file
View File

@@ -0,0 +1,122 @@
<template>
<div>
<v-infinite-scroll :items="contents"
:onLoad="fetchContents">
<!-- TODO: the -mt-4 is necessary because the v-infinite-scroll has some 'top' panel offsetting the list -->
<div class="flex flex-column gap-4 -mt-4">
<template v-for="content in contents" :key="content.id">
<component
:is="isSmallScreen ? ContentCardSm : ContentCardNormal"
:content="content"
@content-deleted="onContentDeleted"
></component>
</template>
</div>
<template v-slot:empty>
Il n'y a pas plus de contenus
</template>
<template v-slot:error>
<v-alert type="error">{{ errorMessage }}</v-alert>
</template>
</v-infinite-scroll>
</div>
</template>
<style>
</style>
<script setup>
import {useClient} from '@/plugins/api.js';
import {onBeforeUnmount, onMounted, ref, watch} from 'vue';
import ContentCardNormal from "@/views/contents/contentcards/NContentCard.vue";
import ContentCardSm from "@/views/contents/contentcards/SmContentCard.vue";
const props = defineProps({
creatorId: {
type: String,
required: true
},
news: {
type: Array,
default: () => [],
},
});
const client = useClient()
const contents = ref(props.news)
const errorMessage = ref()
let last_id = null
const isSmallScreen = ref(false);
const updateScreenSize = () => {
isSmallScreen.value = window.matchMedia('(max-width: 600px)').matches;
};
onMounted(() => {
updateScreenSize();
window.addEventListener('resize', updateScreenSize);
});
onBeforeUnmount(() => {
window.removeEventListener('resize', updateScreenSize);
});
async function onContentDeleted(contentId) {
contents.value = contents.value.filter(c => c.id !== contentId)
}
const creatorIdWatcher = watch(
() => props.creatorId,
(newCreatorId) => {
if (newCreatorId) {
contents.value = []
last_id = null
fetchContents({
done: () => {
}
});
}
})
async function fetchContents({done, page_size = 10}) {
if (props.creatorId == null) return
try {
let uri = `/api/contents/creator/${props.creatorId}?page_size=${page_size}`
if (last_id !== null) uri = uri + `&last_id=${last_id}`
const response = await client.get(uri)
if (response.status >= 200 && response.status < 300) {
const contentCount = response.data.length
if (contentCount > 0) {
contents.value.push(...response.data)
const [last_content] = response.data.slice(-1)
last_id = last_content.id
}
if (contentCount < page_size)
done('empty')
else
done('ok')
}
} catch (error) {
console.error("Failed to fetch posts", error);
errorMessage.value = error.message || "Failed to fetch contents";
done('error')
}
}
</script>

View File

@@ -18,7 +18,7 @@
<span class="value">Un portrait vous permet de personnaliser votre profil</span>
<span>
<img
:src="userStore.user.portraitUrl"
:src="userProfileStore.value.portraitUrl"
alt="Profile Image"
class="rounded-full"
width="48px"
@@ -30,7 +30,7 @@
class="editableValue"
@click="openEditFullname">
<span class="label">{{ $t('personnalinformation.fullname') }}</span>
<span class="value">{{ userStore.fullname }}</span>
<span class="value">{{ userProfileStore.fullname }}</span>
<span><v-icon>mdi-chevron-right</v-icon></span>
</button>
@@ -38,7 +38,7 @@
class="editableValue"
@click="openEditAlias">
<span class="label">{{ $t('personnalinformation.alias') }}</span>
<span class="value">{{ userStore.user.alias }}</span>
<span class="value">{{ userProfileStore.value.alias }}</span>
<span><v-icon>mdi-chevron-right</v-icon></span>
</button>
@@ -46,7 +46,7 @@
class="editableValue"
@click="openEditBirthday">
<span class="label">{{ $t('personnalinformation.dob') }}</span>
<span class="value">{{ userStore.user.birthDate }}</span>
<span class="value">{{ userProfileStore.value.birthDate }}</span>
<span><v-icon>mdi-chevron-right</v-icon></span>
</button>
@@ -62,7 +62,7 @@
class="editableValue"
@click="openEditEmail">
<span class="label">{{ $t('personnalinformation.email') }}</span>
<span class="value">{{ userStore.user.email }}</span>
<span class="value">{{ userProfileStore.value.email }}</span>
<span><v-icon>mdi-chevron-right</v-icon></span>
</button>
@@ -70,7 +70,7 @@
class="editableValue"
@click="openEditPhone">
<span class="label">{{ $t('personnalinformation.phone') }}</span>
<span class="value">{{ userStore.user.phoneNumber }}</span>
<span class="value">{{ userProfileStore.value.phoneNumber }}</span>
<span><v-icon>mdi-chevron-right</v-icon></span>
</button>
</v-card>
@@ -85,7 +85,7 @@
class="editableValue"
@click="openEditAddress">
<span class="label">{{ $t('personnalinformation.home') }}</span>
<span class="value">{{ userStore.user.address }}</span>
<span class="value">{{ userProfileStore.value.address }}</span>
<span><v-icon>mdi-chevron-right</v-icon></span>
</button>
</v-card>
@@ -94,7 +94,7 @@
<!-- Modal -->
<v-dialog v-model="dialogEditPortraitShown" max-width="600px">
<portrait-dialog
:portrait-url="userStore.user.portraitUrl"
:portrait-url="userProfileStore.value.portraitUrl"
@close="handleCloseEditPortrait"
@save="handleSaveEditPortrait"
></portrait-dialog>
@@ -102,8 +102,8 @@
<v-dialog v-model="dialogEditFullnameShown" max-width="600px">
<fullname-dialog
:firstname="userStore.user.firstname"
:lastname="userStore.user.lastname"
:firstname="userProfileStore.value.firstname"
:lastname="userProfileStore.value.lastname"
@close="handleCloseEditFullname"
@save="handleSaveEditFullname"
></fullname-dialog>
@@ -111,7 +111,7 @@
<v-dialog v-model="dialogEditAliasShown" max-width="600px">
<alias-dialog
:alias="userStore.user.alias"
:alias="userProfileStore.value.alias"
@close="handleCloseEditAlias"
@save="handleSaveEditAlias"
></alias-dialog>
@@ -119,7 +119,7 @@
<v-dialog v-model="dialogEditBirthdayShown" max-width="600px">
<birthday-dialog
:birth-date="userStore.user.birthDate"
:birth-date="userProfileStore.value.birthDate"
@close="handleCloseEditBirthday"
@save="handleSaveEditBirthday"
></birthday-dialog>
@@ -127,7 +127,7 @@
<v-dialog v-model="dialogEditPhoneShown" max-width="600px">
<phone-dialog
:phone="userStore.user.phoneNumber"
:phone="userProfileStore.value.phoneNumber"
@close="handleCloseEditPhone"
@save="handleSaveEditPhone"
></phone-dialog>
@@ -135,7 +135,7 @@
<v-dialog v-model="dialogEditEmailShown" max-width="600px">
<email-dialog
:email="userStore.user.email"
:email="userProfileStore.value.email"
@close="handleCloseEditEmail"
@save="handleSaveEditEmail"
></email-dialog>
@@ -143,7 +143,7 @@
<v-dialog v-model="dialogEditAddressShown" max-width="600px">
<address-dialog
:address="userStore.user.address"
:address="userProfileStore.value.address"
@close="handleCloseEditAddress"
@save="handleSaveEditAddress"
></address-dialog>
@@ -159,9 +159,10 @@ import BirthdayDialog from "@/views/profile/account/BirthdayDialog.vue";
import AliasDialog from "@/views/profile/account/AliasDialog.vue";
import FullnameDialog from "@/views/profile/account/FullnameDialog.vue";
import PortraitDialog from "@/views/profile/account/PortraitDialog.vue";
import {useUserStore} from "@/stores/userStore.js";
import {useUserProfileStore} from "@/stores/userProfileStore.js";
const userProfileStore = useUserProfileStore()
const userStore = useUserStore()
// ### Portrait
@@ -176,7 +177,7 @@ function handleCloseEditPortrait() {
}
function handleSaveEditPortrait(portraitData) {
userStore.changePortrait(portraitData)
userProfileStore.changePortrait(portraitData)
dialogEditPortraitShown.value = false
}
@@ -193,7 +194,7 @@ function handleCloseEditFullname() {
}
function handleSaveEditFullname(firstname, lastname) {
userStore.changeFullname(firstname, lastname)
userProfileStore.changeFullname(firstname, lastname)
dialogEditFullnameShown.value = false
}
@@ -210,7 +211,7 @@ function handleCloseEditAlias() {
}
function handleSaveEditAlias(alias) {
userStore.changeAlias(alias)
userProfileStore.changeAlias(alias)
dialogEditAliasShown.value = false
}
@@ -227,7 +228,7 @@ function handleCloseEditBirthday() {
}
function handleSaveEditBirthday(birthday) {
userStore.changeBirthday(birthday)
userProfileStore.changeBirthday(birthday)
dialogEditBirthdayShown.value = false
}
@@ -244,7 +245,7 @@ function handleCloseEditPhone() {
}
function handleSaveEditPhone(phone) {
userStore.changePhone(phone)
userProfileStore.changePhone(phone)
dialogEditPhoneShown.value = false
}
@@ -261,7 +262,7 @@ function handleCloseEditEmail() {
}
function handleSaveEditEmail(email) {
userStore.changeEmail(email)
userProfileStore.changeEmail(email)
dialogEditEmailShown.value = false
}
@@ -277,7 +278,7 @@ function handleCloseEditAddress() {
}
function handleSaveEditAddress(address) {
userStore.changeAddress(address)
userProfileStore.changeAddress(address)
dialogEditAddressShown.value = false
}
</script>

View File

@@ -1,67 +0,0 @@
<script setup>
import {ref} from 'vue'
import {useClient} from "@/plugins/api.js";
const props = defineProps({
creator: {
required: true
}
})
const emits = defineEmits(['closeRequested'])
const title = ref(props.creator.about.title)
const description = ref(props.creator.about.description)
const client = useClient()
const save = async () => {
try {
await client.post(
`/api/creators/${props.creator.id}/about`,
{
"title": title.value || null,
"description": description.value || null
})
props.creator.about.title = title
props.creator.about.description = description
emits('closeRequested')
} catch (error) {
console.error(error)
}
}
const cancel = () => {
emits('closeRequested')
}
</script>
<template>
<div class="pb-5 text-2xl">About</div>
<div class="flex flex-row align-center">
<v-text-field
variant="outlined"
v-model="title"
label="Titre"
outlined
></v-text-field>
</div>
<div class="flex flex-row align-center">
<v-text-field
variant="outlined"
v-model="description"
label="Description"
outlined
></v-text-field>
</div>
<div class="flex justify-end space-x-4">
<v-btn color="black" variant="text" @click="cancel">Annuler</v-btn>
<v-btn color="#A6147D" @click="save">Enregistrer</v-btn>
</div>
</template>

View File

@@ -1,22 +1,23 @@
<script setup>
import XIcon from '@/assets/icons/x.svg'
import {computed, ref} from 'vue'
import {useUserStore} from "@/stores/userStore.js"
import Socials from './Socials.vue'
import BannerPicker from './BannerPicker.vue'
import ColorsPicker from './ColorsPicker.vue'
import LogoPicker from "./LogoPicker.vue"
import About from "./About.vue";
import CreateCreator from "./CreateCreator.vue";
import {useClient} from "@/plugins/api.js";
import {useCreatorProfileStore} from "@/stores/creatorProfileStore.js";
import {useUserProfileStore} from "@/stores/userProfileStore.js";
const userStore = useUserStore()
const creatorProfileStore = useCreatorProfileStore()
const colorBannerTop = computed(() => userStore.creator.colors.bannerTop || '#a0c0f0')
const colorBannerBottom = computed(() => userStore.creator.colors.bannerBottom || '#a0c0f0')
const colorAccent = computed(() => userStore.creator.colors.accent || '#a0c0f0')
const colorMenu = computed(() => userStore.creator.colors.menu || '#a0c0f0')
const imageBanner = computed(() => userStore.creator.images.banner || '/images/placeholders/banner.png')
const imageLogo = computed(() => userStore.creator.images.logo || '/images/placeholders/logo.png')
const colorBannerTop = computed(() => creatorProfileStore.value.colors.bannerTop || '#a0c0f0')
const colorBannerBottom = computed(() => creatorProfileStore.value.colors.bannerBottom || '#a0c0f0')
const colorAccent = computed(() => creatorProfileStore.value.colors.accent || '#a0c0f0')
const colorMenu = computed(() => creatorProfileStore.value.colors.menu || '#a0c0f0')
const imageBanner = computed(() => creatorProfileStore.value.images.banner || '/images/placeholders/banner.png')
const imageLogo = computed(() => creatorProfileStore.value.images.logo || '/images/placeholders/logo.png')
const dialog = ref(false);
const currentComponent = ref('')
@@ -27,20 +28,20 @@ const componentsMap = {
LogoPicker,
Socials,
ColorsPicker,
About,
CreateCreator
};
async function requestAccept(creatorName) {
const userProfileStore = useUserProfileStore()
const client = useClient()
const response = await client.post(
'/api/creators',
{
'creatorId': userStore.user.id,
'creatorId': userProfileStore.value.id,
'name': creatorName
})
if (response.status >= 200 && response.status < 300) {
await userStore.fetchCurrentCreatorProfile()
await creatorProfileStore.fetchCurrentCreatorProfile()
dialog.value = false
} else {
console.log(`An issue while creating the creator: ${response.statusText}`)
@@ -70,7 +71,7 @@ const closeDialog = () => {
<v-card :style="{ borderRadius: '25px', border: '3px solid rgb(159, 76, 173)' }">
<v-card-text>
<component :is="currentComponent"
:creator="userStore.creator"
:creator="creatorProfileStore.value"
:colorName="colorToEdit"
@closeRequested="closeDialog"
@requestAccept="requestAccept"
@@ -87,7 +88,7 @@ const closeDialog = () => {
{{ $t('creatorinfopage.pageinformation') }}
</h1>
<div v-if="userStore.hasCreator" class="w-full max-w-[800px]">
<div v-if="creatorProfileStore.hasCreator" class="w-full max-w-[800px]">
<div class="my-10 border rounded-2xl">
@@ -97,27 +98,7 @@ const closeDialog = () => {
class="HoverBtn active:bg-gray-300 py-2 px-4 border-gray-400 shadow flex items-center transition duration-200 ease-in-out w-full">
<span class="flex-none pa-2 min-w-32 text-left">{{ $t('creatorinfopage.name') }}</span>
<span class="flex-auto text-left pr-6 capitalize">{{ userStore.creator.name }}</span>
<span class="flex-none">
<v-icon>mdi-chevron-right</v-icon>
</span>
</button>
<button
@click="openDialog('About')"
class="HoverBtn active:bg-gray-300 py-2 px-4 border-gray-400 shadow flex items-center transition duration-200 ease-in-out w-full">
<span class="flex-none pa-2 min-w-32 text-left">{{$t('creatorinfopage.title')}}</span>
<span class="flex-auto text-left pr-6">{{ userStore.creator.about.title }}</span>
<span class="flex-none">
<v-icon>mdi-chevron-right</v-icon>
</span>
</button>
<button
@click="openDialog('About')"
class="HoverBtn active:bg-gray-300 py-2 px-4 border-gray-400 shadow flex items-center transition duration-200 ease-in-out w-full rounded-b-2xl ">
<span class="pa-2 min-w-32 text-left">{{$t('creatorinfopage.description')}}</span>
<span class="flex-auto text-left pr-6">{{ userStore.creator.about.description }}</span>
<span class="flex-auto text-left pr-6 capitalize">{{ creatorProfileStore.value.name }}</span>
<span class="flex-none">
<v-icon>mdi-chevron-right</v-icon>
</span>
@@ -195,7 +176,7 @@ const closeDialog = () => {
@click="openDialog('Socials')"
class="HoverBtn active:bg-gray-300 py-2 px-4 border-gray-400 shadow flex items-center transition duration-200 ease-in-out w-full">
<span class="pa-2 min-w-32 text-left"><v-icon>mdi-facebook</v-icon></span>
<span class="flex-auto text-left pr-6">{{ userStore.creator.socials.facebookUrl }}</span>
<span class="flex-auto text-left pr-6">{{ creatorProfileStore.value.socials.facebookUrl }}</span>
<span class="flex-none">
<v-icon>mdi-chevron-right</v-icon>
</span>
@@ -205,7 +186,7 @@ const closeDialog = () => {
@click="openDialog('Socials')"
class="HoverBtn active:bg-gray-300 py-2 px-4 border-gray-400 shadow flex items-center transition duration-200 ease-in-out w-full">
<span class="flex-none pa-2 min-w-32 text-left"> <v-icon>mdi-instagram</v-icon></span>
<span class="flex-auto text-left pr-6">{{ userStore.creator.socials.instagramUrl }}</span>
<span class="flex-auto text-left pr-6">{{ creatorProfileStore.value.socials.instagramUrl }}</span>
<span class="flex-none">
<v-icon>mdi-chevron-right</v-icon>
</span>
@@ -214,9 +195,10 @@ const closeDialog = () => {
<button
@click="openDialog('Socials')"
class="HoverBtn active:bg-gray-300 py-2 px-4 border-gray-400 shadow flex items-center transition duration-200 ease-in-out w-full">
<span class="flex-none pa-2 min-w-32 text-left"> <img src="/images/hutopymedia/icons/x.svg" width="23px"
height="23px"></span>
<span class="flex-auto text-left pr-6">{{ userStore.creator.socials.xUrl }}</span>
<span class="flex-none pa-2 w-6 h-6 text-left">
<XIcon></XIcon>
</span>
<span class="flex-auto text-left pr-6">{{ creatorProfileStore.value.socials.xUrl }}</span>
<span class="flex-none">
<v-icon>mdi-chevron-right</v-icon>
</span>
@@ -226,7 +208,7 @@ const closeDialog = () => {
@click="openDialog('Socials')"
class="HoverBtn active:bg-gray-300 py-2 px-4 border-gray-400 shadow flex items-center transition duration-200 ease-in-out w-full ">
<span class="pa-2 min-w-32 text-left"><v-icon>mdi-linkedin</v-icon></span>
<span class="flex-auto text-left pr-6">{{ userStore.creator.socials.linkedInUrl }}</span>
<span class="flex-auto text-left pr-6">{{ creatorProfileStore.value.socials.linkedInUrl }}</span>
<span class="flex-none">
<v-icon>mdi-chevron-right</v-icon>
</span>
@@ -236,9 +218,9 @@ const closeDialog = () => {
@click="openDialog('Socials')"
class="HoverBtn active:bg-gray-300 py-2 px-4 border-gray-400 shadow flex items-center transition duration-200 ease-in-out w-full ">
<span class="flex-none pa-2 min-w-32 text-left">
<img src="/images/hutopymedia/icons/tiktok.svg" class="w-5 h-5">
<XIcon class="w-5 h-5"></XIcon>
</span>
<span class="flex-auto text-left pr-6">{{ userStore.creator.socials.tikTokUrl }}</span>
<span class="flex-auto text-left pr-6">{{ creatorProfileStore.value.socials.tikTokUrl }}</span>
<span class="flex-none">
<v-icon>mdi-chevron-right</v-icon>
</span>
@@ -248,7 +230,7 @@ const closeDialog = () => {
@click="openDialog('Socials')"
class="HoverBtn active:bg-gray-300 py-2 px-4 border-gray-400 shadow flex items-center transition duration-200 ease-in-out w-full ">
<span class="pa-2 min-w-32 text-left"><v-icon>mdi-youtube</v-icon></span>
<span class="flex-auto text-left pr-6">{{ userStore.creator.socials.youtubeUrl }}</span>
<span class="flex-auto text-left pr-6">{{ creatorProfileStore.value.socials.youtubeUrl }}</span>
<span class="flex-none">
<v-icon>mdi-chevron-right</v-icon>
</span>
@@ -258,7 +240,7 @@ const closeDialog = () => {
@click="openDialog('Socials')"
class="HoverBtn active:bg-gray-300 py-2 px-4 border-gray-400 shadow flex items-center transition duration-200 ease-in-out w-full ">
<span class="pa-2 min-w-32 text-left"><v-icon>mdi-reddit</v-icon></span>
<span class="flex-auto text-left pr-6">{{ userStore.creator.socials.redditUrl }}</span>
<span class="flex-auto text-left pr-6">{{ creatorProfileStore.value.socials.redditUrl }}</span>
<span class="flex-none">
<v-icon>mdi-chevron-right</v-icon>
</span>
@@ -268,7 +250,7 @@ const closeDialog = () => {
@click="openDialog('Socials')"
class="HoverBtn active:bg-gray-300 py-2 px-4 border-gray-400 shadow flex items-center transition duration-200 ease-in-out w-full rounded-b-2xl ">
<span class="pa-2 min-w-32 text-left"><v-icon>mdi-web</v-icon></span>
<span class="flex-auto text-left pr-6">{{ userStore.creator.socials.websiteUrl }}</span>
<span class="flex-auto text-left pr-6">{{ creatorProfileStore.value.socials.websiteUrl }}</span>
<span class="flex-none">
<v-icon>mdi-chevron-right</v-icon>
</span>

View File

@@ -1,4 +1,5 @@
<script setup>
import XIcon from '@/assets/icons/x.svg'
import {ref} from 'vue'
import {useClient} from "@/plugins/api.js";
@@ -56,6 +57,14 @@ const cancel = () => {
</script>
<style scoped>
.icon {
width: 40px;
height: 40px;
fill: #1976d2;
}
</style>
<template>
<div class="pb-5 text-2xl">Reseaux Sociaux</div>
<div class="flex flex-row align-center">
@@ -100,7 +109,9 @@ const cancel = () => {
</div>
<div class="flex flex-row align-center">
<img src="/images/hutopymedia/icons/tiktok.svg" width="23px" height="23px" class="mb-5 mr-2">
<div class="w-6 h-6 mb-5 mr-2">
<XIcon></XIcon>
</div>
<v-text-field
variant="outlined"
v-model="tikTokUrl"
@@ -118,9 +129,13 @@ const cancel = () => {
outlined
></v-text-field>
</div>
<div class="flex flex-row align-center">
<img src="/images/hutopymedia/icons/x.svg" width="23px" height="23px" class="mb-5 mr-2">
<div class="w-6 h-6 mb-5 mr-2">
<XIcon class="icon"></XIcon>
</div>
<v-text-field
variant="outlined"
v-model="xUrl"

View File

@@ -1,7 +1,7 @@
import {fileURLToPath, URL} from 'node:url'
import {defineConfig, loadEnv} from 'vite'
import vue from '@vitejs/plugin-vue'
import svgLoader from 'vite-svg-loader'
// https://vitejs.dev/config/
export default defineConfig(({mode}) => {
@@ -10,6 +10,7 @@ export default defineConfig(({ mode }) => {
return {
plugins: [
vue(),
svgLoader()
],
resolve: {
alias: {