feat: share badge on social media
This commit is contained in:
90
frontend/src/pages/Badge.vue
Normal file
90
frontend/src/pages/Badge.vue
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="badge.doc">
|
||||||
|
<div class="p-5 flex flex-col items-center mt-40">
|
||||||
|
<div class="text-3xl font-semibold">
|
||||||
|
{{ badge.doc.title }}
|
||||||
|
</div>
|
||||||
|
<img :src="badge.doc.image" :alt="badge.doc.title" class="h-60 mt-2" />
|
||||||
|
<div class="text-lg">
|
||||||
|
{{
|
||||||
|
__('This badge has been awarded to {0} on {1}.').format(
|
||||||
|
userName,
|
||||||
|
dayjs(issuedOn.data?.issued_on).format('DD MMM YYYY')
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
<div class="text-lg mt-2">
|
||||||
|
{{ badge.doc.description }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { createDocumentResource, createResource, Breadcrumbs } from 'frappe-ui'
|
||||||
|
import { computed, inject } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
const allUsers = inject('$allUsers')
|
||||||
|
const dayjs = inject('$dayjs')
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
badgeName: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const badge = createDocumentResource({
|
||||||
|
doctype: 'LMS Badge',
|
||||||
|
name: props.badgeName,
|
||||||
|
})
|
||||||
|
|
||||||
|
const userName = computed(() => {
|
||||||
|
const user = Object.values(allUsers.data).find(
|
||||||
|
(user) => user.name === props.email
|
||||||
|
)
|
||||||
|
return user ? user.full_name : props.email
|
||||||
|
})
|
||||||
|
|
||||||
|
const issuedOn = createResource({
|
||||||
|
url: 'frappe.client.get_value',
|
||||||
|
makeParams(values) {
|
||||||
|
return {
|
||||||
|
doctype: 'LMS Badge Assignment',
|
||||||
|
filters: {
|
||||||
|
member: props.email,
|
||||||
|
badge: props.badgeName,
|
||||||
|
},
|
||||||
|
fieldname: 'issued_on',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess(data) {
|
||||||
|
if (!data.issued_on) {
|
||||||
|
router.push({ name: 'Courses' })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
auto: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const breadcrumbs = computed(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: 'Badges',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: badge.doc.title,
|
||||||
|
route: {
|
||||||
|
name: 'Badge',
|
||||||
|
params: {
|
||||||
|
badge: badge.doc.name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
<div class="grid grid-cols-5 gap-4">
|
<div class="grid grid-cols-5 gap-4">
|
||||||
<div v-if="badges.data" v-for="badge in badges.data">
|
<div v-if="badges.data" v-for="badge in badges.data">
|
||||||
<Popover trigger="hover" leaveDelay="0.01">
|
<Popover trigger="hover" :leaveDelay="Number(0.01)">
|
||||||
<template #target>
|
<template #target>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<img
|
<img
|
||||||
@@ -61,15 +61,26 @@
|
|||||||
<span class="text-xs text-gray-700 font-medium mb-1">
|
<span class="text-xs text-gray-700 font-medium mb-1">
|
||||||
{{ __('Share on') }}:
|
{{ __('Share on') }}:
|
||||||
</span>
|
</span>
|
||||||
<Button
|
<div class="flex items-center space-x-2">
|
||||||
variant="outline"
|
<Button
|
||||||
size="sm"
|
variant="outline"
|
||||||
@click="shareOnLinkedIn(badge)"
|
size="sm"
|
||||||
>
|
@click="shareOnSocial(badge, 'LinkedIn')"
|
||||||
<template #icon>
|
>
|
||||||
<LinkedinIcon class="h-3 w-3 stroke-1.5" />
|
<template #icon>
|
||||||
</template>
|
<LinkedinIcon
|
||||||
</Button>
|
class="h-3 w-3 stroke-1.5 text-gray-700"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
@click="shareOnSocial(badge, 'Twitter')"
|
||||||
|
>
|
||||||
|
<Twitter class="h-3 w-3 stroke-1.5 text-gray-700" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -82,7 +93,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { inject } from 'vue'
|
import { inject } from 'vue'
|
||||||
import { createResource, Popover, Button } from 'frappe-ui'
|
import { createResource, Popover, Button } from 'frappe-ui'
|
||||||
import { X, LinkedinIcon } from 'lucide-vue-next'
|
import { X, LinkedinIcon, Twitter } from 'lucide-vue-next'
|
||||||
import { sessionStore } from '@/stores/session'
|
import { sessionStore } from '@/stores/session'
|
||||||
|
|
||||||
const dayjs = inject('$dayjs')
|
const dayjs = inject('$dayjs')
|
||||||
@@ -117,16 +128,22 @@ const badges = createResource({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const shareOnLinkedIn = (badge) => {
|
const shareOnSocial = (badge, medium) => {
|
||||||
const url = encodeURIComponent(window.location.href)
|
let shareUrl
|
||||||
const image = url.split('/lms')[0] + badge.badge_image
|
const url = encodeURIComponent(
|
||||||
console.log(image)
|
`${window.location.origin}/badges/${badge.badge}/${props.profile.data?.email}`
|
||||||
|
)
|
||||||
const summary = `I am happy to announce that I earned the ${
|
const summary = `I am happy to announce that I earned the ${
|
||||||
badge.badge
|
badge.badge
|
||||||
} badge on ${dayjs(badge.issued_on).format('DD MMM YYYY')} at ${
|
} badge on ${dayjs(badge.issued_on).format('DD MMM YYYY')} at ${
|
||||||
branding.data?.brand_name
|
branding.data?.brand_name
|
||||||
}. Here's the badge I earned: ${image}`
|
}.`
|
||||||
const shareUrl = `https://www.linkedin.com/shareArticle/?url=${url}&text=${summary}&submitted-image-url=${badge.badge_image}`
|
|
||||||
|
if (medium == 'LinkedIn')
|
||||||
|
shareUrl = `https://www.linkedin.com/shareArticle?mini=true&url=${url}&text=${summary}`
|
||||||
|
else if (medium == 'Twitter')
|
||||||
|
shareUrl = `https://twitter.com/intent/tweet?text=${summary}&url=${url}`
|
||||||
|
|
||||||
window.open(shareUrl, '_blank')
|
window.open(shareUrl, '_blank')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -135,6 +135,12 @@ const routes = [
|
|||||||
name: 'Notifications',
|
name: 'Notifications',
|
||||||
component: () => import('@/pages/Notifications.vue'),
|
component: () => import('@/pages/Notifications.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/badges/:badgeName/:email',
|
||||||
|
name: 'Badge',
|
||||||
|
component: () => import('@/pages/Badge.vue'),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
let router = createRouter({
|
let router = createRouter({
|
||||||
@@ -154,7 +160,8 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
isLoggedIn &&
|
isLoggedIn &&
|
||||||
(to.name == 'Lesson' ||
|
(to.name == 'Lesson' ||
|
||||||
to.name == 'Batch' ||
|
to.name == 'Batch' ||
|
||||||
to.name == 'Notifications')
|
to.name == 'Notifications' ||
|
||||||
|
to.name == 'Badge')
|
||||||
) {
|
) {
|
||||||
await allUsers.reload()
|
await allUsers.reload()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,7 +98,7 @@
|
|||||||
"link_fieldname": "badge"
|
"link_fieldname": "badge"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2024-05-14 14:46:13.644382",
|
"modified": "2024-05-27 17:25:55.399830",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Badge",
|
"name": "LMS Badge",
|
||||||
@@ -116,6 +116,15 @@
|
|||||||
"role": "System Manager",
|
"role": "System Manager",
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "All",
|
||||||
|
"share": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sort_field": "creation",
|
"sort_field": "creation",
|
||||||
|
|||||||
@@ -15,10 +15,10 @@
|
|||||||
<meta name="twitter:title" content="{{ meta.title }}" />
|
<meta name="twitter:title" content="{{ meta.title }}" />
|
||||||
<meta name="twitter:image" content="{{ meta.image }}" />
|
<meta name="twitter:image" content="{{ meta.image }}" />
|
||||||
<meta name="twitter:description" content="{{ meta.description }}" />
|
<meta name="twitter:description" content="{{ meta.description }}" />
|
||||||
<script type="module" crossorigin src="/assets/lms/frontend/assets/index-DH4LPAyv.js"></script>
|
<script type="module" crossorigin src="/assets/lms/frontend/assets/index-BCIMZEe3.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="/assets/lms/frontend/assets/frappe-ui-Cdm1MNHD.js">
|
<link rel="modulepreload" crossorigin href="/assets/lms/frontend/assets/frappe-ui-D-bJ8Xgf.js">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/lms/frontend/assets/frappe-ui-B1gEXx4C.css">
|
<link rel="stylesheet" crossorigin href="/assets/lms/frontend/assets/frappe-ui-B1gEXx4C.css">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/lms/frontend/assets/index-CfA8Gbx1.css">
|
<link rel="stylesheet" crossorigin href="/assets/lms/frontend/assets/index-B_uDyhcC.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
|
|||||||
@@ -130,3 +130,20 @@ def get_meta(app_path):
|
|||||||
"keywords": f"{user.full_name}, {user.bio}",
|
"keywords": f"{user.full_name}, {user.bio}",
|
||||||
"link": f"/user/{username}",
|
"link": f"/user/{username}",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if re.match(r"^badges/.*/.*$", app_path):
|
||||||
|
badgeName = app_path.split("/")[1]
|
||||||
|
email = app_path.split("/")[2]
|
||||||
|
badge = frappe.db.get_value(
|
||||||
|
"LMS Badge",
|
||||||
|
badgeName,
|
||||||
|
["title", "image", "description"],
|
||||||
|
as_dict=True,
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"title": badge.title,
|
||||||
|
"image": badge.image,
|
||||||
|
"description": badge.description,
|
||||||
|
"keywords": f"{badge.title}, {badge.description}",
|
||||||
|
"link": f"/badges/{badgeName}/{email}",
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user