mirror of
https://github.com/ArthurDanjou/arthome.git
synced 2026-02-01 20:07:52 +01:00
Working on arthome
This commit is contained in:
19
app/app.vue
19
app/app.vue
@@ -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
|
||||
|
||||
13
app/components/Category.vue
Normal file
13
app/components/Category.vue
Normal 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
18
app/components/Tab.vue
Normal 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>
|
||||
@@ -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
|
||||
}
|
||||
13
app/composables/categories.ts
Normal file
13
app/composables/categories.ts
Normal 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
21
app/composables/tabs.ts
Normal 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
21
app/composables/toasts.ts
Normal 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',
|
||||
})
|
||||
}
|
||||
22
app/composables/user-limit.ts
Normal file
22
app/composables/user-limit.ts
Normal 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,
|
||||
}
|
||||
}
|
||||
@@ -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
7
app/middleware/ghost.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export default defineNuxtRouteMiddleware(async () => {
|
||||
const { loggedIn } = useUserSession()
|
||||
|
||||
if (loggedIn.value) {
|
||||
return navigateTo('/')
|
||||
}
|
||||
})
|
||||
13
app/pages/[user].vue
Normal file
13
app/pages/[user].vue
Normal 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>
|
||||
@@ -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>
|
||||
@@ -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
104
app/pages/login.vue
Normal 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
24
app/pages/profile.vue
Normal 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>
|
||||
Reference in New Issue
Block a user