WIP
This commit is contained in:
@@ -1,12 +1,21 @@
|
||||
<template>
|
||||
<div class="flex flex-row">
|
||||
|
||||
<div class="px-4"
|
||||
:style="{ backgroundColor: brandingStore.colors.primary, color: brandingStore.colors.onPrimary}">
|
||||
|
||||
<div class="px-4"
|
||||
:style="{ backgroundColor: brandingStore.colors.primary, color: brandingStore.colors.onPrimary}">
|
||||
<h1>TEST</h1>
|
||||
<p>GET ME AN EDITOR NOW!</p>
|
||||
|
||||
<p>MODE: {{ envv }}</p>
|
||||
<p>VITE_API_URL: {{ enva }}</p>
|
||||
<p>VITE_STRIPE_API_KEY: {{ envb }}</p>
|
||||
|
||||
<donation-button :creator-id="creatorId"
|
||||
:creator-name="creatorName"
|
||||
:on-success-url="successUrl"
|
||||
:on-cancelled-url="cancelledUrl"
|
||||
></donation-button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
@@ -14,9 +23,24 @@
|
||||
<script setup>
|
||||
|
||||
import {useBrandingStore} from "@/stores/brandingStore.js";
|
||||
import DonationButton from "@/views/creators/DonationButton.vue";
|
||||
import {computed} from "vue";
|
||||
import {useRoute, useRouter} from "vue-router";
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const baseUrl = window.location.origin;
|
||||
const creatorSlug = route.params.creator_slug || route.path.split('/')[1];
|
||||
const successUrl = `${baseUrl}/${creatorSlug}`
|
||||
const cancelledUrl = `${baseUrl}/${creatorSlug}`
|
||||
|
||||
const brandingStore = useBrandingStore()
|
||||
const envv = import.meta.env.MODE
|
||||
const enva = import.meta.env.VITE_API_URL
|
||||
const envb = import.meta.env.VITE_STRIPE_API_KEY
|
||||
|
||||
const creatorId = computed(() => brandingStore.value.id)
|
||||
const creatorName = computed(() => brandingStore.value.name)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
<v-card-text>
|
||||
<v-text-field
|
||||
v-model="tipAmount"
|
||||
v-model="tipAmountInDollars"
|
||||
type="number"
|
||||
:min="0"
|
||||
class="p-2"
|
||||
@@ -67,8 +67,9 @@
|
||||
<div id="checkout">
|
||||
</div>
|
||||
|
||||
<v-spacer></v-spacer>
|
||||
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn block class="ma-auto"
|
||||
style="width: 200px;"
|
||||
@click="closeDialog()">Annuler
|
||||
@@ -89,6 +90,10 @@ import {useBrandingStore} from "@/stores/brandingStore.js";
|
||||
const brandingStore = useBrandingStore()
|
||||
|
||||
const props = defineProps({
|
||||
creatorId: {default: 'missing-creator-id', required: true},
|
||||
creatorName: {default: 'missing-creator-name', required: true},
|
||||
onSuccessUrl: {default: 'missing-on-success-u', required: true},
|
||||
onCancelledUrl: {default: 'missing-on-cancelled-url', required: true},
|
||||
iconColorClass: {default: 'text-black'}
|
||||
});
|
||||
|
||||
@@ -105,7 +110,7 @@ function closeDonationDialog() {
|
||||
|
||||
const isPaymentDialogActive = ref(false);
|
||||
|
||||
const tipAmount = ref(0);
|
||||
const tipAmountInDollars = ref(0);
|
||||
const tipMessage = ref("");
|
||||
|
||||
let stripe = null;
|
||||
@@ -116,23 +121,21 @@ onMounted(async () => {
|
||||
stripe = await loadStripe(import.meta.env.VITE_STRIPE_API_KEY);
|
||||
});
|
||||
|
||||
const fetchClientSecret = async () => {
|
||||
const clientSecret = await createCheckoutSession();
|
||||
return clientSecret;
|
||||
};
|
||||
|
||||
|
||||
async function createCheckoutSession() {
|
||||
const client = useClient()
|
||||
let clientSecret = await client.post('/api/Stripe', {
|
||||
amount: (tipAmount.value * 100),
|
||||
tipMessage: tipMessage.value,
|
||||
creatorId: props.creatorId
|
||||
|
||||
});
|
||||
let clientSecret = await client.post(
|
||||
`/api/tips`,
|
||||
{
|
||||
amount: tipAmountInDollars.value * 100,
|
||||
currency: 'CAD',
|
||||
message: tipMessage.value,
|
||||
creatorId: props.creatorId,
|
||||
checkoutSuccessUrl: props.onSuccessUrl,
|
||||
checkoutCancelledUrl: props.onCancelledUrl
|
||||
});
|
||||
|
||||
let secret = clientSecret["data"];
|
||||
return secret;
|
||||
return clientSecret.data;
|
||||
}
|
||||
|
||||
function closeDialog() {
|
||||
@@ -145,11 +148,10 @@ function closeDialog() {
|
||||
async function goPay() {
|
||||
isPaymentDialogActive.value = true;
|
||||
|
||||
checkout = await stripe.initEmbeddedCheckout({
|
||||
fetchClientSecret,
|
||||
});
|
||||
const response = await createCheckoutSession()
|
||||
|
||||
await checkout.mount('#checkout');
|
||||
// Redirect to the Stripe Checkout page
|
||||
window.location.href = response.stripeCheckoutUrl
|
||||
}
|
||||
|
||||
function preventNonNumeric(event) {
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
<script setup>
|
||||
import {computed, ref} from "vue";
|
||||
import {useSubscriptionStore} from "@/stores/subscriptionStore.js";
|
||||
import {useBrandingStore} from "@/stores/brandingStore.js";
|
||||
|
||||
const brandingStore = useBrandingStore()
|
||||
const subscriptionStore = useSubscriptionStore();
|
||||
|
||||
const isSubscribe = computed(() => !subscriptionStore.isSubscribeTo(brandingStore.value.id));
|
||||
|
||||
function subscribeToCreator() {
|
||||
subscriptionStore.subscribeTo(brandingStore.value.id);
|
||||
}
|
||||
|
||||
// Référence pour contrôler l'affichage du modal
|
||||
const showUnsubscribeModal = ref(false);
|
||||
|
||||
function unsubscribeFromCreator() {
|
||||
subscriptionStore.unsubscribeFrom(brandingStore.value.id);
|
||||
// Fermer le modal après désabonnement
|
||||
showUnsubscribeModal.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<template v-if="isSubscribe">
|
||||
<v-btn
|
||||
:style="{
|
||||
width: '150px',
|
||||
height: '28px',
|
||||
backgroundColor: brandingStore.colors.secondary,
|
||||
color: 'white',
|
||||
borderRadius: '8px 0 0 8px',
|
||||
padding: '10px 24px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
transition: 'background-color 0.3s ease'
|
||||
}"
|
||||
@click="subscribeToCreator"
|
||||
>
|
||||
<div>Follow</div>
|
||||
</v-btn>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<v-btn
|
||||
:style="{
|
||||
width: '150px',
|
||||
height: '28px',
|
||||
backgroundColor: brandingStore.colors.secondary,
|
||||
color: 'white',
|
||||
borderRadius: '8px 0 0 8px',
|
||||
padding: '10px 24px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
transition: 'background-color 0.3s ease'
|
||||
}"
|
||||
@click="showUnsubscribeModal = true"
|
||||
>
|
||||
<div>{{ $t('subscribebutton.unsubscribe') }}</div>
|
||||
</v-btn>
|
||||
</template>
|
||||
|
||||
<v-dialog v-model="showUnsubscribeModal" max-width="500">
|
||||
<v-card class="text-center rounded-xl"
|
||||
:style="{ border: `3px solid ${brandingStore.colors.secondary}` }">
|
||||
|
||||
<div class="flex items-center justify-between py-4 text-2xl font-bold border-b mb-2">
|
||||
<div class="flex-1 text-center">
|
||||
Déabonnement
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-card-title>Confirmation</v-card-title>
|
||||
<v-card-text>Êtes-vous sûr de vouloir vous désabonner ?</v-card-text>
|
||||
<v-card-actions class="justify-center px-4 pb-4">
|
||||
<v-btn text class="flex-grow-1" variant="outlined"
|
||||
:style="{ backgroundColor: 'rgba(255, 255, 255, 0.1)', color: 'rgba(0, 0, 0, 0.4)' }"
|
||||
@click="unsubscribeFromCreator">Oui
|
||||
</v-btn>
|
||||
|
||||
<v-btn class="flex-grow-1"
|
||||
:style="{ borderColor: brandingStore.colors.secondary, color: brandingStore.colors.secondary }"
|
||||
variant="outlined"
|
||||
@click="showUnsubscribeModal = false">
|
||||
<div :style="{ color: brandingStore.colors.secondary }">Non</div>
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
@@ -2,14 +2,17 @@
|
||||
import {useSubscriptionStore} from "@/stores/subscriptionStore.js";
|
||||
import {computed, ref} from "vue";
|
||||
import {useBrandingStore} from "@/stores/brandingStore.js";
|
||||
import {useRouter} from "vue-router";
|
||||
|
||||
const router = useRouter()
|
||||
const brandingStore = useBrandingStore()
|
||||
const subscriptionStore = useSubscriptionStore()
|
||||
|
||||
const isSubscribe = computed(() => !subscriptionStore.isSubscribeTo(brandingStore.value.id));
|
||||
|
||||
function subscribeToCreator() {
|
||||
subscriptionStore.subscribeTo(brandingStore.value.id);
|
||||
const target = `@${brandingStore.currentBrand}/subscription`;
|
||||
router.push(target)
|
||||
}
|
||||
|
||||
// Référence pour contrôler l'affichage du modal
|
||||
@@ -30,7 +33,7 @@ function unsubscribeFromCreator() {
|
||||
height: '28px',
|
||||
backgroundColor: brandingStore.colors.secondary,
|
||||
color: 'white',
|
||||
borderRadius: '0 8px 8px 0',
|
||||
borderRadius: '8px',
|
||||
padding: '10px 24px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
@@ -46,17 +49,17 @@ function unsubscribeFromCreator() {
|
||||
<template v-else>
|
||||
<v-btn
|
||||
:style="{
|
||||
width: '150px',
|
||||
height: '28px',
|
||||
backgroundColor: brandingStore.colors.secondary,
|
||||
color: 'white',
|
||||
borderRadius: '0 8px 8px 0',
|
||||
padding: '10px 24px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
transition: 'background-color 0.3s ease'
|
||||
}"
|
||||
width: '150px',
|
||||
height: '28px',
|
||||
backgroundColor: brandingStore.colors.secondary,
|
||||
color: 'white',
|
||||
borderRadius: '0 8px 8px 0',
|
||||
padding: '10px 24px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
transition: 'background-color 0.3s ease'
|
||||
}"
|
||||
@click="showUnsubscribeModal = true"
|
||||
>
|
||||
<div>{{ $t('subscribebutton.unsubscribe') }}</div>
|
||||
|
||||
@@ -13,7 +13,7 @@ const subscriptionStore = useSubscriptionStore()
|
||||
|
||||
<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' "
|
||||
:src="subscription.creatorPortraitUrl"
|
||||
alt="Profile Image"
|
||||
class="rounded-full mx-2"
|
||||
width="32px"
|
||||
|
||||
@@ -1,82 +1,90 @@
|
||||
<script setup>
|
||||
import { useBrandingStore } from "@/stores/brandingStore.js";
|
||||
import { ref } from 'vue';
|
||||
import {useBrandingStore} from "@/stores/brandingStore.js";
|
||||
import {ref, onMounted} from 'vue';
|
||||
import {useClient} from "@/plugins/api.js";
|
||||
import {useRoute, useRouter} from "vue-router";
|
||||
|
||||
const branding = useBrandingStore();
|
||||
const selectedTier = ref(null);
|
||||
const tiers = ref([
|
||||
{
|
||||
id: 1,
|
||||
name: 'Les Visionnaires',
|
||||
description: 'Container blablal HtmlContainer blablal HtmlContainer blablalContainer blablal HtmlContainer blablal HtmlContainer blablalContainer blablal HtmlContainer blablal HtmlContainer blablalContainer blablal HtmlContainer blablal HtmlContainer blablalContainer blablal HtmlContainer blablal HtmlContainer blablalContainer blablal HtmlContainer blablal HtmlContainer blablalContainer blablal HtmlContainer blablal HtmlContainer blablalContainer blablal HtmlContainer blablal HtmlContainer blablalContainer blablal HtmlContainer blablal HtmlContainer blablal...',
|
||||
price: 15,
|
||||
},
|
||||
]);
|
||||
const router = useRouter()
|
||||
const brandingStore = useBrandingStore();
|
||||
|
||||
const tiers = ref([]);
|
||||
|
||||
// Fetch tiers from API
|
||||
async function fetchTiers() {
|
||||
const client = useClient()
|
||||
const response = await client.get(
|
||||
`/api/membership/tiers/${brandingStore.value.id}`
|
||||
);
|
||||
tiers.value = response.data;
|
||||
}
|
||||
|
||||
// Fetch tiers when the component is mounted
|
||||
onMounted(() => {
|
||||
fetchTiers();
|
||||
});
|
||||
|
||||
// Colors
|
||||
|
||||
const onPrimary = { color: branding.colors.onPrimary}
|
||||
const Primary = { backgroundColor: branding.colors.primary}
|
||||
const onPrimary = {color: brandingStore.colors.onPrimary}
|
||||
const Primary = {backgroundColor: brandingStore.colors.primary}
|
||||
|
||||
const onSecondaryColor = { color: branding.colors.onSecondary}
|
||||
const secondaryColor = { backgroundColor: branding.colors.secondary}
|
||||
const onSecondaryColor = {color: brandingStore.colors.onSecondary}
|
||||
const secondaryColor = {backgroundColor: brandingStore.colors.secondary}
|
||||
|
||||
function selectTier(id) {
|
||||
selectedTier.value = id;
|
||||
const route = useRoute()
|
||||
const baseUrl = window.location.origin;
|
||||
const creatorSlug = route.params.creator_slug || route.path.split('/')[1];
|
||||
const successUrl = `${baseUrl}/${creatorSlug}/content`
|
||||
const cancelledUrl = `${baseUrl}/${creatorSlug}`
|
||||
|
||||
async function doSubscribe(tier) {
|
||||
try {
|
||||
const client = useClient()
|
||||
const response = await client.post(
|
||||
`/api/membership/subscribe`,
|
||||
{
|
||||
creatorId: brandingStore.value.id,
|
||||
tierId: tier.id,
|
||||
checkoutSuccessUrl: successUrl, // TODO: ensure the success-url will insert subscription
|
||||
checkoutCancelledUrl: cancelledUrl
|
||||
})
|
||||
|
||||
window.location.href = response.data.stripeCheckoutUrl;
|
||||
} catch (error) {
|
||||
console.error("Error loading subscriptions:", error);
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container class="d-flex justify-center">
|
||||
<v-row justify="center">
|
||||
<!-- Tiers -->
|
||||
|
||||
<v-col
|
||||
:cols="12 / Math.min(tiers.length, 3)"
|
||||
md="4"
|
||||
v-for="tier in tiers"
|
||||
:key="tier.id"
|
||||
>
|
||||
<div class="bg-white shadow-2xl rounded-2xl">
|
||||
<v-img src="/images/hutopymedia/loginpage/loginhutopy.png" class="rounded-t-2xl"></v-img>
|
||||
<div class="pa-6" :style="[Primary, onPrimary]">
|
||||
<v-card-title class="text-h4 text-center py-4 ">{{ tier.name }}</v-card-title>
|
||||
|
||||
<div class="text-justify">{{ tier.description }}</div>
|
||||
</div>
|
||||
<div class="text-center bg-fuchsia-800 py-10" :style="[Primary]">
|
||||
<v-btn
|
||||
class="mx-auto"
|
||||
width="200px"
|
||||
@click="selectTier(tier.id)"
|
||||
:style="{
|
||||
backgroundColor: selectedTier === tier.id ? branding.colors.background : branding.colors.secondary,
|
||||
color: selectedTier === tier.id ? branding.colors.onPrimary : branding.colors.onSecondary
|
||||
}"
|
||||
>
|
||||
{{ selectedTier === tier.id ? 'Sélectionné' : 'Choisir' }}
|
||||
</v-btn>
|
||||
|
||||
<v-btn @click="doSubscribe(tier)" variant="text">
|
||||
<div class="bg-white shadow-2xl rounded-2xl">
|
||||
<v-img src="/images/hutopymedia/loginpage/loginhutopy.png" class="rounded-t-2xl"></v-img>
|
||||
<div class="pa-6" :style="[Primary, onPrimary]">
|
||||
<v-card-title class="text-h4 text-center py-4 ">{{ tier.name }}</v-card-title>
|
||||
<div class="text-justify">{{ tier.description }}</div>
|
||||
</div>
|
||||
|
||||
<v-card-text class="text-center rounded-b-2xl" :style="[secondaryColor, onSecondaryColor]">
|
||||
<span class="text-h5">{{ tier.price }} $ / par mois</span>
|
||||
</v-card-text>
|
||||
</div>
|
||||
<v-card-text class="text-center rounded-b-2xl" :style="[secondaryColor,onSecondaryColor]" >
|
||||
<span class="text-h5">{{ tier.price }} $ / par mois </span>
|
||||
</v-card-text>
|
||||
</div>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
<v-card-actions class="d-flex justify-center mt-10">
|
||||
<v-btn
|
||||
:style="{ backgroundColor: branding.colors.secondary, color: branding.colors.onBackground }"
|
||||
width="250px"
|
||||
variant="elevated"
|
||||
>
|
||||
Payer
|
||||
</v-btn>
|
||||
|
||||
|
||||
</v-card-actions>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@@ -85,7 +93,7 @@ function selectTier(id) {
|
||||
}
|
||||
|
||||
.dotted-border {
|
||||
border: 2px dotted ;
|
||||
border: 2px dotted;
|
||||
padding: 1px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
</div>
|
||||
<!-- Follow and Subscribe Buttons -->
|
||||
<div class="flex flex-row space-x-1 justify-center mt-3 mb-2">
|
||||
<follow-button></follow-button>
|
||||
<subscribe-button></subscribe-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -150,7 +149,6 @@
|
||||
<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()
|
||||
|
||||
Reference in New Issue
Block a user