This commit is contained in:
2024-10-22 16:40:50 -04:00
parent 18e0b5ad03
commit 14c97c35dc
13 changed files with 141 additions and 1219 deletions

View File

@@ -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>

View File

@@ -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) {

View File

@@ -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>

View File

@@ -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>

View File

@@ -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"

View File

@@ -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>

View File

@@ -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()