Skip to content

Commit

Permalink
Merge pull request #92 from botmaster/refactor/no-ref/refactor-stores
Browse files Browse the repository at this point in the history
refactor: Refactor stores
  • Loading branch information
botmaster authored Feb 12, 2024
2 parents ebed8bb + 83d9a50 commit f5201ce
Show file tree
Hide file tree
Showing 19 changed files with 271 additions and 87 deletions.
2 changes: 2 additions & 0 deletions assets/scss/vendors/_tailwindcss-form.scss
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// Select
.form-select, .form-multiselect {
@apply block w-full rounded-md border-0 py-1.5 text-body-txt shadow-sm ring-1 ring-inset ring-polarnight-nord-3
bg-white dark:bg-polarnight-nord-0 placeholder-body-txt
focus:ring-2 focus:ring-inset focus:ring-accent sm:max-w-xs sm:text-sm sm:leading-6;
}

input[type="text"].form-input, .input-text {
@apply block w-full rounded-md border-0 py-1.5 text-body-txt shadow-sm ring-1 ring-inset ring-polarnight-nord-3
bg-white dark:bg-polarnight-nord-0 placeholder-body-txt
focus:ring-2 focus:ring-inset focus:ring-accent sm:max-w-xs sm:text-sm sm:leading-6;
}

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ function displayValue(options: IMultiSelectTagOption[]) {
>
<div class="multi-select__options-header">
<ComboboxInput
class="multi-select__search-input"
class="multi-select__search-input form-input"
placeholder="Search..."
@change="query = $event.target.value"
/>
Expand Down Expand Up @@ -106,7 +106,7 @@ function displayValue(options: IMultiSelectTagOption[]) {

<style scoped lang="scss">
.multi-select {
@apply w-full relative flex items-center bg-white min-w-48 p-0;
@apply w-full relative flex items-center min-w-48 p-0;
&__values-container {
@apply min-w-0 max-w-64 flex-grow;
Expand All @@ -128,19 +128,19 @@ function displayValue(options: IMultiSelectTagOption[]) {
}
&__options {
@apply absolute left-0 top-[100%] z-10 mt-1 w-full rounded-md bg-white text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm;
@apply absolute left-0 top-[100%] z-10 mt-1 w-full rounded-md bg-white dark:bg-polarnight-nord-0 text-base shadow-lg ring-1 ring-inset ring-polarnight-nord-3 focus:outline-none sm:text-sm;
}
&__options-header {
@apply px-2 py-2;
}
&__search-input {
@apply w-full px-3 py-1.5 text-body-txt border-0 rounded-md shadow-sm;
@apply w-full;
}
&__option-list {
@apply relative overflow-auto max-h-60;
@apply relative overflow-auto max-h-60 mr-px;
}
&__options-footer {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
64 changes: 64 additions & 0 deletions components/app/readings/ArticleCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<script setup lang="ts">
import type { IArticle } from '~/types/types';
defineProps<{
item: IArticle
}>();
const { locale: currentLocale } = useI18n();
// Computed - Map status color to tailwind color
const statusColor = computed<Record<string, string>>(() => {
return {
red: 'badge--is-danger',
green: 'badge--is-success',
blue: 'badge--is-info',
};
});
</script>

<template>
<AppCard
:data-pageid="item.id"
class="h-full"
>
<template #image>
<img v-if="item.image" loading="lazy" :src="item.image" alt="">
<div v-if="item.status" class="absolute right-2 top-1.5">
<span
class="badge shadow-md"
:class="item.status.color ? statusColor[item.status.color] : 'badge--is-neutral'"
>{{
item.status.name
}}</span>
</div>
</template>

<h3 class="font-body text-base font-bold capitalize leading-tight">
<span class="mr-[0.3em]">{{ item.title }}</span>
</h3>
<p v-if="item.tags.length" class="mt-2 flex flex-wrap gap-2">
<span
v-for="tag in item.tags"
:key="tag.id"
class="badge badge--is-neutral badge--is-small"
>{{ tag.name }}</span>
</p>
<p v-if="item.score" class="mt-2 text-xs">
{{ item.score.name }}
</p>

<template #footer>
<p v-if="item.createdTime" class="mt-2 text-xs font-normal text-muted-text">
{{ new Date(item.createdTime).toLocaleDateString(currentLocale) }}
</p>
<p class="truncate">
<Icon name="material-symbols:link" class="text-lg" /> <a :href="item.url" target="_blank">{{ item.url }}</a>
</p>
</template>
</AppCard>
</template>

<style scoped lang="scss">
</style>
File renamed without changes.
23 changes: 23 additions & 0 deletions error.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script setup lang="ts">
import type { NuxtError } from '#app';
const props = defineProps({
error: Object as () => NuxtError,
});
const localePath = useLocalePath();
</script>

<template>
<div>
<NuxtLayout>
<div class="container mx-auto mb-12 mt-8 md:mb-24 md:mt-20">
<h1>{{ error?.statusCode }}</h1>
<pre v-if="error">{{ error }}</pre>
<NuxtLink :to="localePath({ name: 'index' })">
Go back home
</NuxtLink>
</div>
</NuxtLayout>
</div>
</template>
89 changes: 13 additions & 76 deletions pages/readings.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
<script setup lang="ts">
import { useRouteQuery } from '@vueuse/router';
import type { PartialDatabaseObjectResponse } from '@notionhq/client/build/src/api-endpoints';
import type { IArticle, IArticleTag, IPage } from '~/types/types';
import { useArticleDatabaseInfoStore } from '~/stores/article-database-info.store';
const config = useRuntimeConfig();
const { locale: currentLocale, t } = useI18n();
// Fetch Content data
Expand All @@ -30,34 +29,14 @@ const search = useRouteQuery('search', '', { transform: String });
const sort = useRouteQuery('sort', 'Created time', { transform: String });
// Pinia store
const databaseStore = useArticleDatabaseInfoStore();
const articlesStore = useArticlesStore();
// Fetch database info
const { data: database } = await useAsyncData('database-info', () =>
$fetch('/api/notion-database-info', {
params: {
database_id: config.public.notionDatabaseId,
},
method: 'GET',
}));
// Computed - Get "tags" multi-select value from dbResponse
const tagList = computed<IArticleTag[]>(() => {
const data = database.value as PartialDatabaseObjectResponse;
const selectProperty = data.properties.Tags as {
type: 'multi_select'
multi_select: {
options: Array<IArticleTag>
}
id: string
name: string
};
return selectProperty.multi_select.options || [];
});
const { error: fetchDatabaseError, pending: fetchDatabasePending } = await useAsyncData('database-info', () => databaseStore.fetchDatabase());
// Fetch page list
const { error, pending, refresh } = await useAsyncData('page-list', () => articlesStore.fetchArticles({
database_id: config.public.notionDatabaseId,
page_size: pageSize.value,
start_cursor: cursor.value,
sorts: [
Expand Down Expand Up @@ -101,18 +80,9 @@ function clearFilters() {
function loadMore() {
isLoadingMore.value = true;
cursor.value = articlesStore.articlesResponse?.response.response.next_cursor || null;
cursor.value = articlesStore.articlesResponse?.response.next_cursor || null;
}
// Computed - Map status color to tailwind color
const statusColor = computed<Record<string, string>>(() => {
return {
red: 'badge--is-danger',
green: 'badge--is-success',
blue: 'badge--is-info',
};
});
watch(
() => articlesStore.articlesResponse,
async (newVal) => {
Expand Down Expand Up @@ -222,18 +192,23 @@ watch(
<div class="container mx-auto mb-12 mt-8 md:mb-24 md:mt-20">
<ContentRenderer v-if="contentData" class="nuxt-content mb-10" :value="contentData" />
<div class="flow">
<template v-if="error">
<template v-if="error || fetchDatabaseError">
<p class="font-code">
<Icon name="material-symbols:error" class="text-xl text-danger" />
Oups
</p>
<p>{{ error }}</p>
<p v-if="error">
{{ error }}
</p>
<p v-if="fetchDatabaseError">
{{ fetchDatabaseError }}
</p>
</template>
<template v-else>
<!-- Action bar -->
<ArticleListActionBar
v-model:selected-options="selectedTags" v-model:search="search" v-model:status="status"
v-model:sort="sort" :tags="tagList" :pending="pending" @clear-filters="clearFilters"
v-model:sort="sort" :tags="databaseStore.tagList" :pending="pending || fetchDatabasePending" @clear-filters="clearFilters"
/>

<!-- Article list -->
Expand All @@ -243,45 +218,7 @@ watch(
class="card-layout relative mt-12 "
>
<li v-for="item in pageListCollection" :key="item.id as string">
<AppCard
:data-pageid="item.id"
class="h-full"
>
<template #image>
<img v-if="item.image" loading="lazy" :src="item.image" alt="">
<div v-if="item.status" class="absolute right-2 top-1.5">
<span
class="badge shadow-md"
:class="item.status.color ? statusColor[item.status.color] : 'badge--is-neutral'"
>{{
item.status.name
}}</span>
</div>
</template>

<h3 class="font-body text-base font-bold capitalize leading-tight">
<span class="mr-[0.3em]">{{ item.title }}</span>
</h3>
<p v-if="item.tags.length" class="mt-2 flex flex-wrap gap-2">
<span
v-for="tag in item.tags"
:key="tag.id"
class="badge badge--is-neutral badge--is-small"
>{{ tag.name }}</span>
</p>
<p v-if="item.score" class="mt-2 text-xs">
{{ item.score.name }}
</p>

<template #footer>
<p v-if="item.createdTime" class="mt-2 text-xs font-normal text-muted-text">
{{ new Date(item.createdTime).toLocaleDateString(currentLocale) }}
</p>
<p class="truncate">
<Icon name="material-symbols:link" class="text-lg" /> <a :href="item.url" target="_blank">{{ item.url }}</a>
</p>
</template>
</AppCard>
<ArticleCard :item="item" />
</li>
</TransitionGroup>
<template v-else>
Expand Down
22 changes: 20 additions & 2 deletions pages/test.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
<script setup lang="ts"></script>
<script setup lang="ts">
// const { data, error } = await useAsyncData('test', () => $fetch('/api/test'));
import { useTestStore } from '~/stores/test.store';
const testStore = useTestStore();
const { data, error } = await useAsyncData('test', () => testStore.fetchTest());
</script>

<template>
<h1>test</h1>
<div v-if="data">
<p>Data:</p>
-> {{ data }}
</div>
<div v-if="error">
<p>Error:</p>
-> {{ error }}
</div>
</template>

<style scoped lang="scss">
h1 {
color: red;
}
</style>
6 changes: 6 additions & 0 deletions server/api/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default defineEventHandler(async (event) => {
throw createError({
statusCode: 502,
statusMessage: 'Something bad happened on the server',
});
});
71 changes: 71 additions & 0 deletions stores/article-database-info.store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* Pinia Article Database store
*/
import type { GetDatabaseResponse } from '@notionhq/client/build/src/api-endpoints';
import { FetchError } from 'ofetch';
import type { IArticleTag } from '~/types/types';

export const useArticleDatabaseInfoStore = defineStore('articleDatabaseInfo', () => {
const config = useRuntimeConfig();

// State
const database = ref<GetDatabaseResponse>();
const loading = ref(false);
const error = ref<null | Error>(null);

// Actions
const fetchDatabase = async () => {
loading.value = true;

try {
const response = await $fetch('/api/notion-database-info', {
params: {
database_id: config.public.notionDatabaseId,
},
method: 'GET',
});
database.value = response;
return response;
}

catch (e) {
if (e instanceof FetchError) {
error.value = e.data;
throw e;
}
else {
error.value = e as Error;
return e;
}
}

finally {
loading.value = false;
}
};

// Getters
// Get "tags" multi-select value from dbResponse
const tagList = computed<IArticleTag[]>(() => {
const selectProperty = database.value?.properties?.Tags as {
type: 'multi_select'
multi_select: {
options: Array<IArticleTag>
}
id: string
name: string
};
return selectProperty?.multi_select?.options || [];
});

return {
loading,
error,
database,
fetchDatabase,
tagList,
};
});

if (import.meta.hot)
import.meta.hot.accept(acceptHMRUpdate(useArticleDatabaseInfoStore, import.meta.hot));
Loading

0 comments on commit f5201ce

Please sign in to comment.