mirror of
https://github.com/ArthurDanjou/arthome.git
synced 2026-01-14 12:14:33 +01:00
Working on arthome
This commit is contained in:
@@ -1,6 +1,23 @@
|
|||||||
export default defineAppConfig({
|
export default defineAppConfig({
|
||||||
ui: {
|
ui: {
|
||||||
gray: 'neutral',
|
gray: 'zinc',
|
||||||
primary: 'gray',
|
primary: 'gray',
|
||||||
|
notifications: {
|
||||||
|
position: 'bottom-0 right-0',
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
color: {
|
||||||
|
white: {
|
||||||
|
outline: 'shadow-sm bg-white dark:bg-gray-900 text-gray-900 dark:text-white ring-1 ring-inset ring-gray-300 dark:ring-gray-700 focus:ring-2 focus:ring-zinc-500 dark:focus:ring-zinc-500',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
color: {
|
||||||
|
white: {
|
||||||
|
outline: 'shadow-sm bg-white dark:bg-gray-900 text-gray-900 dark:text-white ring-1 ring-inset ring-gray-300 dark:ring-gray-700 focus:ring-2 focus:ring-zinc-500 dark:focus:ring-zinc-500',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
44
app/app.vue
44
app/app.vue
@@ -4,58 +4,22 @@ useHead({
|
|||||||
title: 'ArtHome by Arthur Danjou',
|
title: 'ArtHome by Arthur Danjou',
|
||||||
})
|
})
|
||||||
|
|
||||||
const { loggedIn, clear, user } = useUserSession()
|
const { loggedIn } = useUserSession()
|
||||||
const colorMode = useColorMode()
|
|
||||||
|
|
||||||
watch(loggedIn, async () => {
|
watch(loggedIn, async () => {
|
||||||
if (!loggedIn.value) {
|
if (!loggedIn.value) {
|
||||||
navigateTo('/login')
|
navigateTo('/login')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
function toggleColorMode() {
|
|
||||||
colorMode.preference = colorMode.preference === 'dark' ? 'light' : 'dark'
|
|
||||||
}
|
|
||||||
|
|
||||||
async function logout() {
|
|
||||||
await clear()
|
|
||||||
navigateTo('/login')
|
|
||||||
window.location.reload()
|
|
||||||
}
|
|
||||||
|
|
||||||
defineShortcuts({
|
|
||||||
t: () => toggleColorMode(),
|
|
||||||
c: () => toggleColorMode(),
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<NuxtLoadingIndicator color="#808080" />
|
<NuxtLoadingIndicator color="#808080" />
|
||||||
<UContainer>
|
<NuxtLayout>
|
||||||
<ClientOnly>
|
|
||||||
<div class="absolute top-2 right-2 flex gap-2">
|
|
||||||
<UTooltip v-if="loggedIn" text="Déconnexion">
|
|
||||||
<UButton
|
|
||||||
:label="user.name"
|
|
||||||
color="gray"
|
|
||||||
square
|
|
||||||
trailing-icon="i-ph:person-arms-spread-duotone"
|
|
||||||
variant="ghost"
|
|
||||||
@click="logout"
|
|
||||||
/>
|
|
||||||
</UTooltip>
|
|
||||||
<UButton
|
|
||||||
:icon="$colorMode.preference === 'dark' ? 'i-ph:moon-duotone' : 'i-ph:sun-duotone'"
|
|
||||||
color="gray"
|
|
||||||
square
|
|
||||||
variant="ghost"
|
|
||||||
@click="toggleColorMode"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</ClientOnly>
|
|
||||||
<NuxtPage />
|
<NuxtPage />
|
||||||
</UContainer>
|
</NuxtLayout>
|
||||||
|
<UNotifications />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
123
app/components/App/Header.vue
Normal file
123
app/components/App/Header.vue
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const colorMode = useColorMode()
|
||||||
|
const { user, loggedIn, clear } = useUserSession()
|
||||||
|
|
||||||
|
const isSettingsOpen = ref(false)
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
[{
|
||||||
|
slot: 'account',
|
||||||
|
disabled: true,
|
||||||
|
}],
|
||||||
|
[{
|
||||||
|
label: 'Home',
|
||||||
|
icon: 'i-ph:house-line-duotone',
|
||||||
|
action: () => navigateTo('/'),
|
||||||
|
}, {
|
||||||
|
label: 'Settings',
|
||||||
|
icon: 'i-ph:gear-six-duotone',
|
||||||
|
action: () => {
|
||||||
|
console.log('Settings')
|
||||||
|
isSettingsOpen.value = true
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
label: 'Profile',
|
||||||
|
icon: 'i-ph:person-arms-spread-duotone',
|
||||||
|
action: () => navigateTo('/profile'),
|
||||||
|
}],
|
||||||
|
[{
|
||||||
|
slot: 'logout',
|
||||||
|
label: 'Sign out',
|
||||||
|
icon: 'i-ph:sign-out-bold',
|
||||||
|
}],
|
||||||
|
]
|
||||||
|
|
||||||
|
function toggleColorMode() {
|
||||||
|
colorMode.preference = colorMode.preference === 'dark' ? 'light' : 'dark'
|
||||||
|
}
|
||||||
|
|
||||||
|
async function logout() {
|
||||||
|
await clear()
|
||||||
|
navigateTo('/login')
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
defineShortcuts({
|
||||||
|
t: () => toggleColorMode(),
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<header
|
||||||
|
class="fixed top-0 w-full py-4 z-50 bg-white/30 dark:bg-zinc-900/30 duration-300 backdrop-blur"
|
||||||
|
>
|
||||||
|
<UContainer>
|
||||||
|
<div class="flex justify-between w-full items-center">
|
||||||
|
<h1 class="tracking-wide text-lg font-bold text-black dark:text-white">
|
||||||
|
ArtHome
|
||||||
|
</h1>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<ClientOnly>
|
||||||
|
<UDropdown v-if="loggedIn" :items="items" mode="hover" :ui="{ item: { disabled: 'cursor-text select-text' } }" :popper="{ placement: 'bottom-start' }">
|
||||||
|
<UAvatar :src="user.avatar" />
|
||||||
|
|
||||||
|
<template #account>
|
||||||
|
<div class="text-left">
|
||||||
|
<p>
|
||||||
|
Signed in as
|
||||||
|
</p>
|
||||||
|
<p class="truncate font-medium text-gray-900 dark:text-white">
|
||||||
|
{{ user.name }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #item="{ item }">
|
||||||
|
<div class="w-full flex justify-between items-center" @click.prevent="item.action()">
|
||||||
|
<span class="truncate">{{ item.label }}</span>
|
||||||
|
<UIcon :name="item.icon" class="flex-shrink-0 h-4 w-4 text-gray-400 dark:text-gray-500 ms-auto" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #logout="{ item }">
|
||||||
|
<div class="w-full flex justify-between items-center" @click="logout()">
|
||||||
|
<span class="truncate">{{ item.label }}</span>
|
||||||
|
<UIcon :name="item.icon" class="flex-shrink-0 h-4 w-4 text-gray-400 dark:text-gray-500 ms-auto" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UDropdown>
|
||||||
|
<UButton
|
||||||
|
:icon="$colorMode.preference === 'dark' ? 'i-ph:moon-duotone' : 'i-ph:sun-duotone'"
|
||||||
|
color="gray"
|
||||||
|
square
|
||||||
|
size="md"
|
||||||
|
variant="ghost"
|
||||||
|
@click="toggleColorMode"
|
||||||
|
/>
|
||||||
|
</clientonly>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</UContainer>
|
||||||
|
</header>
|
||||||
|
<USlideover v-model="isSettingsOpen">
|
||||||
|
<UCard class="flex flex-col flex-1" :ui="{ body: { base: 'flex-1' }, ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
||||||
|
Settings
|
||||||
|
</h3>
|
||||||
|
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="isSettingsOpen = false" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #default>
|
||||||
|
Hey
|
||||||
|
Delete account
|
||||||
|
Change user details
|
||||||
|
{{ user }}
|
||||||
|
</template>
|
||||||
|
</UCard>
|
||||||
|
</USlideover>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
109
app/components/App/Tab.vue
Normal file
109
app/components/App/Tab.vue
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { TabType } from '~~/types/types'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
tab: TabType
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// DropDown Items
|
||||||
|
const items = [[
|
||||||
|
{
|
||||||
|
label: 'Edit',
|
||||||
|
icon: 'i-ph:pencil-duotone',
|
||||||
|
color: 'green',
|
||||||
|
click: tab => openUpdateTabModal(tab),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Delete',
|
||||||
|
icon: 'i-ph:trash-duotone',
|
||||||
|
color: 'red',
|
||||||
|
click: tab => openDeleteTabModal(tab),
|
||||||
|
},
|
||||||
|
]]
|
||||||
|
|
||||||
|
// Modals
|
||||||
|
const updateTabModal = ref(false)
|
||||||
|
const deleteTabModal = ref(false)
|
||||||
|
|
||||||
|
// Update Category
|
||||||
|
const currentUpdateTab = ref<TabType | null>(null)
|
||||||
|
function openUpdateTabModal(tab: TabType) {
|
||||||
|
currentUpdateTab.value = tab
|
||||||
|
updateTabModal.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete Category
|
||||||
|
const currentDeleteTab = ref<TabType | null>(null)
|
||||||
|
function openDeleteTabModal(tab: TabType) {
|
||||||
|
currentDeleteTab.value = tab
|
||||||
|
deleteTabModal.value = true
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ULink
|
||||||
|
:to="tab.link"
|
||||||
|
class="relative"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<div v-show="tab.primary" class="absolute flex h-3 w-3 -left-1 -top-1">
|
||||||
|
<span class="animate-ping absolute inline-flex h-full w-full rounded-full opacity-75" :class="`bg-${tab.color}-400`" />
|
||||||
|
<span class="relative inline-flex rounded-full h-3 w-3" :class="`bg-${tab.color}-400`" />
|
||||||
|
</div>
|
||||||
|
<UCard
|
||||||
|
:ui="{ body: { base: 'h-full' }, background: 'h-full duration-300 bg-white hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-800' }"
|
||||||
|
>
|
||||||
|
<div class="flex items-center justify-between h-full">
|
||||||
|
<div class="flex gap-4 items-center h-full">
|
||||||
|
<UBadge :color="tab.color" class="p-2" variant="soft">
|
||||||
|
<UIcon :name="`i-ph:${tab.icon}`" size="32" />
|
||||||
|
</UBadge>
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<div :class="`text-${tab.color}-400`" class="text-xl font-medium">
|
||||||
|
<p>{{ tab.name }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<UDropdown
|
||||||
|
:items="items"
|
||||||
|
:popper="{ placement: 'bottom-end', arrow: true }"
|
||||||
|
:ui="{ container: 'z-50 group', width: 'w-40', shadow: 'shadow-2xl' }"
|
||||||
|
>
|
||||||
|
<UButton
|
||||||
|
color="gray"
|
||||||
|
variant="soft"
|
||||||
|
icon="i-ph:dots-three-outline-vertical-duotone"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<template #item="{ item }">
|
||||||
|
<div class="w-full flex items-center justify-between" @click.prevent="item.click(tab)">
|
||||||
|
<span
|
||||||
|
class="truncate"
|
||||||
|
:class="`text-${item.color}-500`"
|
||||||
|
>
|
||||||
|
{{ item.label }}
|
||||||
|
</span>
|
||||||
|
<UIcon
|
||||||
|
:name="item.icon"
|
||||||
|
class="flex-shrink-0 h-4 w-4 ms-auto"
|
||||||
|
:class="`text-${item.color}-500`"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UDropdown>
|
||||||
|
</div>
|
||||||
|
</UCard>
|
||||||
|
<ModalUpdateTab
|
||||||
|
v-if="currentUpdateTab"
|
||||||
|
v-model="updateTabModal"
|
||||||
|
:tab="currentUpdateTab"
|
||||||
|
@close-modal="updateTabModal = false"
|
||||||
|
/>
|
||||||
|
<ModalDeleteTab
|
||||||
|
v-if="currentDeleteTab"
|
||||||
|
v-model="deleteTabModal"
|
||||||
|
:tab="currentDeleteTab"
|
||||||
|
@close-modal="deleteTabModal = false"
|
||||||
|
/>
|
||||||
|
</ULink>
|
||||||
|
</template>
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
56
app/components/CategoryHeader.vue
Normal file
56
app/components/CategoryHeader.vue
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { CategoryType } from '~~/types/types'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
category: CategoryType
|
||||||
|
dropdownItems: { label: string, icon: string, color: string, click: (category: CategoryType) => void }[]
|
||||||
|
}>()
|
||||||
|
defineEmits(['createTab'])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="category" class="flex items-center mb-4" :class="category.nameVisible ? 'justify-between' : 'justify-end'">
|
||||||
|
<div v-if="category.nameVisible" class="flex items-center gap-2 mb-4" :class="`text-${category.color}-500`">
|
||||||
|
<UIcon :name="`i-ph:${category.icon}`" size="28" />
|
||||||
|
<h1 class="font-bold text-2xl">
|
||||||
|
{{ category.name }}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-4">
|
||||||
|
<UButton
|
||||||
|
color="gray"
|
||||||
|
variant="solid"
|
||||||
|
label="New tab"
|
||||||
|
icon="i-ph:plus-circle-duotone"
|
||||||
|
@click.prevent="$emit('createTab')"
|
||||||
|
/>
|
||||||
|
<UDropdown
|
||||||
|
:items="dropdownItems"
|
||||||
|
:popper="{ placement: 'bottom-end', arrow: true }"
|
||||||
|
:ui="{ width: 'w-40', shadow: 'shadow-xl' }"
|
||||||
|
>
|
||||||
|
<UButton
|
||||||
|
color="white"
|
||||||
|
variant="solid"
|
||||||
|
icon="i-ph:dots-three-outline-vertical-duotone"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<template #item="{ item }">
|
||||||
|
<div class="w-full flex items-center justify-between" @click.prevent="item.click(category)">
|
||||||
|
<span
|
||||||
|
class="truncate"
|
||||||
|
:class="`text-${item.color}-500`"
|
||||||
|
>
|
||||||
|
{{ item.label }}
|
||||||
|
</span>
|
||||||
|
<UIcon
|
||||||
|
:name="item.icon"
|
||||||
|
class="flex-shrink-0 h-4 w-4 ms-auto"
|
||||||
|
:class="`text-${item.color}-500`"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UDropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
70
app/components/Modal/CreateCategory.vue
Normal file
70
app/components/Modal/CreateCategory.vue
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormSubmitEvent } from '#ui/types'
|
||||||
|
import type { CreateCategorySchemaType } from '~~/types/types'
|
||||||
|
import { COLORS, CreateCategorySchema } from '~~/types/types'
|
||||||
|
|
||||||
|
const emit = defineEmits(['closeModal'])
|
||||||
|
const { createCategory } = await useCategories()
|
||||||
|
const state = reactive({
|
||||||
|
name: undefined,
|
||||||
|
icon: undefined,
|
||||||
|
color: COLORS[0],
|
||||||
|
nameVisible: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
async function handleCreate(event: FormSubmitEvent<CreateCategorySchemaType>) {
|
||||||
|
await createCategory(event.data)
|
||||||
|
emit('closeModal')
|
||||||
|
state.color = COLORS[0]
|
||||||
|
state.nameVisible = true
|
||||||
|
state.icon = undefined
|
||||||
|
state.name = undefined
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UModal :ui="{ width: 'w-full sm:max-w-md' }">
|
||||||
|
<UCard>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
||||||
|
Create a new category
|
||||||
|
</h3>
|
||||||
|
<UButton
|
||||||
|
color="gray"
|
||||||
|
variant="soft"
|
||||||
|
icon="i-heroicons-x-mark-20-solid"
|
||||||
|
class="p-1"
|
||||||
|
@click="$emit('closeModal')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #default>
|
||||||
|
<UForm :schema="CreateCategorySchema" :state="state" class="space-y-4" @submit="handleCreate">
|
||||||
|
<UFormGroup label="Name" name="name">
|
||||||
|
<UInput v-model="state.name" type="text" variant="outline" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Icon " name="icon" help="Get icon from the Phosphor Collection">
|
||||||
|
<UInput v-model="state.icon" type="text" variant="outline" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Color " name="color">
|
||||||
|
<USelect v-model="state.color" :options="COLORS" variant="outline" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup>
|
||||||
|
<UCheckbox v-model="state.nameVisible" :color="state.color" label="Is the category name visible?" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UButton
|
||||||
|
type="submit"
|
||||||
|
:color="state.color"
|
||||||
|
block
|
||||||
|
label="Create category"
|
||||||
|
/>
|
||||||
|
</UForm>
|
||||||
|
</template>
|
||||||
|
</UCard>
|
||||||
|
</UModal>
|
||||||
|
</template>
|
||||||
91
app/components/Modal/CreateTab.vue
Normal file
91
app/components/Modal/CreateTab.vue
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormSubmitEvent } from '#ui/types'
|
||||||
|
import { COLORS, type CategoryType, CreateTabSchema, type CreateTabSchemaType } from '~~/types/types'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
category: CategoryType | undefined
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits(['closeModal'])
|
||||||
|
const { createTab } = await useTabs()
|
||||||
|
const { categories } = await useCategories()
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
name: undefined,
|
||||||
|
icon: undefined,
|
||||||
|
link: undefined,
|
||||||
|
color: COLORS[0],
|
||||||
|
primary: false,
|
||||||
|
categoryId: props.category?.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
state.categoryId = props.category?.id
|
||||||
|
})
|
||||||
|
|
||||||
|
async function handleCreate(event: FormSubmitEvent<CreateTabSchemaType>) {
|
||||||
|
await createTab(event.data)
|
||||||
|
emit('closeModal')
|
||||||
|
state.name = undefined
|
||||||
|
state.icon = undefined
|
||||||
|
state.link = undefined
|
||||||
|
state.color = COLORS[0]
|
||||||
|
state.primary = false
|
||||||
|
state.categoryId = props.category?.id
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UModal :ui="{ width: 'w-full sm:max-w-md' }">
|
||||||
|
<UCard>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
||||||
|
Create a new tab
|
||||||
|
</h3>
|
||||||
|
<UButton
|
||||||
|
color="gray"
|
||||||
|
variant="soft"
|
||||||
|
icon="i-heroicons-x-mark-20-solid"
|
||||||
|
class="p-1"
|
||||||
|
@click="$emit('closeModal')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #default>
|
||||||
|
<UForm :schema="CreateTabSchema" :state="state" class="space-y-4" @submit="handleCreate">
|
||||||
|
<UFormGroup label="Name" name="name">
|
||||||
|
<UInput v-model="state.name" type="text" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Icon " name="icon">
|
||||||
|
<UInput v-model="state.icon" type="text" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Color " name="color">
|
||||||
|
<USelect v-model="state.color" :options="COLORS" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Category " name="category">
|
||||||
|
<USelect v-model="state.categoryId" :options="categories" option-attribute="name" value-attribute="id" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Link " name="link">
|
||||||
|
<UInput v-model="state.link" type="text" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup>
|
||||||
|
<UCheckbox v-model="state.primary" :color="state.color" label="Is the tab primary?" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UButton
|
||||||
|
type="submit"
|
||||||
|
:color="state.color"
|
||||||
|
block
|
||||||
|
label="Create tab"
|
||||||
|
/>
|
||||||
|
</UForm>
|
||||||
|
</template>
|
||||||
|
</UCard>
|
||||||
|
</UModal>
|
||||||
|
</template>
|
||||||
58
app/components/Modal/DeleteCategory.vue
Normal file
58
app/components/Modal/DeleteCategory.vue
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { CategoryType } from '~~/types/types'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
category: CategoryType | null
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits(['closeModal'])
|
||||||
|
const { deleteCategory } = await useCategories()
|
||||||
|
|
||||||
|
async function handleDelete() {
|
||||||
|
await deleteCategory(props.category.id)
|
||||||
|
emit('closeModal')
|
||||||
|
}
|
||||||
|
|
||||||
|
defineShortcuts({
|
||||||
|
enter: async () => await handleDelete(),
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UModal :ui="{ width: 'w-full sm:max-w-md' }">
|
||||||
|
<UCard>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
||||||
|
Confirm deletion of '{{ category.name }}'
|
||||||
|
</h3>
|
||||||
|
<UButton
|
||||||
|
color="gray"
|
||||||
|
variant="soft"
|
||||||
|
icon="i-heroicons-x-mark-20-solid"
|
||||||
|
class="p-1"
|
||||||
|
@click="$emit('closeModal')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #default>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<UButton
|
||||||
|
color="red"
|
||||||
|
variant="solid"
|
||||||
|
label="Delete"
|
||||||
|
block
|
||||||
|
@click.prevent="handleDelete"
|
||||||
|
/>
|
||||||
|
<UButton
|
||||||
|
color="gray"
|
||||||
|
variant="solid"
|
||||||
|
label="Cancel"
|
||||||
|
block
|
||||||
|
@click.prevent="$emit('closeModal')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UCard>
|
||||||
|
</UModal>
|
||||||
|
</template>
|
||||||
58
app/components/Modal/DeleteTab.vue
Normal file
58
app/components/Modal/DeleteTab.vue
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { TabType } from '~~/types/types'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
tab: TabType | null
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits(['closeModal'])
|
||||||
|
const { deleteTab } = await useTabs()
|
||||||
|
|
||||||
|
async function handleDelete() {
|
||||||
|
await deleteTab(props.tab.id)
|
||||||
|
emit('closeModal')
|
||||||
|
}
|
||||||
|
|
||||||
|
defineShortcuts({
|
||||||
|
enter: async () => await handleDelete(),
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UModal :ui="{ width: 'w-full sm:max-w-md' }">
|
||||||
|
<UCard>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
||||||
|
Confirm deletion of '{{ tab.name }}'
|
||||||
|
</h3>
|
||||||
|
<UButton
|
||||||
|
color="gray"
|
||||||
|
variant="soft"
|
||||||
|
icon="i-heroicons-x-mark-20-solid"
|
||||||
|
class="p-1"
|
||||||
|
@click="$emit('closeModal')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #default>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<UButton
|
||||||
|
color="red"
|
||||||
|
variant="solid"
|
||||||
|
label="Delete"
|
||||||
|
block
|
||||||
|
@click.prevent="handleDelete"
|
||||||
|
/>
|
||||||
|
<UButton
|
||||||
|
color="gray"
|
||||||
|
variant="solid"
|
||||||
|
label="Cancel"
|
||||||
|
block
|
||||||
|
@click.prevent="$emit('closeModal')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UCard>
|
||||||
|
</UModal>
|
||||||
|
</template>
|
||||||
80
app/components/Modal/UpdateCategory.vue
Normal file
80
app/components/Modal/UpdateCategory.vue
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { COLORS, type CategoryType, UpdateCategorySchema } from '~~/types/types'
|
||||||
|
import type { FormSubmitEvent } from '#ui/types'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
category: CategoryType | null
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits(['closeModal'])
|
||||||
|
const { updateCategory } = await useCategories()
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
name: props.category?.name,
|
||||||
|
icon: props.category?.icon,
|
||||||
|
color: props.category?.color,
|
||||||
|
nameVisible: props.category?.nameVisible,
|
||||||
|
})
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
state.name = props.category?.name
|
||||||
|
state.icon = props.category?.icon
|
||||||
|
state.color = props.category?.color
|
||||||
|
state.nameVisible = props.category?.nameVisible
|
||||||
|
})
|
||||||
|
|
||||||
|
async function handleUpdate(event: FormSubmitEvent<UpdateCategorySchema>) {
|
||||||
|
await updateCategory({
|
||||||
|
id: props.category!.id,
|
||||||
|
...event.data,
|
||||||
|
})
|
||||||
|
emit('closeModal')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UModal :ui="{ width: 'w-full sm:max-w-md' }">
|
||||||
|
<UCard>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
||||||
|
Update category '{{ category.name }}'
|
||||||
|
</h3>
|
||||||
|
<UButton
|
||||||
|
color="gray"
|
||||||
|
variant="soft"
|
||||||
|
icon="i-heroicons-x-mark-20-solid"
|
||||||
|
class="p-1"
|
||||||
|
@click="$emit('closeModal')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #default>
|
||||||
|
<UForm :schema="UpdateCategorySchema" :state="state" class="space-y-4" @submit="handleUpdate">
|
||||||
|
<UFormGroup label="Name" name="name">
|
||||||
|
<UInput v-model="state.name" type="text" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Color " name="color">
|
||||||
|
<USelect v-model="state.color" :options="COLORS" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Icon " name="icon">
|
||||||
|
<UInput v-model="state.icon" type="text" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup>
|
||||||
|
<UCheckbox v-model="state.nameVisible" :color="state.color" label="Is the category name visible?" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UButton
|
||||||
|
type="submit"
|
||||||
|
:color="state.color"
|
||||||
|
block
|
||||||
|
label="Update category"
|
||||||
|
/>
|
||||||
|
</UForm>
|
||||||
|
</template>
|
||||||
|
</UCard>
|
||||||
|
</UModal>
|
||||||
|
</template>
|
||||||
89
app/components/Modal/UpdateTab.vue
Normal file
89
app/components/Modal/UpdateTab.vue
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormSubmitEvent } from '#ui/types'
|
||||||
|
import type { COLORS, TabType, UpdateTabSchemaType } from '~~/types/types'
|
||||||
|
import { UpdateTabSchema } from '~~/types/types'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
tab: TabType | null
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits(['closeModal'])
|
||||||
|
const { categories } = await useCategories()
|
||||||
|
const { updateTab } = await useTabs()
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
name: props.tab?.name,
|
||||||
|
icon: props.tab?.icon,
|
||||||
|
color: props.tab?.color,
|
||||||
|
primary: props.tab?.primary,
|
||||||
|
categoryId: props.tab?.categoryId,
|
||||||
|
})
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
state.name = props.tab?.name
|
||||||
|
state.icon = props.tab?.icon
|
||||||
|
state.color = props.tab?.color
|
||||||
|
state.primary = props.tab?.primary
|
||||||
|
state.categoryId = props.tab?.categoryId
|
||||||
|
})
|
||||||
|
|
||||||
|
async function handleUpdate(event: FormSubmitEvent<UpdateTabSchemaType>) {
|
||||||
|
await updateTab({
|
||||||
|
id: props.tab!.id,
|
||||||
|
...event.data,
|
||||||
|
categoryId: Number(event.data.categoryId),
|
||||||
|
})
|
||||||
|
emit('closeModal')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UModal :ui="{ width: 'w-full sm:max-w-md' }">
|
||||||
|
<UCard>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
||||||
|
Update category '{{ tab.name }}'
|
||||||
|
</h3>
|
||||||
|
<UButton
|
||||||
|
color="gray"
|
||||||
|
variant="soft"
|
||||||
|
icon="i-heroicons-x-mark-20-solid"
|
||||||
|
class="p-1"
|
||||||
|
@click="$emit('closeModal')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #default>
|
||||||
|
<UForm :schema="UpdateTabSchema" :state="state" class="space-y-4" @submit="handleUpdate">
|
||||||
|
<UFormGroup label="Name" name="name">
|
||||||
|
<UInput v-model="state.name" type="text" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Color " name="color">
|
||||||
|
<USelect v-model="state.color" :options="COLORS" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Icon " name="icon">
|
||||||
|
<UInput v-model="state.icon" type="text" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Category " name="category">
|
||||||
|
<USelect v-model="state.categoryId" :options="categories" option-attribute="name" value-attribute="id" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup>
|
||||||
|
<UCheckbox v-model="state.primary" :color="state.color" label="Is the category primary?" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UButton
|
||||||
|
type="submit"
|
||||||
|
:color="state.color"
|
||||||
|
block
|
||||||
|
label="Update tab"
|
||||||
|
/>
|
||||||
|
</UForm>
|
||||||
|
</template>
|
||||||
|
</UCard>
|
||||||
|
</UModal>
|
||||||
|
</template>
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
<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,13 +1,47 @@
|
|||||||
export function useCategories() {
|
import type { type CategoryType, CreateCategorySchema, UpdateCategorySchema } from '~~/types/types'
|
||||||
async function getCategories() {
|
|
||||||
return useAsyncData<CategoryType[]>(async () => {
|
export async function useCategories() {
|
||||||
const res = await $fetch('/api/categories')
|
const { data: categories, refresh }
|
||||||
console.log('res', res)
|
= await useAsyncData<CategoryType[]>(async () => await useRequestFetch()('/api/categories'))
|
||||||
return res
|
|
||||||
|
async function getCategory(id: number) {
|
||||||
|
return categories.data.value.find(category => category.id === id)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createCategory(category: CreateCategorySchema) {
|
||||||
|
await $fetch('/api/categories', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(category),
|
||||||
})
|
})
|
||||||
|
.catch(error => useErrorToast('Category creation failed!', `Error: ${error}`))
|
||||||
|
await refresh()
|
||||||
|
await useSuccessToast('Category successfully created!')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateCategory(category: UpdateCategorySchema & { id: number }) {
|
||||||
|
await $fetch(`/api/categories/${category.id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
body: JSON.stringify(category),
|
||||||
|
})
|
||||||
|
.catch(error => useErrorToast('Category update failed!', `Error: ${error}`))
|
||||||
|
await refresh()
|
||||||
|
await useSuccessToast('Category successfully updated!')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteCategory(id: number) {
|
||||||
|
await $fetch(`/api/categories/${id}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
})
|
||||||
|
.catch(error => useErrorToast('Category deletion failed!', `Error: ${error}`))
|
||||||
|
await refresh()
|
||||||
|
await useSuccessToast('Category successfully deleted!')
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getCategories,
|
categories,
|
||||||
|
getCategory,
|
||||||
|
createCategory,
|
||||||
|
updateCategory,
|
||||||
|
deleteCategory,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,52 @@
|
|||||||
export function useTabs() {
|
import type { CreateTabSchema, TabType, UpdateTabSchema } from '~~/types/types'
|
||||||
async function createTab(tab: TabType) {
|
|
||||||
console.log('createTab', tab)
|
export async function useTabs() {
|
||||||
return tab
|
const { data: tabs, refresh }
|
||||||
|
= await useAsyncData<TabType[]>(async () => await useRequestFetch()('/api/tabs'))
|
||||||
|
|
||||||
|
function getTabsForCategory(categoryId: number): TabType[] {
|
||||||
|
return tabs.value.filter(tab => tab.categoryId === categoryId)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteTab(tab: TabType) {
|
async function createTab(tab: CreateTabSchema) {
|
||||||
console.log('deleteTab', tab)
|
await $fetch('/api/tabs', {
|
||||||
return tab
|
method: 'POST',
|
||||||
|
body: JSON.stringify(tab),
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
await refresh()
|
||||||
|
useSuccessToast('Tab successfully created!')
|
||||||
|
})
|
||||||
|
.catch(error => useErrorToast('Tab creation failed!', `Error: ${error}`))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateTab(tab: TabType) {
|
async function updateTab(tab: UpdateTabSchema) {
|
||||||
console.log('updateTab', tab)
|
console.log(tab)
|
||||||
return tab
|
await $fetch(`/api/tabs/${tab.id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
body: JSON.stringify(tab),
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
await refresh()
|
||||||
|
useSuccessToast('Tab successfully updated!')
|
||||||
|
})
|
||||||
|
.catch(error => useErrorToast('Tab update failed!', `Error: ${error}`))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteTab(id: number) {
|
||||||
|
await $fetch(`/api/tabs/${id}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
})
|
||||||
|
.catch(error => useErrorToast('Tab deletion failed!', `Error: ${error}`))
|
||||||
|
await refresh()
|
||||||
|
useSuccessToast('Tab successfully deleted!')
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
tabs,
|
||||||
createTab,
|
createTab,
|
||||||
deleteTab,
|
deleteTab,
|
||||||
|
getTabsForCategory,
|
||||||
|
updateTab,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
8
app/layouts/default.vue
Normal file
8
app/layouts/default.vue
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<AppHeader />
|
||||||
|
<UContainer class="mt-20">
|
||||||
|
<NuxtPage />
|
||||||
|
</UContainer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
7
app/layouts/login.vue
Normal file
7
app/layouts/login.vue
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<UContainer>
|
||||||
|
<NuxtPage />
|
||||||
|
</UContainer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import type { CategoryType } from '~~/types/types'
|
||||||
|
import CategoryHeader from '~/components/CategoryHeader.vue'
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: 'auth',
|
middleware: 'auth',
|
||||||
})
|
})
|
||||||
@@ -8,15 +11,61 @@ onMounted(() => {
|
|||||||
setInterval(() => date.value = new Date(), 1000)
|
setInterval(() => date.value = new Date(), 1000)
|
||||||
})
|
})
|
||||||
|
|
||||||
const { user, session } = useUserSession()
|
const { user } = useUserSession()
|
||||||
|
const { categories } = await useCategories()
|
||||||
|
const { getTabsForCategory } = await useTabs()
|
||||||
|
|
||||||
const { getCategories } = useCategories()
|
// Modals
|
||||||
const categories = await getCategories()
|
const createCategoryModal = ref(false)
|
||||||
|
const updateCategoryModal = ref(false)
|
||||||
|
const deleteCategoryModal = ref(false)
|
||||||
|
const createTabModal = ref(false)
|
||||||
|
|
||||||
|
// Update Category
|
||||||
|
const currentUpdateCategory = ref<CategoryType | null>(null)
|
||||||
|
function openUpdateCategoryModal(category: CategoryType) {
|
||||||
|
currentUpdateCategory.value = category
|
||||||
|
updateCategoryModal.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete Category
|
||||||
|
const currentDeleteCategory = ref<CategoryType | null>(null)
|
||||||
|
function openDeleteCategoryModal(category: CategoryType) {
|
||||||
|
currentDeleteCategory.value = category
|
||||||
|
deleteCategoryModal.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Tab
|
||||||
|
const currentCategory = ref<CategoryType | null>(null)
|
||||||
|
function openCreateTab(category: CategoryType) {
|
||||||
|
currentCategory.value = category
|
||||||
|
createTabModal.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// DropDown Items
|
||||||
|
const items = [[
|
||||||
|
{
|
||||||
|
label: 'Edit',
|
||||||
|
icon: 'i-ph:pencil-duotone',
|
||||||
|
color: 'green',
|
||||||
|
click: category => openUpdateCategoryModal(category),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Delete',
|
||||||
|
icon: 'i-ph:trash-duotone',
|
||||||
|
color: 'red',
|
||||||
|
click: category => openDeleteCategoryModal(category),
|
||||||
|
},
|
||||||
|
]]
|
||||||
|
|
||||||
|
defineShortcuts({
|
||||||
|
c: () => createCategoryModal.value = true,
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main v-if="user" class="my-12">
|
<main v-if="user" class="my-12">
|
||||||
<div v-if="date" class="flex flex-col items-center">
|
<div v-if="date" class="flex flex-col items-center mb-12">
|
||||||
<h1 class="text-6xl md:text-9xl font-bold">
|
<h1 class="text-6xl md:text-9xl font-bold">
|
||||||
{{ useDateFormat(date, 'HH') }}
|
{{ useDateFormat(date, 'HH') }}
|
||||||
<span class="animate-pulse">:</span>
|
<span class="animate-pulse">:</span>
|
||||||
@@ -26,29 +75,72 @@ const categories = await getCategories()
|
|||||||
{{ useDateFormat(date, 'dddd D MMMM YYYY', { locales: user.language }) }}
|
{{ useDateFormat(date, 'dddd D MMMM YYYY', { locales: user.language }) }}
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="flex justify-end mb-8 gap-4">
|
||||||
{{ user }}
|
<UButton
|
||||||
|
icon="i-ph:folder-simple-plus-duotone"
|
||||||
|
color="black"
|
||||||
|
variant="solid"
|
||||||
|
size="lg"
|
||||||
|
@click.prevent="createCategoryModal = true"
|
||||||
|
>
|
||||||
|
Create Category
|
||||||
|
<UKbd>C</UKbd>
|
||||||
|
</UButton>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<section v-if="categories">
|
||||||
{{ session }}
|
<div v-if="categories.length > 0" class="space-y-12">
|
||||||
</div>
|
<div
|
||||||
<div>
|
v-for="category in categories"
|
||||||
{{ user === session.user }}
|
:key="category.id"
|
||||||
</div>
|
>
|
||||||
<div v-if="categories">
|
<CategoryHeader
|
||||||
{{ categories }}
|
:dropdown-items="items"
|
||||||
</div>
|
:category="category"
|
||||||
<div>
|
@create-tab="openCreateTab(category)"
|
||||||
<Category>
|
/>
|
||||||
<Tab
|
<div v-if="getTabsForCategory(category.id).length > 0" class="grid grid-cols-1 auto-rows-auto sm:grid-cols-3 md:grid-cols-5 gap-4">
|
||||||
:tab="{
|
<AppTab
|
||||||
name: 'Test',
|
v-for="tab in getTabsForCategory(category.id)"
|
||||||
nameVisible: true,
|
:key="tab.id"
|
||||||
icon: 'i-ph:cloud-duotone',
|
:tab="tab"
|
||||||
color: 'blue',
|
|
||||||
}"
|
|
||||||
/>
|
/>
|
||||||
</Category>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else class="flex gap-2 items-center">
|
||||||
|
<UIcon name="i-ph:empty-duotone" size="16" />
|
||||||
|
<h1 class="text-sm font-medium">
|
||||||
|
The category is empty.
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="flex gap-2 items-center">
|
||||||
|
<UIcon name="i-ph:empty-duotone" size="20" />
|
||||||
|
<h1 class="text-lg font-medium">
|
||||||
|
You don't have any categories.
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<ModalCreateCategory
|
||||||
|
v-model="createCategoryModal"
|
||||||
|
@close-modal="createCategoryModal = false"
|
||||||
|
/>
|
||||||
|
<ModalUpdateCategory
|
||||||
|
v-if="currentUpdateCategory"
|
||||||
|
v-model="updateCategoryModal"
|
||||||
|
:category="currentUpdateCategory"
|
||||||
|
@close-modal="updateCategoryModal = false"
|
||||||
|
/>
|
||||||
|
<ModalDeleteCategory
|
||||||
|
v-if="currentDeleteCategory"
|
||||||
|
v-model="deleteCategoryModal"
|
||||||
|
:category="currentDeleteCategory"
|
||||||
|
@close-modal="deleteCategoryModal = false"
|
||||||
|
/>
|
||||||
|
<ModalCreateTab
|
||||||
|
v-if="currentCategory"
|
||||||
|
v-model="createTabModal"
|
||||||
|
:category="currentCategory"
|
||||||
|
@close-modal="createTabModal = false"
|
||||||
|
/>
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ const { loggedIn } = useUserSession()
|
|||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: 'ghost',
|
middleware: 'ghost',
|
||||||
|
layout: 'login',
|
||||||
})
|
})
|
||||||
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
email: z.string().email('Invalid email'),
|
email: z.string().email('Invalid email'),
|
||||||
})
|
})
|
||||||
|
|
||||||
const form = ref()
|
|
||||||
type Schema = z.output<typeof schema>
|
type Schema = z.output<typeof schema>
|
||||||
const state = reactive({ email: undefined })
|
const state = reactive({ email: undefined })
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ if (import.meta.server) {
|
|||||||
</template>
|
</template>
|
||||||
<template #default>
|
<template #default>
|
||||||
<div v-if="!loggedIn" class="flex flex-col gap-4 p-4">
|
<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">
|
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
|
||||||
<UFormGroup name="email">
|
<UFormGroup name="email">
|
||||||
<UInput v-model="state.email" color="gray" placeholder="arthur@arthome.com" />
|
<UInput v-model="state.email" color="gray" placeholder="arthur@arthome.com" />
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { user, loggedIn, session, clear } = useUserSession()
|
const { user, loggedIn, clear } = useUserSession()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -10,9 +10,6 @@ const { user, loggedIn, session, clear } = useUserSession()
|
|||||||
<div>
|
<div>
|
||||||
LoggedIn: {{ loggedIn }}
|
LoggedIn: {{ loggedIn }}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
Session: {{ session }}
|
|
||||||
</div>
|
|
||||||
<div @click="clear">
|
<div @click="clear">
|
||||||
clear
|
clear
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,17 +14,7 @@ export default defineNuxtConfig({
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Nuxt Modules
|
// Nuxt Modules
|
||||||
modules: [
|
modules: ['@nuxthub/core', '@nuxt/ui', '@vueuse/nuxt', '@nuxtjs/google-fonts', 'nuxt-auth-utils', '@nuxt/content', '@nuxthq/studio', '@nuxt/image', 'nuxt-mapbox', '@pinia/nuxt'],
|
||||||
'@nuxthub/core',
|
|
||||||
'@nuxt/ui',
|
|
||||||
'@vueuse/nuxt',
|
|
||||||
'@nuxtjs/google-fonts',
|
|
||||||
'nuxt-auth-utils',
|
|
||||||
'@nuxt/content',
|
|
||||||
'@nuxthq/studio',
|
|
||||||
'@nuxt/image',
|
|
||||||
'nuxt-mapbox',
|
|
||||||
],
|
|
||||||
|
|
||||||
// Nuxt UI
|
// Nuxt UI
|
||||||
ui: {
|
ui: {
|
||||||
|
|||||||
21
package.json
21
package.json
@@ -15,9 +15,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/content": "^2.13.2",
|
"@nuxt/content": "^2.13.2",
|
||||||
"@nuxt/image": "^1.7.0",
|
"@nuxt/image": "^1.7.1",
|
||||||
"@nuxthq/studio": "^2.0.3",
|
"@nuxthq/studio": "^2.0.3",
|
||||||
"@nuxthub/core": "^0.7.3",
|
"@nuxthub/core": "^0.7.7",
|
||||||
"@nuxtjs/google-fonts": "^3.2.0",
|
"@nuxtjs/google-fonts": "^3.2.0",
|
||||||
"drizzle-orm": "^0.33.0",
|
"drizzle-orm": "^0.33.0",
|
||||||
"h3-zod": "^0.5.3",
|
"h3-zod": "^0.5.3",
|
||||||
@@ -28,21 +28,20 @@
|
|||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^2.26.1",
|
"@antfu/eslint-config": "^2.27.3",
|
||||||
"@nuxt/devtools": "^1.3.14",
|
"@nuxt/devtools": "^1.4.1",
|
||||||
"@nuxt/ui": "^2.18.4",
|
"@nuxt/ui": "^2.18.4",
|
||||||
"@types/node": "^22.4.2",
|
"@types/node": "^22.5.1",
|
||||||
"@types/pg": "^8.11.6",
|
"@vueuse/core": "^11.0.3",
|
||||||
"@vueuse/core": "^11.0.1",
|
"@vueuse/nuxt": "^11.0.3",
|
||||||
"@vueuse/nuxt": "^11.0.1",
|
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"drizzle-kit": "^0.24.1",
|
"drizzle-kit": "^0.24.2",
|
||||||
"eslint": "^9.9.0",
|
"eslint": "^9.9.1",
|
||||||
"mapbox-gl": "^3.6.0",
|
"mapbox-gl": "^3.6.0",
|
||||||
"nuxt": "^3.13.0",
|
"nuxt": "^3.13.0",
|
||||||
"nuxt-mapbox": "^1.6.0",
|
"nuxt-mapbox": "^1.6.0",
|
||||||
"typescript": "^5.5.4",
|
"typescript": "^5.5.4",
|
||||||
"vue-tsc": "^2.0.29",
|
"vue-tsc": "^2.0.29",
|
||||||
"wrangler": "^3.72.1"
|
"wrangler": "^3.72.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1419
pnpm-lock.yaml
generated
1419
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
18
server/api/categories/[id].delete.ts
Normal file
18
server/api/categories/[id].delete.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
try {
|
||||||
|
const user = await getUserSession(event)
|
||||||
|
const { id } = await getRouterParams(event)
|
||||||
|
await useDrizzle()
|
||||||
|
.delete(tables.categories)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(tables.categories.id, id),
|
||||||
|
eq(tables.categories.userId, user.id),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return { statusCode: 200 }
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { err }
|
||||||
|
}
|
||||||
|
})
|
||||||
28
server/api/categories/[id].put.ts
Normal file
28
server/api/categories/[id].put.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { useValidatedBody } from 'h3-zod'
|
||||||
|
import { UpdateCategorySchema } from '~~/types/types'
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
try {
|
||||||
|
const user = await getUserSession(event)
|
||||||
|
const { id } = await getRouterParams(event)
|
||||||
|
const body = await useValidatedBody(event, UpdateCategorySchema)
|
||||||
|
await useDrizzle()
|
||||||
|
.update(tables.categories)
|
||||||
|
.set({
|
||||||
|
name: body.name,
|
||||||
|
icon: body.icon,
|
||||||
|
color: body.color,
|
||||||
|
nameVisible: body.nameVisible,
|
||||||
|
})
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(tables.categories.id, id),
|
||||||
|
eq(tables.categories.userId, user.id),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return { statusCode: 200 }
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { err }
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const user = await getUserSession(event)
|
const user = await requireUserSession(event)
|
||||||
console.log('session', user)
|
return useDrizzle()
|
||||||
return useDrizzle().query.categories.findMany({
|
.select()
|
||||||
where: eq(tables.users.id, user.id),
|
.from(tables.categories)
|
||||||
})
|
.where(
|
||||||
|
eq(tables.categories.userId, user.user.id),
|
||||||
|
)
|
||||||
|
.orderBy(tables.categories.id, 'desc')
|
||||||
})
|
})
|
||||||
|
|||||||
20
server/api/categories/index.post.ts
Normal file
20
server/api/categories/index.post.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { useValidatedBody } from 'h3-zod'
|
||||||
|
import { CreateCategorySchema } from '~~/types/types'
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
try {
|
||||||
|
const user = await getUserSession(event)
|
||||||
|
const body = await useValidatedBody(event, CreateCategorySchema)
|
||||||
|
await useDrizzle().insert(tables.categories).values({
|
||||||
|
name: body.name,
|
||||||
|
icon: body.icon,
|
||||||
|
color: body.color,
|
||||||
|
nameVisible: body.nameVisible,
|
||||||
|
userId: user.id,
|
||||||
|
})
|
||||||
|
return { statusCode: 200 }
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { err }
|
||||||
|
}
|
||||||
|
})
|
||||||
16
server/api/tabs/[id].delete.ts
Normal file
16
server/api/tabs/[id].delete.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
try {
|
||||||
|
const { id } = await getRouterParams(event)
|
||||||
|
await useDrizzle()
|
||||||
|
.delete(tables.tabs)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(tables.tabs.id, id),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return { statusCode: 200 }
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { err }
|
||||||
|
}
|
||||||
|
})
|
||||||
29
server/api/tabs/[id].put.ts
Normal file
29
server/api/tabs/[id].put.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { useValidatedBody } from 'h3-zod'
|
||||||
|
import { UpdateTabSchema } from '~~/types/types'
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
try {
|
||||||
|
const { id } = await getRouterParams(event)
|
||||||
|
console.log(await readBody(event))
|
||||||
|
const body = await useValidatedBody(event, UpdateTabSchema)
|
||||||
|
await useDrizzle()
|
||||||
|
.update(tables.tabs)
|
||||||
|
.set({
|
||||||
|
name: body.name,
|
||||||
|
icon: body.icon,
|
||||||
|
color: body.color,
|
||||||
|
nameVisible: body.nameVisible,
|
||||||
|
link: body.link,
|
||||||
|
})
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(tables.tabs.id, id),
|
||||||
|
eq(tables.tabs.categoryId, body.categoryId),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return { statusCode: 200 }
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { err }
|
||||||
|
}
|
||||||
|
})
|
||||||
6
server/api/tabs/index.get.ts
Normal file
6
server/api/tabs/index.get.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export default defineEventHandler(async () => {
|
||||||
|
return useDrizzle()
|
||||||
|
.select()
|
||||||
|
.from(tables.tabs)
|
||||||
|
.orderBy(tables.tabs.id, 'desc')
|
||||||
|
})
|
||||||
20
server/api/tabs/index.post.ts
Normal file
20
server/api/tabs/index.post.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { useValidatedBody } from 'h3-zod'
|
||||||
|
import { CreateTabSchema } from '~~/types/types'
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
try {
|
||||||
|
const body = await useValidatedBody(event, CreateTabSchema)
|
||||||
|
await useDrizzle().insert(tables.tabs).values({
|
||||||
|
name: body.name,
|
||||||
|
icon: body.icon,
|
||||||
|
color: body.color,
|
||||||
|
nameVisible: body.nameVisible,
|
||||||
|
categoryId: body.categoryId,
|
||||||
|
link: body.link,
|
||||||
|
})
|
||||||
|
return { statusCode: 200 }
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { err }
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -6,17 +6,10 @@ END $$;
|
|||||||
--> statement-breakpoint
|
--> statement-breakpoint
|
||||||
CREATE TABLE IF NOT EXISTS "categories" (
|
CREATE TABLE IF NOT EXISTS "categories" (
|
||||||
"id" serial PRIMARY KEY NOT NULL,
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
"name" text DEFAULT '' NOT NULL,
|
"name" text DEFAULT '',
|
||||||
"name_visible" boolean DEFAULT true NOT NULL,
|
"name_visible" boolean DEFAULT true,
|
||||||
"icon" text DEFAULT 'i-ph:circle-wavy-question-duotone' NOT NULL,
|
"icon" text DEFAULT 'i-ph:circle-wavy-question-duotone',
|
||||||
"color" text DEFAULT 'gray' NOT NULL,
|
"color" text DEFAULT 'gray',
|
||||||
"page_id" integer NOT NULL,
|
|
||||||
"created_at" timestamp (3) DEFAULT now(),
|
|
||||||
"updated_at" timestamp (3)
|
|
||||||
);
|
|
||||||
--> statement-breakpoint
|
|
||||||
CREATE TABLE IF NOT EXISTS "pages" (
|
|
||||||
"id" serial PRIMARY KEY NOT NULL,
|
|
||||||
"user_id" integer NOT NULL,
|
"user_id" integer NOT NULL,
|
||||||
"created_at" timestamp (3) DEFAULT now(),
|
"created_at" timestamp (3) DEFAULT now(),
|
||||||
"updated_at" timestamp (3)
|
"updated_at" timestamp (3)
|
||||||
@@ -24,10 +17,10 @@ CREATE TABLE IF NOT EXISTS "pages" (
|
|||||||
--> statement-breakpoint
|
--> statement-breakpoint
|
||||||
CREATE TABLE IF NOT EXISTS "tabs" (
|
CREATE TABLE IF NOT EXISTS "tabs" (
|
||||||
"id" serial PRIMARY KEY NOT NULL,
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
"name" text DEFAULT '' NOT NULL,
|
"name" text DEFAULT '',
|
||||||
"name_visible" boolean DEFAULT true NOT NULL,
|
"name_visible" boolean DEFAULT true,
|
||||||
"icon" text DEFAULT 'i-ph:circle-wavy-question-duotone' NOT NULL,
|
"icon" text DEFAULT 'i-ph:circle-wavy-question-duotone',
|
||||||
"color" text DEFAULT 'gray' NOT NULL,
|
"color" text DEFAULT 'gray',
|
||||||
"category_id" integer NOT NULL,
|
"category_id" integer NOT NULL,
|
||||||
"created_at" timestamp (3) DEFAULT now(),
|
"created_at" timestamp (3) DEFAULT now(),
|
||||||
"updated_at" timestamp (3)
|
"updated_at" timestamp (3)
|
||||||
@@ -43,10 +36,11 @@ CREATE TABLE IF NOT EXISTS "users" (
|
|||||||
"google_id" text,
|
"google_id" text,
|
||||||
"google_token" text,
|
"google_token" text,
|
||||||
"description" text DEFAULT '',
|
"description" text DEFAULT '',
|
||||||
"private" boolean DEFAULT false NOT NULL,
|
"avatar" text DEFAULT '',
|
||||||
"timezone" text DEFAULT 'undefined' NOT NULL,
|
"private" boolean DEFAULT false,
|
||||||
"location" text DEFAULT 'undefined' NOT NULL,
|
"language" text DEFAULT 'en-EN',
|
||||||
"subscription" "subscription" DEFAULT 'free' NOT NULL,
|
"location" text DEFAULT 'unknown',
|
||||||
|
"subscription" "subscription" DEFAULT 'free',
|
||||||
"created_at" timestamp (3) DEFAULT now(),
|
"created_at" timestamp (3) DEFAULT now(),
|
||||||
"updated_at" timestamp (3),
|
"updated_at" timestamp (3),
|
||||||
CONSTRAINT "users_email_unique" UNIQUE("email"),
|
CONSTRAINT "users_email_unique" UNIQUE("email"),
|
||||||
@@ -55,13 +49,7 @@ CREATE TABLE IF NOT EXISTS "users" (
|
|||||||
);
|
);
|
||||||
--> statement-breakpoint
|
--> statement-breakpoint
|
||||||
DO $$ BEGIN
|
DO $$ BEGIN
|
||||||
ALTER TABLE "categories" ADD CONSTRAINT "categories_page_id_pages_id_fk" FOREIGN KEY ("page_id") REFERENCES "public"."pages"("id") ON DELETE cascade ON UPDATE no action;
|
ALTER TABLE "categories" ADD CONSTRAINT "categories_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
ALTER TABLE "pages" ADD CONSTRAINT "pages_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
|
|
||||||
EXCEPTION
|
EXCEPTION
|
||||||
WHEN duplicate_object THEN null;
|
WHEN duplicate_object THEN null;
|
||||||
END $$;
|
END $$;
|
||||||
1
server/database/migrations/0001_fancy_tyger_tiger.sql
Normal file
1
server/database/migrations/0001_fancy_tyger_tiger.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "tabs" ADD COLUMN "link" text DEFAULT '';
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
ALTER TABLE "categories" ALTER COLUMN "id" SET DATA TYPE integer;--> statement-breakpoint
|
|
||||||
ALTER TABLE "pages" ALTER COLUMN "id" SET DATA TYPE integer;--> statement-breakpoint
|
|
||||||
ALTER TABLE "tabs" ALTER COLUMN "id" SET DATA TYPE integer;--> statement-breakpoint
|
|
||||||
ALTER TABLE "users" ALTER COLUMN "id" SET DATA TYPE integer;
|
|
||||||
2
server/database/migrations/0002_cool_dexter_bennett.sql
Normal file
2
server/database/migrations/0002_cool_dexter_bennett.sql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE "tabs" ADD COLUMN "primary" boolean DEFAULT false;--> statement-breakpoint
|
||||||
|
ALTER TABLE "tabs" DROP COLUMN IF EXISTS "name_visible";
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
ALTER TABLE "categories" ALTER COLUMN "name" DROP NOT NULL;--> statement-breakpoint
|
|
||||||
ALTER TABLE "categories" ALTER COLUMN "name_visible" DROP NOT NULL;--> statement-breakpoint
|
|
||||||
ALTER TABLE "categories" ALTER COLUMN "icon" DROP NOT NULL;--> statement-breakpoint
|
|
||||||
ALTER TABLE "categories" ALTER COLUMN "color" DROP NOT NULL;--> statement-breakpoint
|
|
||||||
ALTER TABLE "tabs" ALTER COLUMN "name" DROP NOT NULL;--> statement-breakpoint
|
|
||||||
ALTER TABLE "tabs" ALTER COLUMN "name_visible" DROP NOT NULL;--> statement-breakpoint
|
|
||||||
ALTER TABLE "tabs" ALTER COLUMN "icon" DROP NOT NULL;--> statement-breakpoint
|
|
||||||
ALTER TABLE "tabs" ALTER COLUMN "color" DROP NOT NULL;--> statement-breakpoint
|
|
||||||
ALTER TABLE "users" ALTER COLUMN "private" DROP NOT NULL;--> statement-breakpoint
|
|
||||||
ALTER TABLE "users" ALTER COLUMN "timezone" DROP NOT NULL;--> statement-breakpoint
|
|
||||||
ALTER TABLE "users" ALTER COLUMN "location" SET DEFAULT 'unknown';--> statement-breakpoint
|
|
||||||
ALTER TABLE "users" ALTER COLUMN "location" DROP NOT NULL;--> statement-breakpoint
|
|
||||||
ALTER TABLE "users" ALTER COLUMN "subscription" DROP NOT NULL;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ALTER TABLE "users" ADD COLUMN "avatar" text DEFAULT '';
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE "users" RENAME COLUMN "timezone" TO "language";--> statement-breakpoint
|
|
||||||
ALTER TABLE "users" ALTER COLUMN "language" SET DEFAULT 'english';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ALTER TABLE "users" ALTER COLUMN "language" SET DEFAULT 'en-EN';
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"id": "a8ec7e1e-1087-4ab5-be19-459dc9b0a4e0",
|
"id": "c52dbfc1-beae-4a41-8725-66def9fdacea",
|
||||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"dialect": "postgresql",
|
"dialect": "postgresql",
|
||||||
@@ -18,78 +18,29 @@
|
|||||||
"name": "name",
|
"name": "name",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": "''"
|
"default": "''"
|
||||||
},
|
},
|
||||||
"name_visible": {
|
"name_visible": {
|
||||||
"name": "name_visible",
|
"name": "name_visible",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": true
|
"default": true
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"name": "icon",
|
"name": "icon",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": "'i-ph:circle-wavy-question-duotone'"
|
"default": "'i-ph:circle-wavy-question-duotone'"
|
||||||
},
|
},
|
||||||
"color": {
|
"color": {
|
||||||
"name": "color",
|
"name": "color",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
|
||||||
"default": "'gray'"
|
|
||||||
},
|
|
||||||
"page_id": {
|
|
||||||
"name": "page_id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
"notNull": false,
|
||||||
"default": "now()"
|
"default": "'gray'"
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"categories_page_id_pages_id_fk": {
|
|
||||||
"name": "categories_page_id_pages_id_fk",
|
|
||||||
"tableFrom": "categories",
|
|
||||||
"tableTo": "pages",
|
|
||||||
"columnsFrom": [
|
|
||||||
"page_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"public.pages": {
|
|
||||||
"name": "pages",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "serial",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
},
|
||||||
"user_id": {
|
"user_id": {
|
||||||
"name": "user_id",
|
"name": "user_id",
|
||||||
@@ -113,9 +64,9 @@
|
|||||||
},
|
},
|
||||||
"indexes": {},
|
"indexes": {},
|
||||||
"foreignKeys": {
|
"foreignKeys": {
|
||||||
"pages_user_id_users_id_fk": {
|
"categories_user_id_users_id_fk": {
|
||||||
"name": "pages_user_id_users_id_fk",
|
"name": "categories_user_id_users_id_fk",
|
||||||
"tableFrom": "pages",
|
"tableFrom": "categories",
|
||||||
"tableTo": "users",
|
"tableTo": "users",
|
||||||
"columnsFrom": [
|
"columnsFrom": [
|
||||||
"user_id"
|
"user_id"
|
||||||
@@ -144,28 +95,28 @@
|
|||||||
"name": "name",
|
"name": "name",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": "''"
|
"default": "''"
|
||||||
},
|
},
|
||||||
"name_visible": {
|
"name_visible": {
|
||||||
"name": "name_visible",
|
"name": "name_visible",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": true
|
"default": true
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"name": "icon",
|
"name": "icon",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": "'i-ph:circle-wavy-question-duotone'"
|
"default": "'i-ph:circle-wavy-question-duotone'"
|
||||||
},
|
},
|
||||||
"color": {
|
"color": {
|
||||||
"name": "color",
|
"name": "color",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": "'gray'"
|
"default": "'gray'"
|
||||||
},
|
},
|
||||||
"category_id": {
|
"category_id": {
|
||||||
@@ -266,33 +217,40 @@
|
|||||||
"notNull": false,
|
"notNull": false,
|
||||||
"default": "''"
|
"default": "''"
|
||||||
},
|
},
|
||||||
|
"avatar": {
|
||||||
|
"name": "avatar",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "''"
|
||||||
|
},
|
||||||
"private": {
|
"private": {
|
||||||
"name": "private",
|
"name": "private",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
"timezone": {
|
"language": {
|
||||||
"name": "timezone",
|
"name": "language",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": "'undefined'"
|
"default": "'en-EN'"
|
||||||
},
|
},
|
||||||
"location": {
|
"location": {
|
||||||
"name": "location",
|
"name": "location",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": "'undefined'"
|
"default": "'unknown'"
|
||||||
},
|
},
|
||||||
"subscription": {
|
"subscription": {
|
||||||
"name": "subscription",
|
"name": "subscription",
|
||||||
"type": "subscription",
|
"type": "subscription",
|
||||||
"typeSchema": "public",
|
"typeSchema": "public",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": "'free'"
|
"default": "'free'"
|
||||||
},
|
},
|
||||||
"created_at": {
|
"created_at": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"id": "0550ff2a-d819-4a38-a515-915d5ef620a6",
|
"id": "1a96f2ca-db04-445d-b671-d61aaeef8882",
|
||||||
"prevId": "a8ec7e1e-1087-4ab5-be19-459dc9b0a4e0",
|
"prevId": "c52dbfc1-beae-4a41-8725-66def9fdacea",
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"dialect": "postgresql",
|
"dialect": "postgresql",
|
||||||
"tables": {
|
"tables": {
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
"columns": {
|
"columns": {
|
||||||
"id": {
|
"id": {
|
||||||
"name": "id",
|
"name": "id",
|
||||||
"type": "integer",
|
"type": "serial",
|
||||||
"primaryKey": true,
|
"primaryKey": true,
|
||||||
"notNull": true
|
"notNull": true
|
||||||
},
|
},
|
||||||
@@ -18,78 +18,29 @@
|
|||||||
"name": "name",
|
"name": "name",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": "''"
|
"default": "''"
|
||||||
},
|
},
|
||||||
"name_visible": {
|
"name_visible": {
|
||||||
"name": "name_visible",
|
"name": "name_visible",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": true
|
"default": true
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"name": "icon",
|
"name": "icon",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": "'i-ph:circle-wavy-question-duotone'"
|
"default": "'i-ph:circle-wavy-question-duotone'"
|
||||||
},
|
},
|
||||||
"color": {
|
"color": {
|
||||||
"name": "color",
|
"name": "color",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
|
||||||
"default": "'gray'"
|
|
||||||
},
|
|
||||||
"page_id": {
|
|
||||||
"name": "page_id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
"notNull": false,
|
||||||
"default": "now()"
|
"default": "'gray'"
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"categories_page_id_pages_id_fk": {
|
|
||||||
"name": "categories_page_id_pages_id_fk",
|
|
||||||
"tableFrom": "categories",
|
|
||||||
"tableTo": "pages",
|
|
||||||
"columnsFrom": [
|
|
||||||
"page_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"public.pages": {
|
|
||||||
"name": "pages",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
},
|
||||||
"user_id": {
|
"user_id": {
|
||||||
"name": "user_id",
|
"name": "user_id",
|
||||||
@@ -113,9 +64,9 @@
|
|||||||
},
|
},
|
||||||
"indexes": {},
|
"indexes": {},
|
||||||
"foreignKeys": {
|
"foreignKeys": {
|
||||||
"pages_user_id_users_id_fk": {
|
"categories_user_id_users_id_fk": {
|
||||||
"name": "pages_user_id_users_id_fk",
|
"name": "categories_user_id_users_id_fk",
|
||||||
"tableFrom": "pages",
|
"tableFrom": "categories",
|
||||||
"tableTo": "users",
|
"tableTo": "users",
|
||||||
"columnsFrom": [
|
"columnsFrom": [
|
||||||
"user_id"
|
"user_id"
|
||||||
@@ -136,7 +87,7 @@
|
|||||||
"columns": {
|
"columns": {
|
||||||
"id": {
|
"id": {
|
||||||
"name": "id",
|
"name": "id",
|
||||||
"type": "integer",
|
"type": "serial",
|
||||||
"primaryKey": true,
|
"primaryKey": true,
|
||||||
"notNull": true
|
"notNull": true
|
||||||
},
|
},
|
||||||
@@ -144,30 +95,37 @@
|
|||||||
"name": "name",
|
"name": "name",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": "''"
|
"default": "''"
|
||||||
},
|
},
|
||||||
"name_visible": {
|
"name_visible": {
|
||||||
"name": "name_visible",
|
"name": "name_visible",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": true
|
"default": true
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"name": "icon",
|
"name": "icon",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": "'i-ph:circle-wavy-question-duotone'"
|
"default": "'i-ph:circle-wavy-question-duotone'"
|
||||||
},
|
},
|
||||||
"color": {
|
"color": {
|
||||||
"name": "color",
|
"name": "color",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": "'gray'"
|
"default": "'gray'"
|
||||||
},
|
},
|
||||||
|
"link": {
|
||||||
|
"name": "link",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "''"
|
||||||
|
},
|
||||||
"category_id": {
|
"category_id": {
|
||||||
"name": "category_id",
|
"name": "category_id",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
@@ -213,7 +171,7 @@
|
|||||||
"columns": {
|
"columns": {
|
||||||
"id": {
|
"id": {
|
||||||
"name": "id",
|
"name": "id",
|
||||||
"type": "integer",
|
"type": "serial",
|
||||||
"primaryKey": true,
|
"primaryKey": true,
|
||||||
"notNull": true
|
"notNull": true
|
||||||
},
|
},
|
||||||
@@ -266,33 +224,40 @@
|
|||||||
"notNull": false,
|
"notNull": false,
|
||||||
"default": "''"
|
"default": "''"
|
||||||
},
|
},
|
||||||
|
"avatar": {
|
||||||
|
"name": "avatar",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "''"
|
||||||
|
},
|
||||||
"private": {
|
"private": {
|
||||||
"name": "private",
|
"name": "private",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
"timezone": {
|
"language": {
|
||||||
"name": "timezone",
|
"name": "language",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": "'undefined'"
|
"default": "'en-EN'"
|
||||||
},
|
},
|
||||||
"location": {
|
"location": {
|
||||||
"name": "location",
|
"name": "location",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": "'undefined'"
|
"default": "'unknown'"
|
||||||
},
|
},
|
||||||
"subscription": {
|
"subscription": {
|
||||||
"name": "subscription",
|
"name": "subscription",
|
||||||
"type": "subscription",
|
"type": "subscription",
|
||||||
"typeSchema": "public",
|
"typeSchema": "public",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": true,
|
"notNull": false,
|
||||||
"default": "'free'"
|
"default": "'free'"
|
||||||
},
|
},
|
||||||
"created_at": {
|
"created_at": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"id": "7d4e591a-f6c7-48eb-b9e8-e0e200bfea26",
|
"id": "b9aba4fe-7f04-4acc-b47f-2d29d739df98",
|
||||||
"prevId": "0550ff2a-d819-4a38-a515-915d5ef620a6",
|
"prevId": "1a96f2ca-db04-445d-b671-d61aaeef8882",
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"dialect": "postgresql",
|
"dialect": "postgresql",
|
||||||
"tables": {
|
"tables": {
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
"columns": {
|
"columns": {
|
||||||
"id": {
|
"id": {
|
||||||
"name": "id",
|
"name": "id",
|
||||||
"type": "integer",
|
"type": "serial",
|
||||||
"primaryKey": true,
|
"primaryKey": true,
|
||||||
"notNull": true
|
"notNull": true
|
||||||
},
|
},
|
||||||
@@ -42,55 +42,6 @@
|
|||||||
"notNull": false,
|
"notNull": false,
|
||||||
"default": "'gray'"
|
"default": "'gray'"
|
||||||
},
|
},
|
||||||
"page_id": {
|
|
||||||
"name": "page_id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"categories_page_id_pages_id_fk": {
|
|
||||||
"name": "categories_page_id_pages_id_fk",
|
|
||||||
"tableFrom": "categories",
|
|
||||||
"tableTo": "pages",
|
|
||||||
"columnsFrom": [
|
|
||||||
"page_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"public.pages": {
|
|
||||||
"name": "pages",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"user_id": {
|
"user_id": {
|
||||||
"name": "user_id",
|
"name": "user_id",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
@@ -113,9 +64,9 @@
|
|||||||
},
|
},
|
||||||
"indexes": {},
|
"indexes": {},
|
||||||
"foreignKeys": {
|
"foreignKeys": {
|
||||||
"pages_user_id_users_id_fk": {
|
"categories_user_id_users_id_fk": {
|
||||||
"name": "pages_user_id_users_id_fk",
|
"name": "categories_user_id_users_id_fk",
|
||||||
"tableFrom": "pages",
|
"tableFrom": "categories",
|
||||||
"tableTo": "users",
|
"tableTo": "users",
|
||||||
"columnsFrom": [
|
"columnsFrom": [
|
||||||
"user_id"
|
"user_id"
|
||||||
@@ -136,7 +87,7 @@
|
|||||||
"columns": {
|
"columns": {
|
||||||
"id": {
|
"id": {
|
||||||
"name": "id",
|
"name": "id",
|
||||||
"type": "integer",
|
"type": "serial",
|
||||||
"primaryKey": true,
|
"primaryKey": true,
|
||||||
"notNull": true
|
"notNull": true
|
||||||
},
|
},
|
||||||
@@ -147,12 +98,12 @@
|
|||||||
"notNull": false,
|
"notNull": false,
|
||||||
"default": "''"
|
"default": "''"
|
||||||
},
|
},
|
||||||
"name_visible": {
|
"primary": {
|
||||||
"name": "name_visible",
|
"name": "primary",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": false,
|
"notNull": false,
|
||||||
"default": true
|
"default": false
|
||||||
},
|
},
|
||||||
"icon": {
|
"icon": {
|
||||||
"name": "icon",
|
"name": "icon",
|
||||||
@@ -168,6 +119,13 @@
|
|||||||
"notNull": false,
|
"notNull": false,
|
||||||
"default": "'gray'"
|
"default": "'gray'"
|
||||||
},
|
},
|
||||||
|
"link": {
|
||||||
|
"name": "link",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "''"
|
||||||
|
},
|
||||||
"category_id": {
|
"category_id": {
|
||||||
"name": "category_id",
|
"name": "category_id",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
@@ -213,7 +171,7 @@
|
|||||||
"columns": {
|
"columns": {
|
||||||
"id": {
|
"id": {
|
||||||
"name": "id",
|
"name": "id",
|
||||||
"type": "integer",
|
"type": "serial",
|
||||||
"primaryKey": true,
|
"primaryKey": true,
|
||||||
"notNull": true
|
"notNull": true
|
||||||
},
|
},
|
||||||
@@ -266,6 +224,13 @@
|
|||||||
"notNull": false,
|
"notNull": false,
|
||||||
"default": "''"
|
"default": "''"
|
||||||
},
|
},
|
||||||
|
"avatar": {
|
||||||
|
"name": "avatar",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "''"
|
||||||
|
},
|
||||||
"private": {
|
"private": {
|
||||||
"name": "private",
|
"name": "private",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@@ -273,12 +238,12 @@
|
|||||||
"notNull": false,
|
"notNull": false,
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
"timezone": {
|
"language": {
|
||||||
"name": "timezone",
|
"name": "language",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": false,
|
"notNull": false,
|
||||||
"default": "'undefined'"
|
"default": "'en-EN'"
|
||||||
},
|
},
|
||||||
"location": {
|
"location": {
|
||||||
"name": "location",
|
"name": "location",
|
||||||
|
|||||||
@@ -1,364 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "d4ae60ba-5be1-4aa9-90d7-0690a599bf8e",
|
|
||||||
"prevId": "7d4e591a-f6c7-48eb-b9e8-e0e200bfea26",
|
|
||||||
"version": "7",
|
|
||||||
"dialect": "postgresql",
|
|
||||||
"tables": {
|
|
||||||
"public.categories": {
|
|
||||||
"name": "categories",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "''"
|
|
||||||
},
|
|
||||||
"name_visible": {
|
|
||||||
"name": "name_visible",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"icon": {
|
|
||||||
"name": "icon",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "'i-ph:circle-wavy-question-duotone'"
|
|
||||||
},
|
|
||||||
"color": {
|
|
||||||
"name": "color",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "'gray'"
|
|
||||||
},
|
|
||||||
"page_id": {
|
|
||||||
"name": "page_id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"categories_page_id_pages_id_fk": {
|
|
||||||
"name": "categories_page_id_pages_id_fk",
|
|
||||||
"tableFrom": "categories",
|
|
||||||
"tableTo": "pages",
|
|
||||||
"columnsFrom": [
|
|
||||||
"page_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"public.pages": {
|
|
||||||
"name": "pages",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"pages_user_id_users_id_fk": {
|
|
||||||
"name": "pages_user_id_users_id_fk",
|
|
||||||
"tableFrom": "pages",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"public.tabs": {
|
|
||||||
"name": "tabs",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "''"
|
|
||||||
},
|
|
||||||
"name_visible": {
|
|
||||||
"name": "name_visible",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"icon": {
|
|
||||||
"name": "icon",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "'i-ph:circle-wavy-question-duotone'"
|
|
||||||
},
|
|
||||||
"color": {
|
|
||||||
"name": "color",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "'gray'"
|
|
||||||
},
|
|
||||||
"category_id": {
|
|
||||||
"name": "category_id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"tabs_category_id_categories_id_fk": {
|
|
||||||
"name": "tabs_category_id_categories_id_fk",
|
|
||||||
"tableFrom": "tabs",
|
|
||||||
"tableTo": "categories",
|
|
||||||
"columnsFrom": [
|
|
||||||
"category_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"public.users": {
|
|
||||||
"name": "users",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"username": {
|
|
||||||
"name": "username",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"name": "email",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"github_id": {
|
|
||||||
"name": "github_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"github_token": {
|
|
||||||
"name": "github_token",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"google_id": {
|
|
||||||
"name": "google_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"google_token": {
|
|
||||||
"name": "google_token",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"name": "description",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "''"
|
|
||||||
},
|
|
||||||
"avatar": {
|
|
||||||
"name": "avatar",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "''"
|
|
||||||
},
|
|
||||||
"private": {
|
|
||||||
"name": "private",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"timezone": {
|
|
||||||
"name": "timezone",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "'undefined'"
|
|
||||||
},
|
|
||||||
"location": {
|
|
||||||
"name": "location",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "'unknown'"
|
|
||||||
},
|
|
||||||
"subscription": {
|
|
||||||
"name": "subscription",
|
|
||||||
"type": "subscription",
|
|
||||||
"typeSchema": "public",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "'free'"
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {
|
|
||||||
"users_email_unique": {
|
|
||||||
"name": "users_email_unique",
|
|
||||||
"nullsNotDistinct": false,
|
|
||||||
"columns": [
|
|
||||||
"email"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"users_github_id_unique": {
|
|
||||||
"name": "users_github_id_unique",
|
|
||||||
"nullsNotDistinct": false,
|
|
||||||
"columns": [
|
|
||||||
"github_id"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"users_google_id_unique": {
|
|
||||||
"name": "users_google_id_unique",
|
|
||||||
"nullsNotDistinct": false,
|
|
||||||
"columns": [
|
|
||||||
"google_id"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"enums": {
|
|
||||||
"public.subscription": {
|
|
||||||
"name": "subscription",
|
|
||||||
"schema": "public",
|
|
||||||
"values": [
|
|
||||||
"free",
|
|
||||||
"paid"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"schemas": {},
|
|
||||||
"sequences": {},
|
|
||||||
"_meta": {
|
|
||||||
"columns": {},
|
|
||||||
"schemas": {},
|
|
||||||
"tables": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,364 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "704c03b2-8d7f-47ce-a551-95289048c5f2",
|
|
||||||
"prevId": "d4ae60ba-5be1-4aa9-90d7-0690a599bf8e",
|
|
||||||
"version": "7",
|
|
||||||
"dialect": "postgresql",
|
|
||||||
"tables": {
|
|
||||||
"public.categories": {
|
|
||||||
"name": "categories",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "''"
|
|
||||||
},
|
|
||||||
"name_visible": {
|
|
||||||
"name": "name_visible",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"icon": {
|
|
||||||
"name": "icon",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "'i-ph:circle-wavy-question-duotone'"
|
|
||||||
},
|
|
||||||
"color": {
|
|
||||||
"name": "color",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "'gray'"
|
|
||||||
},
|
|
||||||
"page_id": {
|
|
||||||
"name": "page_id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"categories_page_id_pages_id_fk": {
|
|
||||||
"name": "categories_page_id_pages_id_fk",
|
|
||||||
"tableFrom": "categories",
|
|
||||||
"tableTo": "pages",
|
|
||||||
"columnsFrom": [
|
|
||||||
"page_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"public.pages": {
|
|
||||||
"name": "pages",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"pages_user_id_users_id_fk": {
|
|
||||||
"name": "pages_user_id_users_id_fk",
|
|
||||||
"tableFrom": "pages",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"public.tabs": {
|
|
||||||
"name": "tabs",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "''"
|
|
||||||
},
|
|
||||||
"name_visible": {
|
|
||||||
"name": "name_visible",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"icon": {
|
|
||||||
"name": "icon",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "'i-ph:circle-wavy-question-duotone'"
|
|
||||||
},
|
|
||||||
"color": {
|
|
||||||
"name": "color",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "'gray'"
|
|
||||||
},
|
|
||||||
"category_id": {
|
|
||||||
"name": "category_id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"tabs_category_id_categories_id_fk": {
|
|
||||||
"name": "tabs_category_id_categories_id_fk",
|
|
||||||
"tableFrom": "tabs",
|
|
||||||
"tableTo": "categories",
|
|
||||||
"columnsFrom": [
|
|
||||||
"category_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"public.users": {
|
|
||||||
"name": "users",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"username": {
|
|
||||||
"name": "username",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"name": "email",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"github_id": {
|
|
||||||
"name": "github_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"github_token": {
|
|
||||||
"name": "github_token",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"google_id": {
|
|
||||||
"name": "google_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"google_token": {
|
|
||||||
"name": "google_token",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"name": "description",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "''"
|
|
||||||
},
|
|
||||||
"avatar": {
|
|
||||||
"name": "avatar",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "''"
|
|
||||||
},
|
|
||||||
"private": {
|
|
||||||
"name": "private",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"language": {
|
|
||||||
"name": "language",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "'english'"
|
|
||||||
},
|
|
||||||
"location": {
|
|
||||||
"name": "location",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "'unknown'"
|
|
||||||
},
|
|
||||||
"subscription": {
|
|
||||||
"name": "subscription",
|
|
||||||
"type": "subscription",
|
|
||||||
"typeSchema": "public",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "'free'"
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {
|
|
||||||
"users_email_unique": {
|
|
||||||
"name": "users_email_unique",
|
|
||||||
"nullsNotDistinct": false,
|
|
||||||
"columns": [
|
|
||||||
"email"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"users_github_id_unique": {
|
|
||||||
"name": "users_github_id_unique",
|
|
||||||
"nullsNotDistinct": false,
|
|
||||||
"columns": [
|
|
||||||
"github_id"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"users_google_id_unique": {
|
|
||||||
"name": "users_google_id_unique",
|
|
||||||
"nullsNotDistinct": false,
|
|
||||||
"columns": [
|
|
||||||
"google_id"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"enums": {
|
|
||||||
"public.subscription": {
|
|
||||||
"name": "subscription",
|
|
||||||
"schema": "public",
|
|
||||||
"values": [
|
|
||||||
"free",
|
|
||||||
"paid"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"schemas": {},
|
|
||||||
"sequences": {},
|
|
||||||
"_meta": {
|
|
||||||
"columns": {},
|
|
||||||
"schemas": {},
|
|
||||||
"tables": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,364 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "e891a8e0-61c1-4351-90fe-caace29457a8",
|
|
||||||
"prevId": "704c03b2-8d7f-47ce-a551-95289048c5f2",
|
|
||||||
"version": "7",
|
|
||||||
"dialect": "postgresql",
|
|
||||||
"tables": {
|
|
||||||
"public.categories": {
|
|
||||||
"name": "categories",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "''"
|
|
||||||
},
|
|
||||||
"name_visible": {
|
|
||||||
"name": "name_visible",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"icon": {
|
|
||||||
"name": "icon",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "'i-ph:circle-wavy-question-duotone'"
|
|
||||||
},
|
|
||||||
"color": {
|
|
||||||
"name": "color",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "'gray'"
|
|
||||||
},
|
|
||||||
"page_id": {
|
|
||||||
"name": "page_id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"categories_page_id_pages_id_fk": {
|
|
||||||
"name": "categories_page_id_pages_id_fk",
|
|
||||||
"tableFrom": "categories",
|
|
||||||
"tableTo": "pages",
|
|
||||||
"columnsFrom": [
|
|
||||||
"page_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"public.pages": {
|
|
||||||
"name": "pages",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"pages_user_id_users_id_fk": {
|
|
||||||
"name": "pages_user_id_users_id_fk",
|
|
||||||
"tableFrom": "pages",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"public.tabs": {
|
|
||||||
"name": "tabs",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "''"
|
|
||||||
},
|
|
||||||
"name_visible": {
|
|
||||||
"name": "name_visible",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"icon": {
|
|
||||||
"name": "icon",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "'i-ph:circle-wavy-question-duotone'"
|
|
||||||
},
|
|
||||||
"color": {
|
|
||||||
"name": "color",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "'gray'"
|
|
||||||
},
|
|
||||||
"category_id": {
|
|
||||||
"name": "category_id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {
|
|
||||||
"tabs_category_id_categories_id_fk": {
|
|
||||||
"name": "tabs_category_id_categories_id_fk",
|
|
||||||
"tableFrom": "tabs",
|
|
||||||
"tableTo": "categories",
|
|
||||||
"columnsFrom": [
|
|
||||||
"category_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"onDelete": "cascade",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {}
|
|
||||||
},
|
|
||||||
"public.users": {
|
|
||||||
"name": "users",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "integer",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"username": {
|
|
||||||
"name": "username",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"name": "email",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"github_id": {
|
|
||||||
"name": "github_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"github_token": {
|
|
||||||
"name": "github_token",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"google_id": {
|
|
||||||
"name": "google_id",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"google_token": {
|
|
||||||
"name": "google_token",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"name": "description",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "''"
|
|
||||||
},
|
|
||||||
"avatar": {
|
|
||||||
"name": "avatar",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "''"
|
|
||||||
},
|
|
||||||
"private": {
|
|
||||||
"name": "private",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"language": {
|
|
||||||
"name": "language",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "'en-EN'"
|
|
||||||
},
|
|
||||||
"location": {
|
|
||||||
"name": "location",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "'unknown'"
|
|
||||||
},
|
|
||||||
"subscription": {
|
|
||||||
"name": "subscription",
|
|
||||||
"type": "subscription",
|
|
||||||
"typeSchema": "public",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "'free'"
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamp (3)",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {
|
|
||||||
"users_email_unique": {
|
|
||||||
"name": "users_email_unique",
|
|
||||||
"nullsNotDistinct": false,
|
|
||||||
"columns": [
|
|
||||||
"email"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"users_github_id_unique": {
|
|
||||||
"name": "users_github_id_unique",
|
|
||||||
"nullsNotDistinct": false,
|
|
||||||
"columns": [
|
|
||||||
"github_id"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"users_google_id_unique": {
|
|
||||||
"name": "users_google_id_unique",
|
|
||||||
"nullsNotDistinct": false,
|
|
||||||
"columns": [
|
|
||||||
"google_id"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"enums": {
|
|
||||||
"public.subscription": {
|
|
||||||
"name": "subscription",
|
|
||||||
"schema": "public",
|
|
||||||
"values": [
|
|
||||||
"free",
|
|
||||||
"paid"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"schemas": {},
|
|
||||||
"sequences": {},
|
|
||||||
"_meta": {
|
|
||||||
"columns": {},
|
|
||||||
"schemas": {},
|
|
||||||
"tables": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,43 +5,22 @@
|
|||||||
{
|
{
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"when": 1724455773734,
|
"when": 1724865045534,
|
||||||
"tag": "0000_wild_luke_cage",
|
"tag": "0000_giant_stranger",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"when": 1724455851539,
|
"when": 1724884620789,
|
||||||
"tag": "0001_goofy_dormammu",
|
"tag": "0001_fancy_tyger_tiger",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 2,
|
"idx": 2,
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"when": 1724456130150,
|
"when": 1725015619221,
|
||||||
"tag": "0002_slim_whistler",
|
"tag": "0002_cool_dexter_bennett",
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 3,
|
|
||||||
"version": "7",
|
|
||||||
"when": 1724528975297,
|
|
||||||
"tag": "0003_curious_solo",
|
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 4,
|
|
||||||
"version": "7",
|
|
||||||
"when": 1724531645621,
|
|
||||||
"tag": "0004_sharp_shocker",
|
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 5,
|
|
||||||
"version": "7",
|
|
||||||
"when": 1724532003950,
|
|
||||||
"tag": "0005_tense_the_order",
|
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -23,53 +23,39 @@ export const users = pgTable('users', {
|
|||||||
...timestamps,
|
...timestamps,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const pages = pgTable('pages', {
|
|
||||||
id,
|
|
||||||
userId: integer('user_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => users.id, { onDelete: 'cascade' }),
|
|
||||||
...timestamps,
|
|
||||||
})
|
|
||||||
|
|
||||||
export const categories = pgTable('categories', {
|
export const categories = pgTable('categories', {
|
||||||
id,
|
id,
|
||||||
name: text('name').default(''),
|
name: text('name').default(''),
|
||||||
nameVisible: boolean('name_visible').default(true),
|
nameVisible: boolean('name_visible').default(true),
|
||||||
icon: text('icon').default('i-ph:circle-wavy-question-duotone'),
|
icon: text('icon').default('i-ph:circle-wavy-question-duotone'),
|
||||||
color: text('color').default('gray'),
|
color: text('color').default('gray'),
|
||||||
pageId: integer('page_id')
|
userId: integer('user_id')
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => pages.id, { onDelete: 'cascade' }),
|
.references(() => users.id, { onDelete: 'cascade' }),
|
||||||
...timestamps,
|
...timestamps,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const tabs = pgTable('tabs', {
|
export const tabs = pgTable('tabs', {
|
||||||
id,
|
id,
|
||||||
name: text('name').default(''),
|
name: text('name').default(''),
|
||||||
nameVisible: boolean('name_visible').default(true),
|
primary: boolean('primary').default(false),
|
||||||
icon: text('icon').default('i-ph:circle-wavy-question-duotone'),
|
icon: text('icon').default('i-ph:circle-wavy-question-duotone'),
|
||||||
color: text('color').default('gray'),
|
color: text('color').default('gray'),
|
||||||
|
link: text('link').default(''),
|
||||||
categoryId: integer('category_id')
|
categoryId: integer('category_id')
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => categories.id, { onDelete: 'cascade' }),
|
.references(() => categories.id, { onDelete: 'cascade' }),
|
||||||
...timestamps,
|
...timestamps,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const usersRelations = relations(users, ({ one }) => ({
|
export const usersRelations = relations(users, ({ many }) => ({
|
||||||
page: one(pages, {
|
|
||||||
fields: [users.id],
|
|
||||||
references: [pages.userId],
|
|
||||||
}),
|
|
||||||
}))
|
|
||||||
|
|
||||||
export const pagesRelations = relations(pages, ({ many }) => ({
|
|
||||||
categories: many(categories),
|
categories: many(categories),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
export const categoriesRelations = relations(categories, ({ one, many }) => ({
|
export const categoriesRelations = relations(categories, ({ one, many }) => ({
|
||||||
page: one(pages, {
|
user: one(users, {
|
||||||
fields: [categories.pageId],
|
fields: [categories.userId],
|
||||||
references: [pages.id],
|
references: [users.id],
|
||||||
}),
|
}),
|
||||||
tabs: many(tabs),
|
tabs: many(tabs),
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export default oauthGoogleEventHandler({
|
export default oauthGoogleEventHandler({
|
||||||
config: {
|
config: {
|
||||||
emailRequired: true,
|
emailRequired: true,
|
||||||
|
scope: ['email', 'profile'],
|
||||||
},
|
},
|
||||||
async onSuccess(event, { user: oauthUser, tokens }) {
|
async onSuccess(event, { user: oauthUser, tokens }) {
|
||||||
const userSession = await getUserSession(event)
|
const userSession = await getUserSession(event)
|
||||||
@@ -15,7 +16,7 @@ export default oauthGoogleEventHandler({
|
|||||||
googleToken: tokens.access_token,
|
googleToken: tokens.access_token,
|
||||||
})
|
})
|
||||||
|
|
||||||
await replaceUserSession(event, {
|
await setUserSession(event, {
|
||||||
id: userSession.id,
|
id: userSession.id,
|
||||||
user: userSession,
|
user: userSession,
|
||||||
googleId: oauthUser.sub,
|
googleId: oauthUser.sub,
|
||||||
@@ -35,7 +36,7 @@ export default oauthGoogleEventHandler({
|
|||||||
googleToken: tokens.access_token,
|
googleToken: tokens.access_token,
|
||||||
})
|
})
|
||||||
|
|
||||||
await replaceUserSession(event, {
|
await setUserSession(event, {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
user,
|
user,
|
||||||
})
|
})
|
||||||
@@ -76,7 +77,7 @@ export default oauthGoogleEventHandler({
|
|||||||
subscription: 'free',
|
subscription: 'free',
|
||||||
})
|
})
|
||||||
|
|
||||||
await replaceUserSession(event, {
|
await setUserSession(event, {
|
||||||
id: createdUser.id,
|
id: createdUser.id,
|
||||||
user: createdUser,
|
user: createdUser,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -10,9 +10,3 @@ export function useDrizzle() {
|
|||||||
const config = useRuntimeConfig()
|
const config = useRuntimeConfig()
|
||||||
return drizzle(postgres(config.postgres.url, { prepare: false }), { schema })
|
return drizzle(postgres(config.postgres.url, { prepare: false }), { schema })
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserType = typeof schema.users.$inferSelect
|
|
||||||
export type UserInsert = typeof schema.users.$inferInsert
|
|
||||||
|
|
||||||
export type TabType = typeof schema.tabs.$inferSelect
|
|
||||||
export type CategoryType = typeof schema.categories.$inferSelect
|
|
||||||
|
|||||||
@@ -1,18 +1,11 @@
|
|||||||
import * as pg from 'drizzle-orm/pg-core'
|
import { serial, timestamp } from 'drizzle-orm/pg-core'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A centralized list of standardized Drizzle ORM schema field definitions to prevent duplication errors
|
* A centralized list of standardized Drizzle ORM schema field definitions to prevent duplication errors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const createdAt = pg
|
export const createdAt = timestamp('created_at', { mode: 'date', precision: 3 }).defaultNow()
|
||||||
.timestamp('created_at', { mode: 'date', precision: 3 })
|
export const updatedAt = timestamp('updated_at', { mode: 'date', precision: 3 }).$onUpdate(() => new Date())
|
||||||
.defaultNow()
|
export const id = serial('id').primaryKey()
|
||||||
|
|
||||||
export const updatedAt = pg
|
|
||||||
.timestamp('updated_at', { mode: 'date', precision: 3 })
|
|
||||||
.$onUpdate(() => new Date())
|
|
||||||
|
|
||||||
export const id = pg.integer('id').primaryKey({ autoIncrement: true })
|
|
||||||
|
|
||||||
export const timestamps = {
|
export const timestamps = {
|
||||||
createdAt,
|
createdAt,
|
||||||
|
|||||||
@@ -1,7 +1,66 @@
|
|||||||
import type { ParsedContent } from '@nuxt/content'
|
import type { ParsedContent } from '@nuxt/content'
|
||||||
|
import { z } from 'zod'
|
||||||
|
|
||||||
|
export const COLORS = ['gray', 'slate', 'zinc', 'neutral', 'stone', 'red', 'orange', 'amber', 'yellow', 'lime', 'green', 'emerald', 'teal', 'cyan', 'sky', 'blue', 'indigo', 'violet', 'purple', 'fuchsia', 'pink', 'rose']
|
||||||
|
|
||||||
export const Subscription = ['free', 'paid'] as const
|
export const Subscription = ['free', 'paid'] as const
|
||||||
|
|
||||||
|
// Category
|
||||||
|
export const CreateCategorySchema = z.object({
|
||||||
|
name: z.string().min(4),
|
||||||
|
icon: z.string(),
|
||||||
|
color: z.enum(COLORS).default('gray'),
|
||||||
|
nameVisible: z.boolean().optional().default(false),
|
||||||
|
})
|
||||||
|
export const CreateCategorySchemaType = z.infer<typeof CreateCategorySchema>
|
||||||
|
|
||||||
|
export const UpdateCategorySchema = z.object({
|
||||||
|
name: z.string().min(4).optional(),
|
||||||
|
icon: z.string().optional(),
|
||||||
|
color: z.string().optional(),
|
||||||
|
nameVisible: z.boolean().optional().default(false),
|
||||||
|
})
|
||||||
|
export const UpdateCategorySchemaType = z.infer<typeof UpdateCategorySchema>
|
||||||
|
|
||||||
|
export interface CategoryType {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
icon: string
|
||||||
|
color: string
|
||||||
|
nameVisible: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tab
|
||||||
|
export const CreateTabSchema = z.object({
|
||||||
|
name: z.string().min(4),
|
||||||
|
icon: z.string(),
|
||||||
|
color: z.enum(COLORS).default('gray'),
|
||||||
|
primary: z.boolean().optional().default(false),
|
||||||
|
link: z.string(),
|
||||||
|
categoryId: z.number(),
|
||||||
|
})
|
||||||
|
export const CreateTabSchemaType = z.infer<typeof CreateTabSchema>
|
||||||
|
|
||||||
|
export const UpdateTabSchema = z.object({
|
||||||
|
name: z.string().min(4).optional(),
|
||||||
|
icon: z.string().optional(),
|
||||||
|
color: z.enum(COLORS).default('gray').optional(),
|
||||||
|
primary: z.boolean().optional().default(false),
|
||||||
|
link: z.string().optional(),
|
||||||
|
categoryId: z.number(),
|
||||||
|
})
|
||||||
|
export const UpdateTabSchemaType = z.infer<typeof UpdateTabSchema>
|
||||||
|
|
||||||
|
export interface TabType {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
icon: string
|
||||||
|
color: string
|
||||||
|
primary: boolean
|
||||||
|
categoryId: number
|
||||||
|
link: string
|
||||||
|
}
|
||||||
|
|
||||||
// todo: delete
|
// todo: delete
|
||||||
export interface AppType extends ParsedContent {
|
export interface AppType extends ParsedContent {
|
||||||
primary?: boolean
|
primary?: boolean
|
||||||
|
|||||||
Reference in New Issue
Block a user