mirror of
https://github.com/ArthurDanjou/website.git
synced 2026-01-14 12:14:42 +01:00
add oauth and create suggestion
This commit is contained in:
4
src/auth.d.ts
vendored
4
src/auth.d.ts
vendored
@@ -1,7 +1,9 @@
|
||||
declare module '#auth-utils' {
|
||||
interface UserSession {
|
||||
user: {
|
||||
username: string
|
||||
email: string,
|
||||
username: string,
|
||||
picture: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,5 +16,6 @@ const getColor = computed(() => appConfig.ui.primary)
|
||||
<NuxtPage />
|
||||
<Footer />
|
||||
</main>
|
||||
<UNotifications />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,31 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import { providers } from '~~/types'
|
||||
|
||||
useHead({
|
||||
title: 'Sign my guestbook • Arthur Danjou',
|
||||
})
|
||||
|
||||
const providers = [
|
||||
{
|
||||
slug: 'github',
|
||||
label: 'Use Github',
|
||||
icon: 'i-ph-github-logo-bold',
|
||||
link: '/api/auth/github',
|
||||
color: 'black',
|
||||
},
|
||||
{
|
||||
slug: 'twitter',
|
||||
label: 'Use Twitter',
|
||||
icon: 'i-ph-twitter-logo-bold',
|
||||
link: '/api/auth/twitter',
|
||||
color: 'cyan',
|
||||
},
|
||||
{
|
||||
slug: 'google',
|
||||
label: 'Use Google',
|
||||
icon: 'i-ph-google-logo-bold',
|
||||
link: '/api/auth/google',
|
||||
color: 'red',
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import { useTalentsStore } from '~/store/talents'
|
||||
import { providers } from '~~/types'
|
||||
|
||||
useHead({
|
||||
title: 'Discover new talents • Arthur Danjou',
|
||||
})
|
||||
|
||||
const categories = ref<Array<{ label: string; slug: string }>>([{ label: 'All', slug: 'all' }])
|
||||
const categories = ref<Array<{ label: string, slug: string }>>([{ label: 'All', slug: 'all' }])
|
||||
const { getCategory, setCategory, isFavorite, toggleFavorite } = useTalentsStore()
|
||||
const { loggedIn, clear } = useUserSession()
|
||||
|
||||
const {
|
||||
data: talents,
|
||||
@@ -17,7 +19,7 @@ const {
|
||||
favorite: isFavorite,
|
||||
category: getCategory,
|
||||
},
|
||||
watch: [isFavorite, getCategory]
|
||||
watch: [isFavorite, getCategory],
|
||||
})
|
||||
|
||||
function isCategory(category: string) {
|
||||
@@ -28,12 +30,38 @@ const {
|
||||
data: getCategories,
|
||||
} = await useFetch('/api/categories', { method: 'GET' })
|
||||
|
||||
getCategories.value?.forEach(category => categories.value.push({ label: category.name, slug: category.slug }))
|
||||
getCategories.value?.forEach((category: any) => categories.value.push({ label: category.name, slug: category.slug }))
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
function getColor() {
|
||||
return `text-${appConfig.ui.primary}-500`
|
||||
}
|
||||
|
||||
const toast = useToast()
|
||||
const suggestContent = ref<string>('')
|
||||
async function suggest() {
|
||||
if (suggestContent.value.trim().length < 4)
|
||||
return
|
||||
|
||||
await $fetch('/api/suggestion', {
|
||||
method: 'post',
|
||||
body: {
|
||||
content: suggestContent.value,
|
||||
},
|
||||
}).then((suggestion) => {
|
||||
toast.add({
|
||||
title: `Your suggestion for '${suggestion.content}'' has been successfully added`,
|
||||
icon: 'i-material-symbols-check-circle-outline-rounded',
|
||||
timeout: 4000,
|
||||
})
|
||||
}).catch(() => {
|
||||
toast.add({
|
||||
title: 'You already have suggested someone',
|
||||
color: 'red',
|
||||
})
|
||||
})
|
||||
suggestContent.value = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -59,16 +87,49 @@ function getColor() {
|
||||
Are you a web talent? Do you want to promote your project? Do you want to launch your career or gain visibility?
|
||||
</p>
|
||||
</div>
|
||||
<NuxtLink href="mailto:arthurdanjou@outlook.fr?subject=Join your talents' list">
|
||||
<UButton label="Join the talent's list" color="primary" />
|
||||
</NuxtLink>
|
||||
<div v-if="loggedIn" class="flex items-center justify-between gap-4">
|
||||
<div class="w-full relative flex items-center">
|
||||
<input
|
||||
v-model="suggestContent"
|
||||
type="text"
|
||||
required
|
||||
min="4"
|
||||
class="w-full rounded-lg p-2 h-10 focus:outline-none bg-gray-100 dark:bg-stone-800"
|
||||
placeholder="Suggest one name"
|
||||
>
|
||||
<UButton
|
||||
class="absolute right-1 top-1 text-gray-900 dark:text-white rounded-md"
|
||||
label="Send"
|
||||
:disabled="suggestContent.trim().length < 4"
|
||||
variant="soft"
|
||||
@click.prevent="suggest()"
|
||||
/>
|
||||
</div>
|
||||
<UButton
|
||||
@click.prevent="clear()"
|
||||
>
|
||||
Logout
|
||||
</UButton>
|
||||
</div>
|
||||
<div v-else class="flex gap-2">
|
||||
<UButton
|
||||
v-for="provider in providers"
|
||||
:key="provider.slug"
|
||||
:label="provider.label"
|
||||
:color="provider.color"
|
||||
variant="solid"
|
||||
:icon="provider.icon"
|
||||
:to="provider.link"
|
||||
external
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="getCategories" class="flex gap-2 w-full items-center justify-between pb-2 border-b border-zinc-100 dark:border-zinc-700/40 mb-4">
|
||||
<div class="flex gap-2 overflow-x-scroll sm:overflow-x-hidden bg-gray-100 dark:bg-gray-800 rounded-lg p-1 relative">
|
||||
<div
|
||||
v-for="category in categories"
|
||||
:key="category.slug"
|
||||
class="relative px-3 py-1 text-sm font-medium rounded-md h-8 text-gray-500 dark:text-gray-400 min-w-fit flex items-center justify-center w-full focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 transition-colors duration-200 ease-out cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-700 hover:text-black dark:hover:text-white"
|
||||
class="relative px-3 py-1 text-sm font-medium rounded-md h-8 text-gray-500 dark:text-gray-400 min-w-fit flex items-center justify-center w-full focus:outline-none transition-colors duration-200 ease-out cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-700 hover:text-black dark:hover:text-white"
|
||||
:class="{ 'text-gray-900 dark:text-white relative !bg-white dark:!bg-stone-900 rounded-md shadow-sm': isCategory(category.slug) }"
|
||||
@click.prevent="setCategory(category.slug)"
|
||||
>
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
const SuggestionValidator = z.object({
|
||||
author: z.string().trim(),
|
||||
content: z.string(),
|
||||
}).parse
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { author, content } = await getValidatedQuery(event, SuggestionValidator)
|
||||
const { content } = await readValidatedBody(event, SuggestionValidator)
|
||||
const { user } = await requireUserSession(event)
|
||||
return await usePrisma().suggestion.upsert({
|
||||
where: {
|
||||
author: user.email,
|
||||
},
|
||||
update: {
|
||||
content,
|
||||
},
|
||||
create: {
|
||||
author: user.email,
|
||||
content,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -28,7 +28,7 @@ export default defineEventHandler(async (event) => {
|
||||
return await prisma.talent.findMany({
|
||||
where: whereClause,
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
name: 'asc',
|
||||
},
|
||||
include: {
|
||||
categories: {
|
||||
|
||||
18
src/server/routes/auth/github.get.ts
Normal file
18
src/server/routes/auth/github.get.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
export default oauth.githubEventHandler({
|
||||
config: {
|
||||
emailRequired: true,
|
||||
},
|
||||
async onSuccess(event: any, { user }: any) {
|
||||
await setUserSession(event, {
|
||||
user: {
|
||||
email: user.email,
|
||||
picture: user.avatar_url,
|
||||
username: String(user.name).trim(),
|
||||
},
|
||||
})
|
||||
return sendRedirect(event, '/')
|
||||
},
|
||||
onError(error: any) {
|
||||
console.error('GitHub OAuth error:', error)
|
||||
},
|
||||
})
|
||||
12
src/server/routes/auth/google.get.ts
Normal file
12
src/server/routes/auth/google.get.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export default oauth.githubEventHandler({
|
||||
async onSuccess(event: any, { user }: any) {
|
||||
await setUserSession(event, {
|
||||
user: {
|
||||
email: user.email,
|
||||
picture: user.photoUrl,
|
||||
username: String(user.displayName).trim(),
|
||||
},
|
||||
})
|
||||
return sendRedirect(event, '/')
|
||||
},
|
||||
})
|
||||
25
types.ts
25
types.ts
@@ -1,3 +1,4 @@
|
||||
import exp from 'node:constants'
|
||||
import type { MarkdownParsedContent, ParsedContent } from '@nuxt/content/dist/runtime/types'
|
||||
|
||||
export enum ColorsTheme {
|
||||
@@ -69,3 +70,27 @@ export interface Skill extends ParsedContent {
|
||||
}
|
||||
color: string
|
||||
}
|
||||
|
||||
export const providers = [
|
||||
{
|
||||
slug: 'github',
|
||||
label: 'Use Github',
|
||||
icon: 'i-ph-github-logo-bold',
|
||||
link: '/auth/github',
|
||||
color: 'black',
|
||||
},
|
||||
/* {
|
||||
slug: 'twitter',
|
||||
label: 'Use Twitter',
|
||||
icon: 'i-ph-twitter-logo-bold',
|
||||
link: '/auth/twitter',
|
||||
color: 'cyan',
|
||||
}, */
|
||||
{
|
||||
slug: 'google',
|
||||
label: 'Use Google',
|
||||
icon: 'i-ph-google-logo-bold',
|
||||
link: '/auth/google',
|
||||
color: 'red',
|
||||
},
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user