Working on arthome

This commit is contained in:
2024-08-25 18:33:37 +02:00
parent a5120d006a
commit a1e31a89a7
49 changed files with 3139 additions and 284 deletions

View File

@@ -1,22 +1,15 @@
<script lang="ts" setup>
useHead({
link: [{ rel: 'icon', type: 'image/png', href: '/favicon.ico' }],
title: 'Home by Arthur Danjou',
title: 'ArtHome by Arthur Danjou',
})
const { loggedIn, clear, user } = useUserSession()
const colorMode = useColorMode()
const authorized = await isAuthorized()
onMounted(async () => {
if (!authorized) {
navigateTo('/')
}
})
watch(loggedIn, async () => {
if (!loggedIn.value) {
navigateTo('/')
navigateTo('/login')
}
})
@@ -24,6 +17,12 @@ function toggleColorMode() {
colorMode.preference = colorMode.preference === 'dark' ? 'light' : 'dark'
}
async function logout() {
await clear()
navigateTo('/login')
window.location.reload()
}
defineShortcuts({
t: () => toggleColorMode(),
c: () => toggleColorMode(),
@@ -43,7 +42,7 @@ defineShortcuts({
square
trailing-icon="i-ph:person-arms-spread-duotone"
variant="ghost"
@click="clear"
@click="logout"
/>
</UTooltip>
<UButton

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
</script>
<template>
<div>
<slot />
</div>
</template>
<style scoped>
</style>

18
app/components/Tab.vue Normal file
View File

@@ -0,0 +1,18 @@
<script setup lang="ts">
import type { Tab } from '~~/server/utils/db'
defineProps<{
tab: PropType<Tab>
}>()
</script>
<template>
<div>
Tab
{{ tab }}
</div>
</template>
<style scoped>
</style>

View File

@@ -1,10 +0,0 @@
export async function isAuthorized() {
const { user } = useUserSession()
const { data: authorized } = await useFetch('/api/authorized', {
method: 'POST',
body: {
email: user.value?.email ?? 'test@nuxt.com',
},
})
return authorized.value
}

View File

@@ -0,0 +1,13 @@
export function useCategories() {
async function getCategories() {
return useAsyncData<CategoryType[]>(async () => {
const res = await $fetch('/api/categories')
console.log('res', res)
return res
})
}
return {
getCategories,
}
}

21
app/composables/tabs.ts Normal file
View File

@@ -0,0 +1,21 @@
export function useTabs() {
async function createTab(tab: TabType) {
console.log('createTab', tab)
return tab
}
async function deleteTab(tab: TabType) {
console.log('deleteTab', tab)
return tab
}
async function updateTab(tab: TabType) {
console.log('updateTab', tab)
return tab
}
return {
createTab,
deleteTab,
}
}

21
app/composables/toasts.ts Normal file
View File

@@ -0,0 +1,21 @@
export function useSuccessToast(title: string, description?: string) {
const toast = useToast()
toast.add({
title,
description,
color: 'green',
icon: 'i-ph:check-circle-duotone',
})
}
export function useErrorToast(title: string, description?: string) {
const toast = useToast()
toast.add({
title,
description,
color: 'red',
icon: 'i-ph:x-circle-duotone',
})
}

View File

@@ -0,0 +1,22 @@
export function useUserLimit() {
function hasUserFreePlan() {
return true
}
function getRemainingCategories() {
if (!hasUserFreePlan())
return -1
return 3
}
function getRemainingTabs(category_id: number) {
if (!hasUserFreePlan())
return -1
return category_id * 3
}
return {
getRemainingCategories,
getRemainingTabs,
}
}

View File

@@ -1,8 +1,7 @@
export default defineNuxtRouteMiddleware(async () => {
const { loggedIn } = useUserSession()
const authorized = await isAuthorized()
if (!loggedIn.value || !authorized) {
return navigateTo('/')
if (!loggedIn.value) {
return navigateTo('/login')
}
})

7
app/middleware/ghost.ts Normal file
View File

@@ -0,0 +1,7 @@
export default defineNuxtRouteMiddleware(async () => {
const { loggedIn } = useUserSession()
if (loggedIn.value) {
return navigateTo('/')
}
})

13
app/pages/[user].vue Normal file
View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
const router = useRouter()
</script>
<template>
<section>
{{ router.currentRoute.value.params.user }}
</section>
</template>
<style scoped>
</style>

View File

@@ -1,41 +0,0 @@
<script lang="ts" setup>
definePageMeta({
middleware: 'auth',
})
const date = ref<Date>(new Date())
onMounted(() => {
setInterval(() => date.value = new Date(), 1000)
})
const apps = await queryContent('/').find()
const dev = apps.filter(app => app._dir === 'dev')
const perso = apps.filter(app => app._dir === 'perso')
const maths = apps.filter(app => app._dir === 'maths')
const social = apps.filter(app => app._dir === 'social')
</script>
<template>
<main class="my-12">
<div v-if="date" class="flex flex-col items-center">
<h1 class="text-6xl md:text-9xl font-bold">
{{ useDateFormat(date, 'HH') }}
<span class="animate-pulse">:</span>
{{ useDateFormat(date, 'mm') }}
</h1>
<h1 class="text-2xl md:text-5xl">
{{ useDateFormat(date, 'dddd D MMMM YYYY', { locales: () => 'fr-FR' }) }}
</h1>
</div>
<div v-if="apps" class="space-y-12 mt-12">
<section class="grid grid-cols-1 auto-rows-auto sm:grid-cols-3 gap-4">
<Weather />
<Map />
</section>
<Application :apps="perso" title="Personnel" />
<Application :apps="social" title="Social" />
<Application :apps="dev" title="Développement" />
<Application :apps="maths" title="Mathématiques" />
</div>
</main>
</template>

View File

@@ -1,64 +1,54 @@
<script lang="ts" setup>
const { loggedIn } = useUserSession()
const authorized = await isAuthorized()
onMounted(() => {
if (authorized) {
navigateTo('/home')
}
definePageMeta({
middleware: 'auth',
})
const date = ref<Date>(new Date())
onMounted(() => {
setInterval(() => date.value = new Date(), 1000)
})
const { user, session } = useUserSession()
const { getCategories } = useCategories()
const categories = await getCategories()
</script>
<template>
<div class="min-h-screen flex items-center justify-center">
<UCard class="w-full md:w-1/2">
<template #header>
<h1 class="font-bold text-black dark:text-white text-lg space-y-2">
Welcome to ArtHome
</h1>
</template>
<template #default>
<p>
ArtHome is a private platform. You need to request access to be able to use it by asking to
<a
class="duration-300 underline-offset-2 text-md text-black dark:text-white underline decoration-gray-300 dark:decoration-neutral-700 hover:decoration-black dark:hover:decoration-white"
href="mailto:arthurdanjou@outlook.fr"
rel="noopener"
target="_blank"
>Arthur Danjou</a>
</p>
<div v-if="!loggedIn" class="flex gap-2 mt-2">
<UButton
:external="true"
color="black"
icon="i-ph:github-logo-duotone"
label="GitHub"
to="/auth/github"
/>
<UButton
:external="true"
color="red"
icon="i-ph:google-logo-duotone"
label="Google"
to="/auth/google"
/>
</div>
<UButton
v-if="authorized"
color="black"
icon="i-ph:house-duotone"
label="Go Home"
to="/home"
<main v-if="user" class="my-12">
<div v-if="date" class="flex flex-col items-center">
<h1 class="text-6xl md:text-9xl font-bold">
{{ useDateFormat(date, 'HH') }}
<span class="animate-pulse">:</span>
{{ useDateFormat(date, 'mm') }}
</h1>
<h1 class="text-2xl md:text-5xl">
{{ useDateFormat(date, 'dddd D MMMM YYYY', { locales: user.language }) }}
</h1>
</div>
<div>
{{ user }}
</div>
<div>
{{ session }}
</div>
<div>
{{ user === session.user }}
</div>
<div v-if="categories">
{{ categories }}
</div>
<div>
<Category>
<Tab
:tab="{
name: 'Test',
nameVisible: true,
icon: 'i-ph:cloud-duotone',
color: 'blue',
}"
/>
<p v-if="!authorized && loggedIn" class="text-red-500 font-medium">
You're not authorized to access
</p>
</template>
<template #footer>
<p class="italic text-sm">
No personal informations regarding your account are stored in database.
</p>
</template>
</UCard>
</div>
</Category>
</div>
</main>
</template>

104
app/pages/login.vue Normal file
View File

@@ -0,0 +1,104 @@
<script lang="ts" setup>
import { z } from 'zod'
import { useSession } from 'h3'
import type { FormSubmitEvent } from '#ui/types'
const { loggedIn } = useUserSession()
definePageMeta({
middleware: 'ghost',
})
const schema = z.object({
email: z.string().email('Invalid email'),
})
const form = ref()
type Schema = z.output<typeof schema>
const state = reactive({ email: undefined })
async function onSubmit(event: FormSubmitEvent<Schema>) {
// Do something with data
// todo: add login logic
console.log(event.data)
state.email = ''
}
const message = useState<string>('message')
if (import.meta.server) {
const session = await useSession(useRequestEvent()!, {
password: useRuntimeConfig().session.password,
})
message.value = session.data.message
await session.update({
message: '',
})
}
</script>
<template>
<div class="min-h-screen flex flex-col items-center justify-center">
<h1 class="tracking-widest text-4xl font-bold text-black dark:text-white mb-12">
ArtHome
</h1>
<div class="w-full md:w-1/3">
<UAlert
v-if="message"
class="mb-8"
color="red"
variant="outline"
:close-button="{ icon: 'i-ph:x-circle-duotone', color: 'red', variant: 'link', padded: false }"
:description="message"
@close="message = ''"
/>
</div>
<UCard class="w-full md:w-1/3 mt-2">
<template #header>
<h1 class="text-center font-bold text-black dark:text-white text-lg py-2">
Sign in to your ArtHome account
</h1>
</template>
<template #default>
<div v-if="!loggedIn" class="flex flex-col gap-4 p-4">
<UForm ref="form" :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
<UFormGroup name="email">
<UInput v-model="state.email" color="gray" placeholder="arthur@arthome.com" />
</UFormGroup>
<UButton
:external="true"
color="gray"
icon="i-ph:envelope-duotone"
label="Continue with Email"
block
type="submit"
/>
</UForm>
<UDivider label="or" />
<UButton
:external="true"
color="gray"
icon="i-ph:github-logo-duotone"
label="Continue with GitHub"
block
to="/auth/github"
/>
<UButton
:external="true"
color="gray"
icon="i-ph:google-logo-duotone"
label="Continue With Google"
block
to="/auth/google"
/>
</div>
</template>
<template #footer>
<p class="italic text-xs">
We only store your email address, name and profile picture. We will never share your data with third parties.
</p>
</template>
</UCard>
</div>
</template>

24
app/pages/profile.vue Normal file
View File

@@ -0,0 +1,24 @@
<script setup lang="ts">
const { user, loggedIn, session, clear } = useUserSession()
</script>
<template>
<div>
<div>
User: {{ user }}
</div>
<div>
LoggedIn: {{ loggedIn }}
</div>
<div>
Session: {{ session }}
</div>
<div @click="clear">
clear
</div>
</div>
</template>
<style scoped>
</style>