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", "eslint-plugin-vue": "^9.22.0",
"postcss": "^8.4.35", "postcss": "^8.4.35",
"tailwindcss": "^3.4.1", "tailwindcss": "^3.4.1",
"vite": "^5.2.11" "vite": "^5.2.11",
"vite-svg-loader": "^5.1.0"
} }
}, },
"node_modules/@alloc/quick-lru": { "node_modules/@alloc/quick-lru": {
@@ -994,6 +995,16 @@
"tailwindcss": ">=2.0.0" "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": { "node_modules/@types/estree": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
@@ -1692,6 +1703,50 @@
"node": ">= 8" "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": { "node_modules/cssesc": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -1703,6 +1758,42 @@
"node": ">=4" "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": { "node_modules/csstype": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
@@ -1776,6 +1867,65 @@
"node": ">=6.0.0" "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": { "node_modules/eastasianwidth": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
@@ -2887,6 +3037,13 @@
"node": ">=0.10.0" "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": { "node_modules/merge2": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -3931,6 +4088,42 @@
"url": "https://github.com/sponsors/ljharb" "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": { "node_modules/tailwindcss": {
"version": "3.4.10", "version": "3.4.10",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.10.tgz", "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": { "node_modules/vue": {
"version": "3.4.38", "version": "3.4.38",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.38.tgz", "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.38.tgz",

View File

@@ -31,6 +31,7 @@
"eslint-plugin-vue": "^9.22.0", "eslint-plugin-vue": "^9.22.0",
"postcss": "^8.4.35", "postcss": "^8.4.35",
"tailwindcss": "^3.4.1", "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,30 +1,27 @@
<template> <template>
<v-app> <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>
<transition name="slide-fade"> <Header class="fixed w-full z-50 p-2 h-16"></Header>
<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>
</div>
</transition>
<div class="flex flex-col w-full" style="background-color: #f4f4f4"> <div class="flex flex-row mt-16">
<RouterView></RouterView>
<!-- <Footer></Footer>--> <transition name="slide-fade">
<div v-show="sideBarStore.visible"
class="fixed h-full min-w-60 border-r-2 z-30 ">
<side-bar></side-bar>
</div> </div>
</transition>
<div class="flex flex-col w-full min-h-screen bg-amber">
<RouterView></RouterView>
</div> </div>
</div> </div>
</v-app> </v-app>
<size-indicator></size-indicator> <size-indicator></size-indicator>
</template> </template>
<script async setup> <script async setup>

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": { "footer": {
"allRightsReserved": "All rights reserved" "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": { "sidebar": {
"subscriptionTitle": "Subscription" "subscriptionTitle": "Subscription"
}, },

View File

@@ -9,16 +9,6 @@
"footer": { "footer": {
"allRightsReserved": "Tout droits réservés" "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": { "sidebar": {
"subscriptionTitle": "Abonnements" "subscriptionTitle": "Abonnements"
}, },

View File

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

View File

@@ -1,4 +1,4 @@
import { createRouter, createWebHistory } from 'vue-router' import {createRouter, createWebHistory} from 'vue-router'
import About from '@/views/documentation/About.vue' import About from '@/views/documentation/About.vue'
import ContentPolicy from '@/views/documentation/ContentPolicy.vue' import ContentPolicy from '@/views/documentation/ContentPolicy.vue'
import FAQ from '@/views/documentation/FAQ.vue' import FAQ from '@/views/documentation/FAQ.vue'
@@ -14,33 +14,24 @@ import Register from '../views/main/Register.vue'
import Home from '../views/main/Home.vue' import Home from '../views/main/Home.vue'
import Wallet from '../views/main/Wallet.vue' import Wallet from '../views/main/Wallet.vue'
import ProfilePage from '@/views/profile/ProfilePage.vue' import ProfilePage from '@/views/profile/ProfilePage.vue'
import CreatorList from '@/views/creators/CreatorList.vue' import CreatorList from '@/views/browser/CreatorList.vue'
import CreatorPage from "@/views/creators/CreatorPage.vue";
import ContentPage from "@/views/contents/ContentPage.vue";
import PostContent from "@/views/contents/PostContent.vue"; import PostContent from "@/views/contents/PostContent.vue";
import Explorer from "@/views/explorer/explorer.vue"; import Explorer from "@/views/explorer/explorer.vue";
import {useAuthStore} from "@/stores/authStore.js"; import {useAuthStore} from "@/stores/authStore.js";
import ForYouPage from "@/views/profile/ForYouPage.vue"; import ForYouPage from "@/views/profile/ForYouPage.vue";
import CreatorPresentation from "@/views/creators/CreatorPresentation.vue";
import CreatorExclusiveContent from "@/views/creators/CreatorExclusiveContent.vue"; import CreatorLayout from "@/views/creators/CreatorLayout.vue";
import CTA01 from "@/views/CTA01.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 = [ const routes = [
{
path: '/CTA01',
component: CTA01,
meta: { hideSideBar: true }
},
{ {
path: '/', path: '/',
component: Home, component: Home,
meta: { hideSideBar: true } meta: {hideSideBar: true}
},
{
path: '/presentation/@:creator',
component: CreatorPresentation,
}, },
{ {
path: '/browse', path: '/browse',
@@ -52,69 +43,81 @@ const routes = [
}, },
{ {
path: '/@:creator', path: '/@:creator',
component: CreatorPage component: CreatorLayout,
meta: {
requiresAuth: true,
},
children: [
{
path: '',
component: CreatorHome,
},
{
path: 'news',
component: CreatorNews,
},
{
path: 'content',
component: CreatorContent
},
]
}, },
{ {
path: '/content/post', path: '/content/post',
component: PostContent, component: PostContent,
}, },
{
path: '/@:creator/exclusivecontent',
component: CreatorExclusiveContent
},
{ {
path: '/helpandcontact', path: '/helpandcontact',
component: HelpAndContact, component: HelpAndContact,
meta: { hideSideBar: true } meta: {hideSideBar: true}
}, },
{ {
path: '/termsandconditions', path: '/termsandconditions',
name: 'termsandconditions', name: 'termsandconditions',
component: TermsAndConditions, component: TermsAndConditions,
meta: { hideSideBar: true } meta: {hideSideBar: true}
}, },
{ {
path: '/contentpolicy', path: '/contentpolicy',
name: 'contentpolicy', name: 'contentpolicy',
component: ContentPolicy, component: ContentPolicy,
meta: { hideSideBar: true } meta: {hideSideBar: true}
}, },
{ {
path: '/faq', path: '/faq',
name: 'FAQ', name: 'FAQ',
component: FAQ, component: FAQ,
meta: { hideSideBar: true } meta: {hideSideBar: true}
}, },
{ {
path: '/guideforcreators', path: '/guideforcreators',
name: 'guideforcreators', name: 'guideforcreators',
component: CreatorGuide, component: CreatorGuide,
meta: { hideSideBar: true } meta: {hideSideBar: true}
}, },
{ {
path: '/about', path: '/about',
name: 'about', name: 'about',
component: About, component: About,
meta: { hideSideBar: true } meta: {hideSideBar: true}
}, },
{ {
path: '/pricing', path: '/pricing',
name: 'pricing', name: 'pricing',
component: Pricing, component: Pricing,
meta: { hideSideBar: true } meta: {hideSideBar: true}
}, },
{ {
path: '/join', path: '/join',
name: 'join', name: 'join',
component: Join, component: Join,
meta: { hideSideBar: true } meta: {hideSideBar: true}
}, },
{ {
path: '/register', path: '/register',
name: 'register', name: 'register',
component: Register, component: Register,
meta: { hideSideBar: true } meta: {hideSideBar: true}
}, },
{ {
path: '/signup', path: '/signup',
@@ -128,32 +131,31 @@ const routes = [
}, },
{ {
path: '/paymentcompleted', path: '/paymentcompleted',
name: 'PayementCompleted', name: 'PaymentCompleted',
component: PaymentCompleted, component: PaymentCompleted,
}, },
{ {
path: '/wallet', path: '/wallet',
name: 'wallet', name: 'wallet',
component: Wallet, component: Wallet,
meta: { requiresAuth: true } meta: {requiresAuth: true}
}, },
{ {
path: '/profile', path: '/profile',
name: 'profile', name: 'profile',
component: ProfilePage, component: ProfilePage,
meta: { requiresAuth: true } meta: {requiresAuth: true}
}, },
{ {
path: '/explorer', path: '/explorer',
name: 'explorer', name: 'explorer',
component: Explorer, component: Explorer,
}, },
{ {
path: '/feed', path: '/feed',
name: 'feed', name: 'feed',
component: ForYouPage, component: ForYouPage,
}, },
] ]
@@ -162,18 +164,18 @@ const router = createRouter({
routes routes
}) })
// Navigation gards // Navigation guards
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
const authStore = useAuthStore(); const authStore = useAuthStore();
if (to.matched.some(record => record.meta.requiresAuth)) { if (to.matched.some(record => record.meta.requiresAuth)) {
if (!authStore.isAuthenticated) { if (!authStore.isAuthenticated) {
next('/'); next('/');
} else { } else {
next(); next();
} }
} else { } else {
next(); next();
} }
}) })

View File

@@ -63,12 +63,12 @@ export const useAuthStore = defineStore(
} }
} }
async function loginGoogle(accessToken) { async function loginWithGoogle(accessToken) {
try { try {
const response = await clientApi.post( const response = await clientApi.post(
'api/google/sign-in', 'api/users/login-with-google',
{ {
accessToken: accessToken token: accessToken
}) })
updateTokens(response.data) updateTokens(response.data)
} catch (error) { } 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 {useClient} from "@/plugins/api.js";
import {useSessionStorage} from "@vueuse/core"; import {useSessionStorage} from "@vueuse/core";
export const useUserStore = defineStore( export const useUserProfileStore = defineStore(
'user', 'user-profile',
() => { () => {
const authStore = useAuthStore() const authStore = useAuthStore()
const authWatcher = watch( const authWatcher = watch(
() => authStore.isAuthenticated, () => authStore.isAuthenticated,
async (newValue) => { async (newValue) => {
if (newValue) { if (newValue) {
await fetchCurrentUserProfile() await fetchCurrentUserProfile()
await fetchCurrentCreatorProfile()
} else { } else {
user.value = undefined value.value = undefined
creator.value = undefined
} }
}) })
const user = useSessionStorage( const value = useSessionStorage(
'user-user', 'user-profile',
{}, {},
{writeDefaults: false}) {writeDefaults: false})
const creator = useSessionStorage(
'user-creator',
{},
{writeDefaults: false})
const hasCreator = computed(() =>
creator.value
&& Object.getOwnPropertyNames(creator.value).length >= 1)
const fullname = computed(() => { const fullname = computed(() => {
if (user.value) { if (value.value) {
const {firstname, lastname} = user.value; const {firstname, lastname} = value.value;
if (firstname && lastname) { if (firstname && lastname) {
return `${lastname}, ${firstname}`; return `${lastname}, ${firstname}`;
@@ -49,38 +41,29 @@ export const useUserStore = defineStore(
}) })
const alias = computed(() => { const alias = computed(() => {
if (user.value) { if (value.value) {
return user.value.alias || `${user.value.firstname || ''} ${user.value.lastname || ''}`.trim() || 'Anonyme' return value.value.alias || `${value.value.firstname || ''} ${value.value.lastname || ''}`.trim() || 'Anonyme'
} }
return 'Anonyme'; return 'Anonyme';
}) })
const portraitUrl = computed(() => { const portraitUrl = computed(() => {
return user.value && user.value.portraitUrl return value.value && value.value.portraitUrl
? user.value.portraitUrl ? value.value.portraitUrl
: '/images/usersmedia/anonyme/profilepictures/profileAnonymeSquare.png' : '/images/usersmedia/anonyme/profilepictures/profileAnonymeSquare.png'
}) })
const client = useClient()
async function fetchCurrentUserProfile() { async function fetchCurrentUserProfile() {
try { try {
const client = useClient()
const userResponse = await client.get("/api/users/profile"); const userResponse = await client.get("/api/users/profile");
user.value = userResponse.data value.value = userResponse.data
// Cache-busting only if portraitUrl exists // Cache-busting only if portraitUrl exists
if (user.value.portraitUrl) { if (value.value.portraitUrl) {
user.value.portraitUrl = `${user.value.portraitUrl}?${Date.now()}`; value.value.portraitUrl = `${value.value.portraitUrl}?${Date.now()}`;
} }
} catch (error) { } catch (error) {
user.value = undefined; value.value = undefined;
}
}
async function fetchCurrentCreatorProfile() {
try {
const creatorResponse = await client.get(`/api/creators/profile`)
creator.value = creatorResponse.data
} catch (error) {
creator.value = undefined
} }
} }
@@ -92,8 +75,8 @@ export const useUserStore = defineStore(
firstname: firstname, firstname: firstname,
lastname: lastname lastname: lastname
}) })
user.value.firstname = firstname; value.value.firstname = firstname;
user.value.lastname = lastname; value.value.lastname = lastname;
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} }
@@ -106,7 +89,7 @@ export const useUserStore = defineStore(
{ {
alias: alias alias: alias
}) })
user.value.alias = alias; value.value.alias = alias;
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} }
@@ -119,7 +102,7 @@ export const useUserStore = defineStore(
{ {
birthdate: birthdate birthdate: birthdate
}) })
user.value.birthDate = birthdate; value.value.birthDate = birthdate;
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} }
@@ -132,7 +115,7 @@ export const useUserStore = defineStore(
{ {
phoneNumber: phoneNumber phoneNumber: phoneNumber
}) })
user.value.phoneNumber = phoneNumber; value.value.phoneNumber = phoneNumber;
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} }
@@ -145,7 +128,7 @@ export const useUserStore = defineStore(
{ {
email: email email: email
}) })
user.value.email = email; value.value.email = email;
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} }
@@ -158,12 +141,12 @@ export const useUserStore = defineStore(
{ {
address: address address: address
}) })
user.value.address = address; value.value.address = address;
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} }
} }
async function changePortrait(selectedFile) { async function changePortrait(selectedFile) {
try { try {
const formData = new FormData(); const formData = new FormData();
@@ -172,18 +155,16 @@ export const useUserStore = defineStore(
const response = await client.post( const response = await client.post(
`/api/users/portrait`, `/api/users/portrait`,
formData) 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) { } catch (error) {
console.error(error) console.error(error)
} }
} }
return { return {
user, user: value,
creator,
alias, alias,
hasCreator,
fullname, fullname,
portraitUrl, portraitUrl,
changeFullname, changeFullname,
@@ -192,7 +173,6 @@ export const useUserStore = defineStore(
changePhone, changePhone,
changeEmail, changeEmail,
changeAddress, changeAddress,
changePortrait, changePortrait
fetchCurrentCreatorProfile
} }
}) })

View File

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

View File

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

View File

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

View File

@@ -1,29 +1,38 @@
<template> <template>
<div>
<v-infinite-scroll :items="contents"
:onLoad="fetchContents">
<v-infinite-scroll :items="contents" <!-- TODO: the -mt-4 is necessary because the v-infinite-scroll has some 'top' panel offsetting the list -->
:onLoad="fetchContents" <div class="flex flex-column gap-4 -mt-4">
class="md:gap-2"> <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-for="content in contents" :key="content.id"> <template v-slot:error>
<component <v-alert type="error">{{ errorMessage }}</v-alert>
:is="isSmallScreen ? ContentCardSm : ContentCardNormal" </template>
:content="content"
@content-deleted="onContentDeleted"
></component>
</template>
<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>
</v-infinite-scroll>
</div>
</template> </template>
<style>
</style>
<script setup> <script setup>
import {useClient} from '@/plugins/api.js'; import {useClient} from '@/plugins/api.js';

View File

@@ -1,8 +1,9 @@
<script setup> <script setup>
import {useClient} from '@/plugins/api.js'; import {useClient} from '@/plugins/api.js';
import {ref} from 'vue'; import {ref} from 'vue';
import {useUserStore} from '@/stores/userStore.js';
import {v7} from 'uuid'; import {v7} from 'uuid';
import {useCreatorProfileStore} from "@/stores/creatorProfileStore.js";
import {useUserProfileStore} from "@/stores/userProfileStore.js";
const props = defineProps({ const props = defineProps({
creator: {type: Object, required: true}, creator: {type: Object, required: true},
@@ -10,7 +11,8 @@ const props = defineProps({
const emits = defineEmits(['content-posted']) const emits = defineEmits(['content-posted'])
const userStore = useUserStore(); const userProfileStore = useUserProfileStore()
const creatorProfileStore = useCreatorProfileStore()
const isDialogActive = ref(false); const isDialogActive = ref(false);
@@ -31,7 +33,7 @@ const removeUrl = (index) => {
async function publishPost() { async function publishPost() {
const formData = new FormData(); const formData = new FormData();
formData.append('id', v7()); formData.append('id', v7());
formData.append('creatorId', userStore.user.id); formData.append('creatorId', creatorProfileStore.value.id);
formData.append('title', title.value); formData.append('title', title.value);
formData.append('description', message.value); formData.append('description', message.value);
files.value.forEach(file => { files.value.forEach(file => {
@@ -73,7 +75,7 @@ const closeDialog = () => {
<template> <template>
<button <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" class="flex items-center text-white transform transition-transform duration-200 hover:text-gray-300 hover:scale-125 px-4"
@click="isDialogActive = true"> @click="isDialogActive = true">
<v-icon style="font-size: 35px; height: 35px; width: 55px;">mdi-text-box-plus-outline</v-icon> <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> <script setup>
import { useUserStore } from "@/stores/userStore.js";
import { REACTIONS } from "@/Constants/Reactions.js"; import { REACTIONS } from "@/Constants/Reactions.js";
import { computed, ref } from "vue"; import { computed, ref } from "vue";
import { useClient } from "@/plugins/api.js"; import { useClient } from "@/plugins/api.js";
import {useAuthStore} from "@/stores/authStore.js" import {useAuthStore} from "@/stores/authStore.js"
import MustBeLogged from "@/views/MustBeLogged.vue"; import MustBeLogged from "@/views/MustBeLogged.vue";
import {useUserProfileStore} from "@/stores/userProfileStore.js";
const userStore = useUserStore(); const userProfileStore = useUserProfileStore();
const authStore = useAuthStore() const authStore = useAuthStore()
const props = defineProps({ const props = defineProps({
@@ -49,8 +49,8 @@ async function reactToContent(reaction) {
const request = { const request = {
ContentId: contentId.value, ContentId: contentId.value,
reaction: reaction, reaction: reaction,
userId: userStore.user.id, userId: userProfileStore.value.id,
userName: `${userStore.user.firstName} ${userStore.user.lastName}`, userName: `${userProfileStore.value.firstName} ${userProfileStore.value.lastName}`,
}; };
adjustReactionCount(reaction); adjustReactionCount(reaction);
await client.post("/api/content/reaction/", request); await client.post("/api/content/reaction/", request);
@@ -61,8 +61,8 @@ async function reactToContent(reaction) {
const requestAdd = { const requestAdd = {
ContentId: contentId.value, ContentId: contentId.value,
reaction: reaction, reaction: reaction,
userId: userStore.user.id, userId: userProfileStore.value.id,
userName: `${userStore.user.firstName} ${userStore.user.lastName}`, userName: `${userProfileStore.value.firstName} ${userProfileStore.value.lastName}`,
}; };
adjustReactionCount(reaction); adjustReactionCount(reaction);
await client.post("/api/content/reaction/", requestAdd); await client.post("/api/content/reaction/", requestAdd);
@@ -71,7 +71,7 @@ async function reactToContent(reaction) {
} else { } else {
const requestRemove = { const requestRemove = {
ContentId: contentId.value, ContentId: contentId.value,
userId: userStore.user.id, userId: userProfileStore.value.id,
}; };
adjustReactionCount(reaction); adjustReactionCount(reaction);
await client.post("/api/content/reaction/remove", requestRemove); await client.post("/api/content/reaction/remove", requestRemove);
@@ -168,7 +168,7 @@ function adjustReactionCount(newReaction) {
} }
function initializeReactions() { 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) { if (userReaction) {
currentReaction.value = userReaction.reaction; currentReaction.value = userReaction.reaction;
hasReacted.value = true; hasReacted.value = true;

View File

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

View File

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

View File

@@ -53,14 +53,7 @@
<div class="flex justify-around py-2"> <div class="flex justify-around py-2">
<Reaction v-if="data" :content="data"></Reaction> <Reaction v-if="data" :content="data"></Reaction>
<donation-button v-if="data" <donation-button v-if="data"></donation-button>
: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> </div>
</div> </div>

View File

@@ -30,7 +30,7 @@
</div> </div>
<div class="flex items-center justify-center w-full h-full z-10"> <div class="flex items-center justify-center w-full h-full z-10">
<img :src="currentImage" alt="Image" class="max-w-full max-h-full object-contain" /> <img :src="currentImage" alt="Image" class="max-w-full max-h-full object-contain"/>
</div> </div>
<!-- right arrow --> <!-- right arrow -->
@@ -45,27 +45,21 @@
<div class="flex flex-col p-6 bg-white overflow-y-auto max-h-screen"> <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="border-b-2 p-6 font-sans space-y-2">
<div class="flex flex-row align-center" v-if="data && data.createdByName"> <div class="flex flex-row align-center" v-if="data && data.createdByName">
<img :src="data.createdByPortraitUrl" class="rounded-full w-9" alt=""> <img :src="data.createdByPortraitUrl" class="rounded-full w-9" alt="">
<p class="ml-2 capitalize ">{{ data.createdByName }}</p> <p class="ml-2 capitalize ">{{ data.createdByName }}</p>
</div> </div>
<div v-if="data && data.description"> <div v-if="data && data.description">
Description: {{ data.description }} Description: {{ data.description }}
</div> </div>
<div class="flex justify-around py-2"> <div v-if="data" class="flex justify-around py-2">
<Reaction v-if="data" :content="data"></Reaction> <reaction :content="data"></reaction>
<donation-button></donation-button>
<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> </div>
</div> </div>
<div class="border-b-2 p-6"> <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> <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="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--> <!--Banner-->
<div class="relative"> <div class="relative">
<div> <div>
<img <img
class="w-full drop-shadow-[0_10px_6px_rgba(0,0,0,0.25)]" 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" alt="Profile Banner"
style="max-height: 425px" style="max-height: 425px"
> >
@@ -19,24 +16,16 @@
</div> </div>
</div> </div>
<div class="h-1" :style="{ backgroundColor: creator.colors.accent || '#6B0065' }"></div>
<!--actions - Lowerpart--> <!--actions - Lowerpart-->
<banner-actions :creator="creator" @content-posted="addContent"></banner-actions> <banner-actions></banner-actions>
</div> </div>
</template> </template>
<script setup> <script setup>
import BannerActions from "@/views/creators/banner/bannerlower/BannerActions.vue"; import BannerActions from "@/views/creators/banner/bannerlower/BannerActions.vue";
import {useBrandingStore} from "@/stores/brandingStore.js";
const props = defineProps({ const branding = useBrandingStore()
creator: {type: Object, required: true},
});
const emits = defineEmits(['content-posted'])
function addContent(content) {
emits('content-posted', content)
}
</script> </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,71 +1,74 @@
<template> <template>
<div class="overflow-hidden relative" @wheel="handleScroll"> <div class="text-center rounded-t-lg p-4 tracking-widest uppercase"
<div class="text-center text-white rounded-t-lg mb-1 py-1" :style="{ color: brandingStore.value.colors.onPrimary, backgroundColor: brandingStore.value.colors.primary}">
:style="{ backgroundColor: creator.colors.bannerTop, letterSpacing: '5px' }"> Actualité
Actualité </div>
<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>
<!-- Container that holds all the posts and allows dynamic scrolling --> <div class="p-4">
<div class="relative max-h-[1000px] overflow-hidden"> <div class="text-lg tracking-[2px]">
<div class="transition-transform duration-500" :style="{ transform: `translateY(-${scrollPosition}px)` }"> {{ item.title }}
<div v-for="(item, index) in actualites" :key="index" class="my-1 text-white" </div>
: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.description"
<p v-if="item.type === 'nouvelle'" class="text-xl" :style="{ letterSpacing: '8px' }">Nouvelle</p> class="text-gray-300 text-sm text-justify mt-1 py-1">
<p v-if="item.type === 'contenu'" class="text-xl" :style="{ letterSpacing: '8px' }">Contenu</p> {{ item.description }}
<p class="text-xs">{{ item.date }}</p> </p>
</div>
<div v-if="item.rating" class="stars flex justify-end">
<div class="p-3"> {{ item.rating }}
<div class="text-lg" style="letter-spacing: 2px">{{ item.title }}</div> </div>
<p v-if="item.description" class="text-gray-300 text-sm text-justify mt-1 py-1">{{ item.description }}</p>
<img v-if="item.photo"
<div v-if="item.rating" class="stars flex justify-end"> :src="item.photo"
{{ item.rating }} class="w-full h-auto my-2"/>
</div>
<video v-if="item.video"
<img v-if="item.photo" :src="item.photo" class="w-full h-auto my-2"/> controls
:src="item.video"
<div class="flex justify-evenly"> class="w-full h-auto my-2">
<v-btn icon variant="plain"> </video>
<v-icon>mdi-thumb-up</v-icon>
</v-btn> <div class="flex justify-evenly">
<v-btn icon variant="plain"> <v-btn icon variant="plain">
<v-icon>mdi-comment-outline</v-icon> <v-icon>mdi-thumb-up</v-icon>
</v-btn> </v-btn>
<v-btn icon variant="plain"> <v-btn icon variant="plain">
<v-icon>mdi-gift-outline</v-icon> <v-icon>mdi-comment-outline</v-icon>
</v-btn> </v-btn>
</div> <v-btn icon variant="plain">
<video v-if="item.video" controls :src="item.video" class="w-full h-auto my-2"></video> <v-icon>mdi-gift-outline</v-icon>
</div> </v-btn>
</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>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, computed, defineProps } from 'vue'; import {ref} from 'vue';
import {useBrandingStore} from "@/stores/brandingStore.js";
const props = defineProps({ const brandingStore = useBrandingStore()
creator: {
type: Object,
required: true,
},
});
const actualites = ref([ const articles = ref([
{ {
type: 'nouvelle', type: 'nouvelle',
title: 'La visite du studio', 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> </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-dialog v-model="donationModal" max-width="500">
<v-form> <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"> <div class="py-4 text-2xl font-bold border-b mb-2">
Je Soutiens! Je Soutiens!
</div> </div>
<div class="flex flex-row align-center px-3"> <div class="flex flex-row align-center px-3">
<img <img
:src="creatorLogo" :src="brandingStore.value.images.logo"
alt="Profile Image" alt="Profile Image"
class="rounded-full" class="rounded-full"
width="40" width="40"
height="40" height="40"
:style="{ border: `2px solid ${colorAccent}` }"> :style="{ border: `2px solid ${brandingStore.value.colors.secondary}` }">
<div class="capitalize px-2 text-2xl">{{ creatorName }}</div> <div class="capitalize px-2 text-2xl">{{ brandingStore.value.name }}</div>
<v-btn icon @click="closeDonationDialog()" class="ml-auto" variant="text"> <v-btn icon @click="closeDonationDialog()" class="ml-auto" variant="text">
<v-icon>mdi-close</v-icon> <v-icon>mdi-close</v-icon>
</v-btn> </v-btn>
@@ -50,7 +50,8 @@
clearable clearable
></v-textarea> ></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"> @click="goPay()" class="w-full mt-5">
Envoyez Envoyez
</v-btn> </v-btn>
@@ -68,34 +69,29 @@
<v-card-actions> <v-card-actions>
<v-spacer></v-spacer> <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-actions>
</v-card> </v-card>
</template> </template>
</v-dialog> </v-dialog>
</template> </template>
<script setup> <script setup>
import {useClient} from '@/plugins/api.js'; import {useClient} from '@/plugins/api.js';
import {loadStripe} from '@stripe/stripe-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({ 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'} 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); const donationModal = ref(false);
function openDonationDialog() { function openDonationDialog() {
@@ -145,6 +141,7 @@ function closeDialog() {
checkout.destroy(); checkout.destroy();
} }
} }
async function goPay() { async function goPay() {
isPaymentDialogActive.value = true; isPaymentDialogActive.value = true;

View File

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

View File

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

View File

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

View File

@@ -1,52 +1,41 @@
<template> <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"
<!-- Container that holds all the posts and allows dynamic scrolling --> :key="index"
<div class="relative h-[1000px] max-h-[1000px] overflow-hidden"> :style="{ backgroundColor: brandingStore.value.colors.primary }">
<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 border-b-2 border-white p-3 mx-2 text-xl tracking-[4px]">
<div class="flex justify-center items-center border-b-2 border-white p-3 mx-2"> {{ item.title }}
<div class="text-xl align-center" style="letter-spacing: 4px">{{ item.title }}</div>
</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>
<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> </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>
<!-- Navigation Buttons directly below the content --> <div v-if="item.amount" class="flex flex-row justify-end space-x-2">
<div v-if="showNavigationButtons" class="flex justify-center mt-2 py-2"> <div class="text-right text-white">{{ item.amount }}$</div>
<v-btn variant="text" class="text-white" height="42" @click="scrollUp"> <div>|</div>
<v-icon size="40">mdi-arrow-up-drop-circle-outline</v-icon> <div v-if="item.quantity"> Quantité: {{ item.quantity }}</div>
</v-btn> </div>
<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>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, computed, defineProps } from 'vue'; import {ref} from 'vue';
import {useBrandingStore} from "@/stores/brandingStore.js";
const props = defineProps({ const brandingStore = useBrandingStore()
creator: {
type: Object,
required: true,
},
});
const actualites = ref([ const rewards = ref([
{ {
title: 'Hoodie', title: 'Hoodie',
description: 'Je suis fier de vous montrer les installations sur lesquelles nous avons travaillé si fort.', 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> </script>

View File

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

View File

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

View File

@@ -12,11 +12,12 @@ const subscriptionStore = useSubscriptionStore()
<RouterLink class="capitalize" :to="`/@${subscription.creatorName}`"> <RouterLink class="capitalize" :to="`/@${subscription.creatorName}`">
<div class="flex items-center content-center font-sans font-semibold pt-2 "> <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
alt="Profile Image" :src="subscription.creatorPortraitUrl ? subscription.creatorPortraitUrl: '/images/usersmedia/anonyme/profilepictures/profileAnonymeSquare.png' "
class="rounded-full mx-2" alt="Profile Image"
width="32px" class="rounded-full mx-2"
height="32px"> width="32px"
height="32px">
{{ subscription.creatorName }} {{ subscription.creatorName }}
</div> </div>
@@ -26,9 +27,7 @@ const subscriptionStore = useSubscriptionStore()
</template> </template>
<template v-else> <template v-else>
<slot> <span>No subscriptions</span>
<span>No subscriptions</span>
</slot>
</template> </template>
</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 BannerActionsLg from "@/views/creators/banner/bannerlower/BannerActionsLg.vue";
import BannerActionsXl from "@/views/creators/banner/bannerlower/BannerActionsXl.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> </script>
<template> <template>
<div> <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 class="d-none d-sm-flex d-md-none"
<banner-actions-md :creator="creator" @content-posted="addContent"></banner-actions-md> ></banner-actions-md>
</div>
<div class="d-none d-md-flex d-lg-none"> <banner-actions-lg class="d-none d-md-flex d-lg-none"
<banner-actions-lg :creator="creator" @content-posted="addContent"></banner-actions-lg> ></banner-actions-lg>
</div>
<div class="d-none d-lg-flex"> <banner-actions-xl class="d-none d-lg-flex"
<banner-actions-xl :creator="creator" @content-posted="addContent"></banner-actions-xl> ></banner-actions-xl>
</div>
</div> </div>
</template> </template>

View File

@@ -1,7 +1,7 @@
<template> <template>
<div class="relative w-full"> <div class="relative w-full">
<div ref="mainContainer" class="rounded-b-2xl pt-2 pb-1" <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 --> <!-- Logo & User Info -->
<div class="relative z-20"> <div class="relative z-20">
@@ -9,36 +9,21 @@
<div> <div>
<img <img
class="shadow-2xl rounded-full border-solid border-4 absolute z-20 max-w-[190px] ml-15 -mt-32" 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" alt="Profile Picture"
:style="{ borderColor: creator.colors.accent || '#A30E79', height: '190px'}" :style="{ borderColor: brandingStore.value.colors.accent, height: '190px'}"
/> />
</div> </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"> <div class="flex flex-row ml-auto space-x-2.5">
<donation-button-banner <donation-button-banner></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>
<div class="flex flex-column"> <div class="flex flex-column">
<!-- Bouton abonnement affiché seulement si non abonné --> <!-- 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"> <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> </div>
</div> </div>
@@ -48,36 +33,27 @@
<!-- Conteneur sticky --> <!-- Conteneur sticky -->
<div v-show="isSticky" class=" sticky-header fixed top-14 left-0 right-0 w-full z-20" <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 class="shadow-3xl flex flex-row items-center py-2 px-2">
<div> <div>
<img <img
class="max-w-[40px] max-h-[40px] ml-5 rounded-full" 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" alt="Profile Picture"
:style="{ borderColor: creator.colors.accent || '#A30E79', height: '190px'}" :style="{ borderColor: brandingStore.value.colors.accent, height: '190px'}"
/> />
</div> </div>
<div class="ml-5 text-white"> <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>
<div class="ml-auto flex flex-row space-x-2.5 mr-3 "> <div class="ml-auto flex flex-row space-x-2.5 mr-3 ">
<donation-button-banner-slim <donation-button-banner-slim></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>
<!-- Afficher le bouton d'abonnement seulement si l'utilisateur n'est pas abonné --> <!-- Afficher le bouton d'abonnement seulement si l'utilisateur n'est pas abonné -->
<subscribe-button-slim <subscribe-button-slim
v-if="!isSubscribed" v-if="!isSubscribed">
:creator="creator"
:color-border="creator.colors.menu">
</subscribe-button-slim> </subscribe-button-slim>
</div> </div>
@@ -88,22 +64,19 @@
<script setup> <script setup>
import { ref, onMounted, computed } from 'vue'; import {ref, onMounted, computed} from 'vue';
import { useSubscriptionStore } from "@/stores/subscriptionStore.js";
import SubscribeButton from "@/views/creators/SubscribeButton.vue"; import SubscribeButton from "@/views/creators/SubscribeButton.vue";
import DonationButtonBanner from "@/views/creators/DonationButtonBanner.vue"; import DonationButtonBanner from "@/views/creators/DonationButtonBanner.vue";
import CreatorAbout from "@/views/creators/CreatorAbout.vue";
import SubscribeButtonSlim from "@/views/creators/SubscribeButtonSlim.vue"; import SubscribeButtonSlim from "@/views/creators/SubscribeButtonSlim.vue";
import DonationButtonBannerSlim from "@/views/creators/DonationButtonBannerSlim.vue"; import DonationButtonBannerSlim from "@/views/creators/DonationButtonBannerSlim.vue";
import {useSubscriptionStore} from "@/stores/subscriptionStore.js";
import {useBrandingStore} from "@/stores/brandingStore.js";
const props = defineProps({ const brandingStore = useBrandingStore()
creator: { type: Object, required: true } const subscriptionStore = useSubscriptionStore()
});
const subscriptionStore = useSubscriptionStore();
// Calculer si l'utilisateur est abonné // 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 isSticky = ref(false);
const mainContainer = ref(null); const mainContainer = ref(null);
@@ -113,7 +86,7 @@ onMounted(() => {
([entry]) => { ([entry]) => {
isSticky.value = !entry.isIntersecting; isSticky.value = !entry.isIntersecting;
}, },
{ threshold: 0 } {threshold: 0}
); );
if (mainContainer.value) { if (mainContainer.value) {

View File

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

View File

@@ -1,32 +1,14 @@
<template> <template>
<div class="w-full"> <div class="w-full">
<div <div
:style="{ :style="{backgroundColor: brandingStore.value.colors.bannerBottom, borderBottom: `2px solid ${brandingStore.value.colors.accent}`}">
backgroundColor: creator.colors.bannerBottom || '#A30E79',
borderBottom: `2px solid ${creator.colors.accent || '#000000'}`
}">
<div> <div>
<!-- Logo-Name-Followers--> <!-- Logo-Name-Followers-->
<div class="flex flex-row relative z-20"> <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"> <div class="flex flex-column text-white capitalize px-2 mt-1">
<p class="capitalize text-2xl font-bold">{{ creator.name }}</p> <p class="capitalize text-2xl font-bold">{{ brandingStore.value.name }}</p>
<div>{{ creator.subscriberCount }} {{ $t('banner.subscription')}}</div> <div>{{ brandingStore.value.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>
</div> </div>
</div> </div>
@@ -35,20 +17,11 @@
<div class="flex flex-row items-center justify-between w-full px-4"> <div class="flex flex-row items-center justify-between w-full px-4">
<div> <div>
<subscribe-button :creator="creator"></subscribe-button> <subscribe-button></subscribe-button>
</div> </div>
<div class="flex ml-auto space-x-4"> <div class="flex ml-auto space-x-4">
<publish-content-button :creator="creator" <donation-button iconColorClass="text-white"
@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> ></donation-button>
</div> </div>
@@ -62,17 +35,9 @@
<script setup> <script setup>
import SubscribeButton from "@/views/creators/SubscribeButton.vue"; import SubscribeButton from "@/views/creators/SubscribeButton.vue";
import PublishContentButton from "@/views/contents/PublishContentButton.vue";
import DonationButton from "@/views/creators/DonationButton.vue"; import DonationButton from "@/views/creators/DonationButton.vue";
import CreatorAbout from "@/views/creators/CreatorAbout.vue"; import {useBrandingStore} from "@/stores/brandingStore.js";
const props = defineProps({ const brandingStore = useBrandingStore()
creator: {type: Object, required: true}
});
const emits = defineEmits(['content-posted']);
function addContent(content) {
emits('content-posted', content);
}
</script> </script>

View File

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

View File

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

View File

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

View File

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

View File

@@ -63,13 +63,8 @@
</p> </p>
</div> </div>
<selected-footer></selected-footer>
</template> </template>
<style> <style>
@import '@/cssstyle/tosstyle.css'; @import '@/cssstyle/tosstyle.css';
</style> </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'
<footer> import FacebookIcon from '@/assets/icons/facebook.svg'
<div class="p-4 text-center font-sans"> import InstagramIcon from '@/assets/icons/instagram.svg'
</script>
<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') }} Hutopy &copy;{{ new Date().getFullYear() }} - {{ $t('footer.allRightsReserved') }}
</div> </div>
</footer> </footer>
</template> </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 }"> <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"> <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"> <span class="max-w-xs hidden md:block capitalize">
{{ userStore.alias }} {{ userProfileStore.alias }}
</span> </span>
<img <img
:src="userStore.portraitUrl" :src="userProfileStore.portraitUrl"
alt="Profile Image" alt="Profile Image"
class="ml-2 rounded-full" class="ml-2 rounded-full"
width="32" width="32"
@@ -119,13 +119,13 @@
</template> </template>
<template v-else> <template v-else>
<v-list-item v-if="userStore.creator && Object.keys(userStore.creator).length > 0" class="nav-button"> <v-list-item v-if="creatorProfileStore.value && Object.keys(creatorProfileStore.value).length > 0" class="nav-button">
<router-link :to="`/@${userStore.creator.name}`"> <router-link :to="`/@${creatorProfileStore.value.name}`">
<v-btn class="w-100" variant="plain">@{{ userStore.creator.name }}</v-btn> <v-btn class="w-100" variant="plain">@{{ creatorProfileStore.value.name }}</v-btn>
</router-link> </router-link>
</v-list-item> </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"> <router-link to="/profile?target=CreatorPage">
<v-btn class="w-100" variant="plain">Activer votre page</v-btn> <v-btn class="w-100" variant="plain">Activer votre page</v-btn>
</router-link> </router-link>
@@ -158,12 +158,16 @@
import { ref, onBeforeUnmount, onBeforeMount, watch } from "vue"; import { ref, onBeforeUnmount, onBeforeMount, watch } from "vue";
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useSideBarStore } from '@/stores/sideBarStore.js'; import { useSideBarStore } from '@/stores/sideBarStore.js';
import { useUserStore } from "@/stores/userStore.js";
import { useAuthStore } from "@/stores/authStore.js"; import { useAuthStore } from "@/stores/authStore.js";
import { useDisplay } from 'vuetify'; import { useDisplay } from 'vuetify';
import {useUserProfileStore} from "@/stores/userProfileStore.js";
import {useCreatorProfileStore} from "@/stores/creatorProfileStore.js";
const authStore = useAuthStore(); const authStore = useAuthStore();
const userStore = useUserStore(); const userProfileStore = useUserProfileStore();
const creatorProfileStore = useCreatorProfileStore();
const sideBarStore = useSideBarStore(); const sideBarStore = useSideBarStore();
const { smAndDown } = useDisplay(); const { smAndDown } = useDisplay();

View File

@@ -1,41 +1,51 @@
<template> <template>
<div class="bg-gray-100"> <div class="bg-gray-100">
<div class="py-6"> <div class="py-6">
<div class=" mx-auto flex justify-center"> <div class=" mx-auto flex justify-center">
<img src="/images/hutopymedia/banners/hutopy.png" alt="Hutopy Logo" class="h-24"> <img src="/images/hutopymedia/banners/hutopy.png" alt="Hutopy Logo" class="h-24">
</div> </div>
</div> </div>
</div> </div>
<div class="mx-auto flex justify-center pt-10 max-w-[980px]"> <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> <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"> <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"> <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"> <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> </p>
</div> </div>
</div> </div>
<div class="relative group w-full max-w-[250px] md:max-w-[306px] rounded-2xl overflow-hidden mx-auto"> <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"> <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"> <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> </p>
</div> </div>
</div> </div>
<div class="relative group w-full max-w-[250px] md:max-w-[306px] rounded-2xl overflow-hidden mx-auto"> <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"> <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"> <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> </p>
</div> </div>
</div> </div>
@@ -43,7 +53,7 @@
</div> </div>
<div> <div>
<!-- Main Content Section --> <!-- Main Content Section -->
<div class="max-w-4xl mx-auto px-6 py-8 space-y-6"> <div class="max-w-4xl mx-auto px-6 py-8 space-y-6">
<img src="/images/hutopymedia/homepage/votrehutopy.png" alt="YourHutopy" class="mx-auto mb-8"> <img src="/images/hutopymedia/homepage/votrehutopy.png" alt="YourHutopy" class="mx-auto mb-8">
@@ -84,43 +94,39 @@
<div class="grid grid-cols-1 md:grid-cols-3 gap-4"> <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="flex justify-center items-center"> <div class="flex justify-center items-center">
<RouterLink to="/@Hutopy"> <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> </RouterLink>
</div> </div>
<div class="flex justify-center items-center"> <div class="flex justify-center items-center">
<RouterLink to="/@guillaumem"> <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> </RouterLink>
</div> </div>
<div class="flex justify-center items-center"> <div class="flex justify-center items-center">
<RouterLink to="/@chloebeaugrand"> <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> </RouterLink>
</div> </div>
</div> </div>
<div class="flex justify-center"> <div class="flex justify-center">
<RouterLink to="/browse">
<v-btn variant="tonal" density="default" class="mb-2 w-full">
Découvre les autres créateurs
</v-btn>
</RouterLink>
</div>
</div>
<selected-footer></selected-footer>
</div>
</div>
</template>
<script setup>
import SelectedFooter from "@/views/main/SelectedFooter.vue"; <RouterLink to="/browse">
</script> <v-btn variant="tonal" density="default" class="mb-2 w-full">
Découvre les autres créateurs
</v-btn>
</RouterLink>
</div>
</div>
</div>
</div>
</template>
<style scoped> <style scoped>

View File

@@ -16,11 +16,19 @@
Utilisateur Utilisateur
</v-btn> </v-btn>
<div v-if="showEmailForm" class="w-full mt-2"> <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="email" label="Courriel" variant="outlined" dense prepend-inner-icon="mdi-email"
<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> 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> <v-text-field v-model="password" label="Mot de passe" :type="showPassword ? 'text' : 'password'"
<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> variant="outlined" dense prepend-inner-icon="mdi-lock" append-inner-icon="mdi-eye"
<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> @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> </div>
</div> </div>
@@ -40,7 +48,6 @@ const password = ref("");
const errorSnackBar = ref(false); const errorSnackBar = ref(false);
const showEmailForm = ref(false); const showEmailForm = ref(false);
const showPassword = ref(false); const showPassword = ref(false);
const googleCallback = ref('');
const props = defineProps({ const props = defineProps({
onSuccess: { 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() { async function login() {
const result = await authStore.login(email.value, password.value); const response = await authStore.login(email.value, password.value);
if (result === true) { handleResponse(response)
}
function handleResponse(response) {
if (response === true) {
props.onSuccess(); props.onSuccess();
} else { } else {
if (props.onFailure) { if (props.onFailure) {

View File

@@ -10,14 +10,10 @@
</div> </div>
</div> </div>
</div> </div>
<selected-footer></selected-footer>
</template> </template>
<script setup> <script setup>
import SelectedFooter from "@/views/main/SelectedFooter.vue";
import RegisterForm from "@/views/main/RegisterForm.vue"; import RegisterForm from "@/views/main/RegisterForm.vue";
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
const router = useRouter(); const router = useRouter();
</script> </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,12 +1,11 @@
<script setup> <script setup>
import SiteMenu from "@/views/main/SiteMenu.vue";
import SubscriptionList from "@/views/creators/SubscriptionList.vue"; import SubscriptionList from "@/views/creators/SubscriptionList.vue";
import { useAuthStore } from "@/stores/authStore.js"; import {useAuthStore} from "@/stores/authStore.js";
import { useRouter } from 'vue-router'; import {useRouter} from 'vue-router';
import { ref } from "vue"; import {ref} from "vue";
import { useI18n } from 'vue-i18n'; import {useI18n} from 'vue-i18n';
const { locale } = useI18n(); const {locale} = useI18n();
const router = useRouter(); const router = useRouter();
const selectedLanguage = ref(locale.value); const selectedLanguage = ref(locale.value);
@@ -37,14 +36,12 @@ initializeLocale();
</script> </script>
<template> <template>
<nav class="flex flex-col h-full overflow-y-auto custom-scrollbar"> <nav class="flex flex-col h-full overflow-y-auto custom-scrollbar bg-white">
<div class="mt-16"></div>
<div class="flex justify-center py-3"> <div class="flex justify-center py-3">
<v-btn v-if="authStore.isAuthenticated" variant="plain" class="p-8" @click="feedHandler"> <v-btn v-if="authStore.isAuthenticated" variant="plain" class="p-8" @click="feedHandler">
<img src="/images/hutopymedia/icons/feedfollow.svg" alt="feed follow icon" <img src="/images/hutopymedia/icons/feedfollow.svg" alt="feed follow icon"
style="filter: invert(0.5) brightness(0.0); width: 32px; height: 32px;" /> style="filter: invert(0.5) brightness(0.0); width: 32px; height: 32px;"/>
</v-btn> </v-btn>
<v-btn variant="plain" class="p-8" @click="explorerHandler"> <v-btn variant="plain" class="p-8" @click="explorerHandler">
<v-icon style="font-size: 28px;">mdi-earth</v-icon> <v-icon style="font-size: 28px;">mdi-earth</v-icon>
@@ -61,25 +58,18 @@ initializeLocale();
<div v-if="authStore.isAuthenticated" class="flex-grow px-5 py-4"> <div v-if="authStore.isAuthenticated" class="flex-grow px-5 py-4">
<div class="font-bold"> {{ $t('sidebar.subscriptionTitle') }}</div> <div class="font-bold"> {{ $t('sidebar.subscriptionTitle') }}</div>
<div v-if="authStore.isAuthenticated" class="flex-grow px-5"> <div v-if="authStore.isAuthenticated" class="flex-grow px-5">
</div> </div>
<subscription-list> <subscription-list>
<template v-slot:default>
<span v-if="subscriptionListIsEmpty">Aucun abonnement</span>
</template>
</subscription-list> </subscription-list>
</div> </div>
<div v-else class="flex-grow px-5 py-4 flex justify-center font-bold "> <div v-else class="flex-grow px-5 py-4 flex justify-center font-bold ">
<span>Connectez-vous</span> <span>Connectez-vous</span>
</div> </div>
<div class="border-t w-full py-10 mt-auto">
<SiteMenu></SiteMenu>
</div>
</nav> </nav>
</template> </template>
@@ -92,7 +82,7 @@ nav {
/* Firefox */ /* Firefox */
.custom-scrollbar { .custom-scrollbar {
scrollbar-width: thin; scrollbar-width: thin;
scrollbar-color: #903175 #f1f1f1; scrollbar-color: #903175 #f1f1f1;
} }

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> <script async setup>
import { onBeforeMount, ref, computed } from 'vue'; import { onBeforeMount, ref, computed } from 'vue';
import { useRouter } from 'vue-router'; 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 router = useRouter();
const userTransactions = ref([]); const userTransactions = ref([]);
@@ -72,8 +72,8 @@ const transactionCount = computed(() => userTransactions.value.length);
onBeforeMount( () => { onBeforeMount( () => {
try { try {
userTransactions.value = userStore.user.userTransactions; userTransactions.value = userProfileStore.value.userTransactions;
totalBalance.value = userStore.user.totalBalance; totalBalance.value = userProfileStore.value.totalBalance;
} catch (error) { } catch (error) {
navigateToHome(); navigateToHome();
} }

View File

@@ -6,10 +6,11 @@
class="justify-items-center" class="justify-items-center"
> >
<template v-for="message in messages" :key="message"> <template v-for="message in messages" :key="message">
<message :message="message" <div class="border-b">
@message-deleted="(messageId) => handleDeleteMessage(messageId)" <message :message="message"
class="border-b" @message-deleted="(messageId) => handleDeleteMessage(messageId)"
></message> ></message>
</div>
</template> </template>
<template v-slot:load-more="{ props }"> <template v-slot:load-more="{ props }">
@@ -89,7 +90,7 @@ async function fetchMessages({done, page_size = 10}) {
} }
} }
function handleDeleteMessage(message){ function handleDeleteMessage(message) {
messages.value = messages.value.filter(item => item.id !== message.id); messages.value = messages.value.filter(item => item.id !== message.id);
} }
</script> </script>

View File

@@ -1,7 +1,7 @@
<template> <template>
<div class="flex flex-column"> <div class="flex flex-column">
<div class="flex flex-row items-center "> <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-grow">
<div class="flex flex-row bg-gray-100 rounded-2xl"> <div class="flex flex-row bg-gray-100 rounded-2xl">
<v-textarea <v-textarea
@@ -15,7 +15,7 @@
maxlength="1024" maxlength="1024"
class="pr-1 ml-6 flex-grow" class="pr-1 ml-6 flex-grow"
@keydown.enter.prevent="publish" @keydown.enter.prevent="publish"
> >
</v-textarea> </v-textarea>
<div class="flex flex-col justify-center"> <div class="flex flex-col justify-center">
@@ -36,14 +36,17 @@
</div> </div>
</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> </template>
<script setup> <script setup>
import {ref} from 'vue' import {ref} from 'vue'
import {v7} from 'uuid' import {v7} from 'uuid'
import {useClient} from '@/plugins/api.js' 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 {useAuthStore} from "@/stores/authStore.js"
import MustBeLogged from "@/views/MustBeLogged.vue"; import MustBeLogged from "@/views/MustBeLogged.vue";
@@ -59,7 +62,7 @@ const emits = defineEmits(['message-posted'])
const loginModal = ref(false); const loginModal = ref(false);
const client = useClient() const client = useClient()
const value = ref("") const value = ref("")
const userStore = useUserStore() const userProfileStore = useUserProfileStore()
const authStore = useAuthStore() const authStore = useAuthStore()
const publish = async () => { const publish = async () => {
@@ -76,9 +79,9 @@ const publish = async () => {
emits('message-posted', { emits('message-posted', {
"id": messageId, "id": messageId,
"subjectId": props.subjectId, "subjectId": props.subjectId,
"createdBy": userStore.user.id, "createdBy": userProfileStore.value.id,
"createdByName": userStore.alias, "createdByName": userProfileStore.alias,
"createdByPortraitUrl": userStore.portraitUrl, "createdByPortraitUrl": userProfileStore.portraitUrl,
"createdAt": new Date(Date.now()).toISOString(), "createdAt": new Date(Date.now()).toISOString(),
"value": value.value, "value": value.value,
"parentId": null "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 class="value">Un portrait vous permet de personnaliser votre profil</span>
<span> <span>
<img <img
:src="userStore.user.portraitUrl" :src="userProfileStore.value.portraitUrl"
alt="Profile Image" alt="Profile Image"
class="rounded-full" class="rounded-full"
width="48px" width="48px"
@@ -30,7 +30,7 @@
class="editableValue" class="editableValue"
@click="openEditFullname"> @click="openEditFullname">
<span class="label">{{ $t('personnalinformation.fullname') }}</span> <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> <span><v-icon>mdi-chevron-right</v-icon></span>
</button> </button>
@@ -38,7 +38,7 @@
class="editableValue" class="editableValue"
@click="openEditAlias"> @click="openEditAlias">
<span class="label">{{ $t('personnalinformation.alias') }}</span> <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> <span><v-icon>mdi-chevron-right</v-icon></span>
</button> </button>
@@ -46,7 +46,7 @@
class="editableValue" class="editableValue"
@click="openEditBirthday"> @click="openEditBirthday">
<span class="label">{{ $t('personnalinformation.dob') }}</span> <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> <span><v-icon>mdi-chevron-right</v-icon></span>
</button> </button>
@@ -62,7 +62,7 @@
class="editableValue" class="editableValue"
@click="openEditEmail"> @click="openEditEmail">
<span class="label">{{ $t('personnalinformation.email') }}</span> <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> <span><v-icon>mdi-chevron-right</v-icon></span>
</button> </button>
@@ -70,7 +70,7 @@
class="editableValue" class="editableValue"
@click="openEditPhone"> @click="openEditPhone">
<span class="label">{{ $t('personnalinformation.phone') }}</span> <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> <span><v-icon>mdi-chevron-right</v-icon></span>
</button> </button>
</v-card> </v-card>
@@ -85,7 +85,7 @@
class="editableValue" class="editableValue"
@click="openEditAddress"> @click="openEditAddress">
<span class="label">{{ $t('personnalinformation.home') }}</span> <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> <span><v-icon>mdi-chevron-right</v-icon></span>
</button> </button>
</v-card> </v-card>
@@ -94,7 +94,7 @@
<!-- Modal --> <!-- Modal -->
<v-dialog v-model="dialogEditPortraitShown" max-width="600px"> <v-dialog v-model="dialogEditPortraitShown" max-width="600px">
<portrait-dialog <portrait-dialog
:portrait-url="userStore.user.portraitUrl" :portrait-url="userProfileStore.value.portraitUrl"
@close="handleCloseEditPortrait" @close="handleCloseEditPortrait"
@save="handleSaveEditPortrait" @save="handleSaveEditPortrait"
></portrait-dialog> ></portrait-dialog>
@@ -102,8 +102,8 @@
<v-dialog v-model="dialogEditFullnameShown" max-width="600px"> <v-dialog v-model="dialogEditFullnameShown" max-width="600px">
<fullname-dialog <fullname-dialog
:firstname="userStore.user.firstname" :firstname="userProfileStore.value.firstname"
:lastname="userStore.user.lastname" :lastname="userProfileStore.value.lastname"
@close="handleCloseEditFullname" @close="handleCloseEditFullname"
@save="handleSaveEditFullname" @save="handleSaveEditFullname"
></fullname-dialog> ></fullname-dialog>
@@ -111,7 +111,7 @@
<v-dialog v-model="dialogEditAliasShown" max-width="600px"> <v-dialog v-model="dialogEditAliasShown" max-width="600px">
<alias-dialog <alias-dialog
:alias="userStore.user.alias" :alias="userProfileStore.value.alias"
@close="handleCloseEditAlias" @close="handleCloseEditAlias"
@save="handleSaveEditAlias" @save="handleSaveEditAlias"
></alias-dialog> ></alias-dialog>
@@ -119,7 +119,7 @@
<v-dialog v-model="dialogEditBirthdayShown" max-width="600px"> <v-dialog v-model="dialogEditBirthdayShown" max-width="600px">
<birthday-dialog <birthday-dialog
:birth-date="userStore.user.birthDate" :birth-date="userProfileStore.value.birthDate"
@close="handleCloseEditBirthday" @close="handleCloseEditBirthday"
@save="handleSaveEditBirthday" @save="handleSaveEditBirthday"
></birthday-dialog> ></birthday-dialog>
@@ -127,7 +127,7 @@
<v-dialog v-model="dialogEditPhoneShown" max-width="600px"> <v-dialog v-model="dialogEditPhoneShown" max-width="600px">
<phone-dialog <phone-dialog
:phone="userStore.user.phoneNumber" :phone="userProfileStore.value.phoneNumber"
@close="handleCloseEditPhone" @close="handleCloseEditPhone"
@save="handleSaveEditPhone" @save="handleSaveEditPhone"
></phone-dialog> ></phone-dialog>
@@ -135,7 +135,7 @@
<v-dialog v-model="dialogEditEmailShown" max-width="600px"> <v-dialog v-model="dialogEditEmailShown" max-width="600px">
<email-dialog <email-dialog
:email="userStore.user.email" :email="userProfileStore.value.email"
@close="handleCloseEditEmail" @close="handleCloseEditEmail"
@save="handleSaveEditEmail" @save="handleSaveEditEmail"
></email-dialog> ></email-dialog>
@@ -143,7 +143,7 @@
<v-dialog v-model="dialogEditAddressShown" max-width="600px"> <v-dialog v-model="dialogEditAddressShown" max-width="600px">
<address-dialog <address-dialog
:address="userStore.user.address" :address="userProfileStore.value.address"
@close="handleCloseEditAddress" @close="handleCloseEditAddress"
@save="handleSaveEditAddress" @save="handleSaveEditAddress"
></address-dialog> ></address-dialog>
@@ -159,9 +159,10 @@ import BirthdayDialog from "@/views/profile/account/BirthdayDialog.vue";
import AliasDialog from "@/views/profile/account/AliasDialog.vue"; import AliasDialog from "@/views/profile/account/AliasDialog.vue";
import FullnameDialog from "@/views/profile/account/FullnameDialog.vue"; import FullnameDialog from "@/views/profile/account/FullnameDialog.vue";
import PortraitDialog from "@/views/profile/account/PortraitDialog.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 // ### Portrait
@@ -176,7 +177,7 @@ function handleCloseEditPortrait() {
} }
function handleSaveEditPortrait(portraitData) { function handleSaveEditPortrait(portraitData) {
userStore.changePortrait(portraitData) userProfileStore.changePortrait(portraitData)
dialogEditPortraitShown.value = false dialogEditPortraitShown.value = false
} }
@@ -193,7 +194,7 @@ function handleCloseEditFullname() {
} }
function handleSaveEditFullname(firstname, lastname) { function handleSaveEditFullname(firstname, lastname) {
userStore.changeFullname(firstname, lastname) userProfileStore.changeFullname(firstname, lastname)
dialogEditFullnameShown.value = false dialogEditFullnameShown.value = false
} }
@@ -210,7 +211,7 @@ function handleCloseEditAlias() {
} }
function handleSaveEditAlias(alias) { function handleSaveEditAlias(alias) {
userStore.changeAlias(alias) userProfileStore.changeAlias(alias)
dialogEditAliasShown.value = false dialogEditAliasShown.value = false
} }
@@ -227,7 +228,7 @@ function handleCloseEditBirthday() {
} }
function handleSaveEditBirthday(birthday) { function handleSaveEditBirthday(birthday) {
userStore.changeBirthday(birthday) userProfileStore.changeBirthday(birthday)
dialogEditBirthdayShown.value = false dialogEditBirthdayShown.value = false
} }
@@ -244,7 +245,7 @@ function handleCloseEditPhone() {
} }
function handleSaveEditPhone(phone) { function handleSaveEditPhone(phone) {
userStore.changePhone(phone) userProfileStore.changePhone(phone)
dialogEditPhoneShown.value = false dialogEditPhoneShown.value = false
} }
@@ -261,7 +262,7 @@ function handleCloseEditEmail() {
} }
function handleSaveEditEmail(email) { function handleSaveEditEmail(email) {
userStore.changeEmail(email) userProfileStore.changeEmail(email)
dialogEditEmailShown.value = false dialogEditEmailShown.value = false
} }
@@ -277,7 +278,7 @@ function handleCloseEditAddress() {
} }
function handleSaveEditAddress(address) { function handleSaveEditAddress(address) {
userStore.changeAddress(address) userProfileStore.changeAddress(address)
dialogEditAddressShown.value = false dialogEditAddressShown.value = false
} }
</script> </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> <script setup>
import XIcon from '@/assets/icons/x.svg'
import {computed, ref} from 'vue' import {computed, ref} from 'vue'
import {useUserStore} from "@/stores/userStore.js"
import Socials from './Socials.vue' import Socials from './Socials.vue'
import BannerPicker from './BannerPicker.vue' import BannerPicker from './BannerPicker.vue'
import ColorsPicker from './ColorsPicker.vue' import ColorsPicker from './ColorsPicker.vue'
import LogoPicker from "./LogoPicker.vue" import LogoPicker from "./LogoPicker.vue"
import About from "./About.vue";
import CreateCreator from "./CreateCreator.vue"; import CreateCreator from "./CreateCreator.vue";
import {useClient} from "@/plugins/api.js"; 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 colorBannerTop = computed(() => creatorProfileStore.value.colors.bannerTop || '#a0c0f0')
const colorBannerBottom = computed(() => userStore.creator.colors.bannerBottom || '#a0c0f0') const colorBannerBottom = computed(() => creatorProfileStore.value.colors.bannerBottom || '#a0c0f0')
const colorAccent = computed(() => userStore.creator.colors.accent || '#a0c0f0') const colorAccent = computed(() => creatorProfileStore.value.colors.accent || '#a0c0f0')
const colorMenu = computed(() => userStore.creator.colors.menu || '#a0c0f0') const colorMenu = computed(() => creatorProfileStore.value.colors.menu || '#a0c0f0')
const imageBanner = computed(() => userStore.creator.images.banner || '/images/placeholders/banner.png') const imageBanner = computed(() => creatorProfileStore.value.images.banner || '/images/placeholders/banner.png')
const imageLogo = computed(() => userStore.creator.images.logo || '/images/placeholders/logo.png') const imageLogo = computed(() => creatorProfileStore.value.images.logo || '/images/placeholders/logo.png')
const dialog = ref(false); const dialog = ref(false);
const currentComponent = ref('') const currentComponent = ref('')
@@ -27,20 +28,20 @@ const componentsMap = {
LogoPicker, LogoPicker,
Socials, Socials,
ColorsPicker, ColorsPicker,
About,
CreateCreator CreateCreator
}; };
async function requestAccept(creatorName) { async function requestAccept(creatorName) {
const userProfileStore = useUserProfileStore()
const client = useClient() const client = useClient()
const response = await client.post( const response = await client.post(
'/api/creators', '/api/creators',
{ {
'creatorId': userStore.user.id, 'creatorId': userProfileStore.value.id,
'name': creatorName 'name': creatorName
}) })
if (response.status >= 200 && response.status < 300) { if (response.status >= 200 && response.status < 300) {
await userStore.fetchCurrentCreatorProfile() await creatorProfileStore.fetchCurrentCreatorProfile()
dialog.value = false dialog.value = false
} else { } else {
console.log(`An issue while creating the creator: ${response.statusText}`) 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 :style="{ borderRadius: '25px', border: '3px solid rgb(159, 76, 173)' }">
<v-card-text> <v-card-text>
<component :is="currentComponent" <component :is="currentComponent"
:creator="userStore.creator" :creator="creatorProfileStore.value"
:colorName="colorToEdit" :colorName="colorToEdit"
@closeRequested="closeDialog" @closeRequested="closeDialog"
@requestAccept="requestAccept" @requestAccept="requestAccept"
@@ -84,49 +85,29 @@ const closeDialog = () => {
<div class="flex flex-col items-center w-full"> <div class="flex flex-col items-center w-full">
<h1 class="uppercase pb-5 text-2xl"> <h1 class="uppercase pb-5 text-2xl">
<v-icon class="mr-2">mdi-file-edit-outline</v-icon> <v-icon class="mr-2">mdi-file-edit-outline</v-icon>
{{$t('creatorinfopage.pageinformation')}} {{ $t('creatorinfopage.pageinformation') }}
</h1> </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"> <div class="my-10 border rounded-2xl">
<div class="py-5 uppercase ml-4">{{$t('creatorinfopage.informations')}}</div> <div class="py-5 uppercase ml-4">{{ $t('creatorinfopage.informations') }}</div>
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
<button <button
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"> 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-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-auto text-left pr-6 capitalize">{{ creatorProfileStore.value.name }}</span>
<span class="flex-none"> <span class="flex-none">
<v-icon>mdi-chevron-right</v-icon> <v-icon>mdi-chevron-right</v-icon>
</span> </span>
</button> </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-none">
<v-icon>mdi-chevron-right</v-icon>
</span>
</button>
</div> </div>
</div> </div>
<div class="border rounded-2xl"> <div class="border rounded-2xl">
<div class="py-5 uppercase ml-4">{{$t('creatorinfopage.banner&profile')}}</div> <div class="py-5 uppercase ml-4">{{ $t('creatorinfopage.banner&profile') }}</div>
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
<button <button
@@ -168,7 +149,7 @@ const closeDialog = () => {
@click="openDialog('ColorsPicker', 'accent')" @click="openDialog('ColorsPicker', 'accent')"
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"> 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-circle-outline</v-icon></span> <span class="pa-2 min-w-32 text-left"><v-icon>mdi-circle-outline</v-icon></span>
<span class="flex-auto text-left pr-6">{{$t('creatorinfopage.borderpicturecolor')}}</span> <span class="flex-auto text-left pr-6">{{ $t('creatorinfopage.borderpicturecolor') }}</span>
<span class="flex-none"> <span class="flex-none">
<v-icon>mdi-eyedropper-variant</v-icon> <v-icon>mdi-eyedropper-variant</v-icon>
</span> </span>
@@ -178,7 +159,7 @@ const closeDialog = () => {
@click="openDialog('ColorsPicker', 'menu')" @click="openDialog('ColorsPicker', 'menu')"
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"> 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="flex-none pa-2 min-w-32 text-left"> <v-icon>mdi-menu</v-icon></span> <span class="flex-none pa-2 min-w-32 text-left"> <v-icon>mdi-menu</v-icon></span>
<span class="flex-auto text-left pr-6">{{$t('creatorinfopage.menucolor')}}</span> <span class="flex-auto text-left pr-6">{{ $t('creatorinfopage.menucolor') }}</span>
<span class="flex-none"> <span class="flex-none">
<v-icon>mdi-eyedropper-variant</v-icon> <v-icon>mdi-eyedropper-variant</v-icon>
</span> </span>
@@ -188,14 +169,14 @@ const closeDialog = () => {
<div class="mt-10 border rounded-2xl"> <div class="mt-10 border rounded-2xl">
<div class="py-5 uppercase ml-4">{{$t('creatorinfopage.socialnetwork')}}</div> <div class="py-5 uppercase ml-4">{{ $t('creatorinfopage.socialnetwork') }}</div>
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
<button <button
@click="openDialog('Socials')" @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"> 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="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"> <span class="flex-none">
<v-icon>mdi-chevron-right</v-icon> <v-icon>mdi-chevron-right</v-icon>
</span> </span>
@@ -205,7 +186,7 @@ const closeDialog = () => {
@click="openDialog('Socials')" @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"> 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-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"> <span class="flex-none">
<v-icon>mdi-chevron-right</v-icon> <v-icon>mdi-chevron-right</v-icon>
</span> </span>
@@ -214,9 +195,10 @@ const closeDialog = () => {
<button <button
@click="openDialog('Socials')" @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"> 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" <span class="flex-none pa-2 w-6 h-6 text-left">
height="23px"></span> <XIcon></XIcon>
<span class="flex-auto text-left pr-6">{{ userStore.creator.socials.xUrl }}</span> </span>
<span class="flex-auto text-left pr-6">{{ creatorProfileStore.value.socials.xUrl }}</span>
<span class="flex-none"> <span class="flex-none">
<v-icon>mdi-chevron-right</v-icon> <v-icon>mdi-chevron-right</v-icon>
</span> </span>
@@ -226,7 +208,7 @@ const closeDialog = () => {
@click="openDialog('Socials')" @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 "> 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="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"> <span class="flex-none">
<v-icon>mdi-chevron-right</v-icon> <v-icon>mdi-chevron-right</v-icon>
</span> </span>
@@ -236,9 +218,9 @@ const closeDialog = () => {
@click="openDialog('Socials')" @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 "> 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"> <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>
<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"> <span class="flex-none">
<v-icon>mdi-chevron-right</v-icon> <v-icon>mdi-chevron-right</v-icon>
</span> </span>
@@ -248,7 +230,7 @@ const closeDialog = () => {
@click="openDialog('Socials')" @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 "> 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="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"> <span class="flex-none">
<v-icon>mdi-chevron-right</v-icon> <v-icon>mdi-chevron-right</v-icon>
</span> </span>
@@ -258,7 +240,7 @@ const closeDialog = () => {
@click="openDialog('Socials')" @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 "> 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="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"> <span class="flex-none">
<v-icon>mdi-chevron-right</v-icon> <v-icon>mdi-chevron-right</v-icon>
</span> </span>
@@ -268,7 +250,7 @@ const closeDialog = () => {
@click="openDialog('Socials')" @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 "> 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="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"> <span class="flex-none">
<v-icon>mdi-chevron-right</v-icon> <v-icon>mdi-chevron-right</v-icon>
</span> </span>

View File

@@ -1,4 +1,5 @@
<script setup> <script setup>
import XIcon from '@/assets/icons/x.svg'
import {ref} from 'vue' import {ref} from 'vue'
import {useClient} from "@/plugins/api.js"; import {useClient} from "@/plugins/api.js";
@@ -56,6 +57,14 @@ const cancel = () => {
</script> </script>
<style scoped>
.icon {
width: 40px;
height: 40px;
fill: #1976d2;
}
</style>
<template> <template>
<div class="pb-5 text-2xl">Reseaux Sociaux</div> <div class="pb-5 text-2xl">Reseaux Sociaux</div>
<div class="flex flex-row align-center"> <div class="flex flex-row align-center">
@@ -87,56 +96,62 @@ const cancel = () => {
label="Lien LinkedIn" label="Lien LinkedIn"
outlined outlined
></v-text-field> ></v-text-field>
</div> </div>
<div class="flex flex-row align-center"> <div class="flex flex-row align-center">
<v-icon class="mb-5 mr-2">mdi-reddit</v-icon> <v-icon class="mb-5 mr-2">mdi-reddit</v-icon>
<v-text-field <v-text-field
variant="outlined" variant="outlined"
v-model="redditUrl" v-model="redditUrl"
label="Lien Reddit" label="Lien Reddit"
outlined outlined
></v-text-field> ></v-text-field>
</div> </div>
<div class="flex flex-row align-center"> <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">
<v-text-field <XIcon></XIcon>
variant="outlined" </div>
v-model="tikTokUrl" <v-text-field
label="Lien TikTok" variant="outlined"
outlined v-model="tikTokUrl"
></v-text-field> label="Lien TikTok"
outlined
></v-text-field>
</div> </div>
<div class="flex flex-row align-center"> <div class="flex flex-row align-center">
<v-icon class="mb-5 mr-2">mdi-web</v-icon> <v-icon class="mb-5 mr-2">mdi-web</v-icon>
<v-text-field
variant="outlined"
v-model="websiteUrl"
label="Lien site web"
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">
<v-text-field <v-text-field
variant="outlined" variant="outlined"
v-model="xUrl" v-model="websiteUrl"
label="Lien X" label="Lien site web"
outlined outlined
></v-text-field> ></v-text-field>
</div>
<div class="flex flex-row align-center">
<div class="w-6 h-6 mb-5 mr-2">
<XIcon class="icon"></XIcon>
</div>
<v-text-field
variant="outlined"
v-model="xUrl"
label="Lien X"
outlined
></v-text-field>
</div> </div>
<div class="flex flex-row align-center"> <div class="flex flex-row align-center">
<v-icon class="mb-5 mr-2">mdi-youtube</v-icon> <v-icon class="mb-5 mr-2">mdi-youtube</v-icon>
<v-text-field <v-text-field
variant="outlined" variant="outlined"
v-model="youtubeUrl" v-model="youtubeUrl"
label="Lien Youtube" label="Lien Youtube"
outlined outlined
></v-text-field> ></v-text-field>
</div> </div>
<div class="flex justify-end space-x-4"> <div class="flex justify-end space-x-4">

View File

@@ -1,25 +1,26 @@
import { fileURLToPath, URL } from 'node:url' import {fileURLToPath, URL} from 'node:url'
import { defineConfig, loadEnv } from 'vite' import {defineConfig, loadEnv} from 'vite'
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
import svgLoader from 'vite-svg-loader'
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig(({ mode }) => { export default defineConfig(({mode}) => {
// Load environment variables based on the mode // Load environment variables based on the mode
const env = loadEnv(mode, process.cwd(), '') const env = loadEnv(mode, process.cwd(), '')
return { return {
plugins: [ plugins: [
vue(), vue(),
], svgLoader()
resolve: { ],
alias: { resolve: {
'@': fileURLToPath(new URL('./src', import.meta.url)) alias: {
} '@': fileURLToPath(new URL('./src', import.meta.url))
}, }
define: { },
// Define a global constant __APP_ENV__ based on loaded environment variables define: {
VITE_API_URL: JSON.stringify(env.VITE_API_URL), // Define a global constant __APP_ENV__ based on loaded environment variables
VITE_STRIPE_API_KEY: JSON.stringify(env.VITE_STRIPE_API_KEY) VITE_API_URL: JSON.stringify(env.VITE_API_URL),
VITE_STRIPE_API_KEY: JSON.stringify(env.VITE_STRIPE_API_KEY)
}
} }
}
}) })