Skip to content

Commit

Permalink
Merge pull request #101
Browse files Browse the repository at this point in the history
feature/no-ref/add-readings-type
  • Loading branch information
botmaster authored Mar 3, 2024
2 parents b879ba3 + 9508edf commit 5d99693
Show file tree
Hide file tree
Showing 9 changed files with 335 additions and 171 deletions.
8 changes: 6 additions & 2 deletions assets/scss/vendors/_tailwindcss-form.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@
.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;
focus:ring-2 focus:ring-inset focus:ring-accent text-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;
focus:ring-2 focus:ring-inset focus:ring-accent text-sm leading-6;
}

input[type="checkbox"] {
@apply rounded border-b-polarnight-nord-0 text-body-txt shadow-sm focus:border-accent focus:ring focus:ring-offset-0 focus:ring-accent focus:ring-opacity-50;
}

label {
@apply block text-sm;
}
126 changes: 79 additions & 47 deletions components/app/readings/ArticleListActionBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ const { t } = useI18n();
const selectedOptions = defineModel<IArticleTag[]>('selectedOptions', { required: true });
const search = defineModel<string>('search', { required: true });
const status = defineModel<string>('status', { required: true });
const type = defineModel<string>('type', { required: true });
const sort = defineModel<string>('sort', { required: true });
// Computed - Has any filters
const hasFilters = computed<boolean>(() => {
return status.value !== '' || search.value !== '' || selectedOptions.value.length > 0;
return status.value !== '' || type.value !== '' || search.value !== '' || selectedOptions.value.length > 0;
});
// Map status
Expand All @@ -34,6 +35,16 @@ const mapStatus = computed<Record<string, string>>(() => {
};
});
// Map type
const mapType = computed<Record<string, string>>(() => {
return {
all: t('pages.readings.filters.type.all'),
Article: t('pages.readings.filters.type.article'),
Podcast: t('pages.readings.filters.type.podcast'),
Video: t('pages.readings.filters.type.video'),
};
});
// Map sort
const mapSort = computed<Record<string, string>>(() => {
return {
Expand All @@ -47,67 +58,88 @@ const mapSort = computed<Record<string, string>>(() => {

<template>
<div>
<div class="flex flex-col flex-wrap gap-x-4 gap-y-1.5 md:flex-row md:items-end">
<div class="md:mr-8">
<div class="flex flex-col gap-x-4 gap-y-2 lg:flex-row lg:items-end lg:justify-between">
<div class="flex-grow lg:max-w-80">
<input
v-model.lazy="search" autocomplete="search" name="search" type="text"
:placeholder="t('pages.readings.filters.searchPlaceHolder')" class="form-input min-w-64"
:placeholder="t('pages.readings.filters.searchPlaceHolder')" class="form-input"
>
</div>

<div>
<label class="mb-1 block" for="selectStatus">{{ t('pages.readings.filters.statusLabel') }}</label>
<select id="selectStatus" v-model="status" class="form-select">
<option value="">
{{ t('pages.readings.filters.status.all') }}
</option>
<option value="To read">
{{ mapStatus['To read'] }}
</option>
<option value="Read">
{{ mapStatus.Read }}
</option>
<option value="Reading">
{{ mapStatus.Reading }}
</option>
<option value="Canceled">
{{ mapStatus.Canceled }}
</option>
</select>
</div>
<div class="flex flex-wrap items-end gap-x-2 gap-y-1.5">
<Transition name="fade">
<AppLoader v-if="pending" class="m-1 text-2xl" />
</Transition>
<div>
<label class="mb-0.5 block" for="selectStatus">{{ t('pages.readings.filters.statusLabel') }}</label>
<select id="selectStatus" v-model="status" class="form-select">
<option value="">
{{ t('pages.readings.filters.status.all') }}
</option>
<option value="To read">
{{ mapStatus['To read'] }}
</option>
<option value="Read">
{{ mapStatus.Read }}
</option>
<option value="Reading">
{{ mapStatus.Reading }}
</option>
<option value="Canceled">
{{ mapStatus.Canceled }}
</option>
</select>
</div>

<div>
<MultiSelectTag v-model="selectedOptions" :options="tags" :placeholder="t('pages.readings.filters.tagsPlaceHolder')" />
</div>
<div>
<label class="mb-0.5 block" for="selectType">{{ t('pages.readings.filters.typeLabel') }}</label>
<select id="selectType" v-model="type" class="form-select">
<option value="">
{{ t('pages.readings.filters.type.all') }}
</option>
<option value="Article">
{{ mapType.Article }}
</option>
<option value="Video">
{{ mapType.Video }}
</option>
<option value="Podcast">
{{ mapType.Podcast }}
</option>
</select>
</div>

<div>
<label class="mb-1 block" for="selectSort">{{ t('pages.readings.sort.sortLabel') }}</label>
<select id="selectSort" v-model="sort" class="form-select">
<option value="Created time">
{{ mapSort['Created time'] }}
</option>
<option value="Last edited time">
{{ mapSort['Last edited time'] }}
</option>
<option value="Name">
{{ mapSort.Name }}
</option>
<option value="Score">
{{ mapSort.Score }}
</option>
</select>
</div>
<div>
<MultiSelectTag v-model="selectedOptions" :options="tags" :placeholder="t('pages.readings.filters.tagsPlaceHolder')" />
</div>

<Transition name="fade">
<AppLoader v-if="pending" class="m-1 text-2xl" />
</Transition>
<div>
<label class="mb-0.5 block" for="selectSort">{{ t('pages.readings.sort.sortLabel') }}</label>
<select id="selectSort" v-model="sort" class="form-select">
<option value="Created time">
{{ mapSort['Created time'] }}
</option>
<option value="Last edited time">
{{ mapSort['Last edited time'] }}
</option>
<option value="Name">
{{ mapSort.Name }}
</option>
<option value="Score">
{{ mapSort.Score }}
</option>
</select>
</div>
</div>
</div>

<Transition name="fade">
<div v-if="hasFilters" class="mt-4 flex items-start gap-6">
<p class="flex-grow text-sm">
{{ t('common.article', 1) }} <i18n-t v-if="status" keypath="pages.readings.filters.message.withStatus" tag="span" scope="global">
<strong>{{ mapStatus[status] }}</strong>
</i18n-t> <i18n-t v-if="type" keypath="pages.readings.filters.message.withType" tag="span" scope="global">
<strong>{{ mapType[type] }}</strong>
</i18n-t> <i18n-t v-if="selectedOptions.length > 0" tag="span" keypath="pages.readings.filters.message.withTags" scope="global">
<strong>{{ selectedOptions.map(tag => tag.name).toString() }}</strong>
</i18n-t> <i18n-t v-if="search" tag="span" keypath="pages.readings.filters.message.withSearch" scope="global">
Expand Down
3 changes: 1 addition & 2 deletions content/en/readings.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ coverDescription: Like a to-do list, but for articles to be read.
description: Pascal Achard, senior frontend developer. Reading. List of articles to read.
---


## Articles
I store my readings on a Notion page <span class="sr-only">Notion</span><Icon name='logos:notion-icon' title="Notion"></Icon>. You can access it here [ici](https://totoro38.notion.site/My-Reading-List-01cc6c50b8ce483fb887bbdacf47d9b6?pvs=4){target="_blank" rel="noopener"}.
3 changes: 1 addition & 2 deletions content/fr/readings.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ coverDescription: Base de données d’articles collectés au fil du temps.
description: Pascal Achard, développeur frontend senior. Lecture. Liste d'articles à lire.
---


## Articles
Je stocke mes lectures sur une page <span class="sr-only">Notion</span><Icon name='logos:notion-icon' title="Notion"></Icon>. Vous pouvez y accéder [ici](https://totoro38.notion.site/My-Reading-List-01cc6c50b8ce483fb887bbdacf47d9b6?pvs=4){target="_blank" rel="noopener"}.
12 changes: 10 additions & 2 deletions locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@
"sortedBy": "sort by {0}",
"withSearch": "matching {0}",
"withStatus": "with the status {0}",
"withTags": "integrating tags {0}"
"withTags": "integrating tags {0}",
"withType": "type of {0}"
},
"pageSizes": "Articles by page",
"searchPlaceHolder": "Search an article",
Expand All @@ -69,7 +70,14 @@
"toRead": "To read"
},
"statusLabel": "Status",
"tagsPlaceHolder": "Select tags"
"tagsPlaceHolder": "Select tags",
"type": {
"all": "All",
"article": "Article",
"podcast": "Podcast",
"video": "Video"
},
"typeLabel": "Type"
},
"filtersTooRestrictive": "🤔 Filters too restrictive ?",
"sort": {
Expand Down
12 changes: 10 additions & 2 deletions locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@
"sortedBy": "triés par {0}",
"withSearch": "correspondant à {0}",
"withStatus": "avec le status {0}",
"withTags": "intégrant les tags {0}"
"withTags": "intégrant les tags {0}",
"withType": "de type {0}"
},
"pageSizes": "Articles par page",
"searchPlaceHolder": "Rechercher un article",
Expand All @@ -69,7 +70,14 @@
"toRead": "À lire"
},
"statusLabel": "Statut",
"tagsPlaceHolder": "Sélectionner des tags"
"tagsPlaceHolder": "Sélectionner des tags",
"type": {
"all": "Tous",
"article": "Article",
"podcast": "Podcast",
"video": "Vidéo"
},
"typeLabel": "Type"
},
"filtersTooRestrictive": "🤔 Filtres trop restrictifs ?",
"sort": {
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"splitting": "^1.0.6"
},
"devDependencies": {
"@antfu/eslint-config": "^2.6.4",
"@antfu/eslint-config": "^2.7.0",
"@eslint/eslintrc": "^3.0.2",
"@nuxt/content": "^2.12.0",
"@nuxt/devtools": "^1.0.8",
Expand All @@ -40,7 +40,7 @@
"@nuxtjs/tailwindcss": "^6.11.4",
"@pinia/nuxt": "^0.5.1",
"@tailwindcss/forms": "^0.5.7",
"@types/node": "^20.11.22",
"@types/node": "^20.11.24",
"@types/splitting": "^1.0.6",
"@types/uuid": "^9.0.8",
"@typescript-eslint/parser": "^7.1.0",
Expand All @@ -56,6 +56,6 @@
"typescript": "^5.3.3",
"uuid": "^9.0.1",
"vite-svg-loader": "^5.1.0",
"vue-tsc": "^1.8.27"
"vue-tsc": "^2.0.3"
}
}
44 changes: 33 additions & 11 deletions pages/readings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ const selectedTags = ref<IArticleTag[]>([]);
// Route query
const pageSize = useRouteQuery('pageSize', DEFAULT_LIMIT, { transform: Number });
const status = useRouteQuery('status', '', { transform: String });
const selectedStatus = useRouteQuery('status', '', { transform: String });
const selectedType = useRouteQuery('type', '', { transform: String });
const search = useRouteQuery('search', '', { transform: String });
const sort = useRouteQuery('sort', 'Created time', { transform: String });
const selectedSort = useRouteQuery('sort', 'Created time', { transform: String });
// Pinia store
const databaseStore = useArticleDatabaseInfoStore();
Expand Down Expand Up @@ -54,16 +55,22 @@ const { error, pending, refresh } = await useAsyncData(`page-list-${route.fullPa
start_cursor: cursor.value,
sorts: [
{
property: sort.value.replace(/\+/g, ' '),
direction: sort.value === 'Name' ? 'ascending' : 'descending',
property: selectedSort.value.replace(/\+/g, ' '),
direction: selectedSort.value === 'Name' ? 'ascending' : 'descending',
},
],
filter: {
and: [
{
property: 'Status',
select: {
equals: status.value.replace(/\+/g, ' '),
equals: selectedStatus.value.replace(/\+/g, ' '),
},
},
{
property: 'Type',
select: {
equals: selectedType.value.replace(/\+/g, ' '),
},
},
{
Expand Down Expand Up @@ -93,7 +100,8 @@ const { error, pending, refresh } = await useAsyncData(`page-list-${route.fullPa
function clearFilters() {
cursor.value = null;
status.value = '';
selectedStatus.value = '';
selectedType.value = '';
search.value = '';
selectedTags.value = [];
}
Expand Down Expand Up @@ -133,7 +141,19 @@ watch(
// Watch status change
watch(
() => status.value,
() => selectedStatus.value,
async (newVal) => {
if (!newVal && newVal !== '')
return;
cursor.value = null;
await refresh();
},
{ immediate: false },
);
// Watch type change
watch(
() => selectedType.value,
async (newVal) => {
if (!newVal && newVal !== '')
return;
Expand Down Expand Up @@ -169,7 +189,7 @@ watch(
// Watch sort change
watch(
() => sort.value,
() => selectedSort.value,
async (newVal) => {
if (!newVal && newVal !== '')
return;
Expand Down Expand Up @@ -210,7 +230,9 @@ watch(
</template>
</HeroComponent>
<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="lg:w-9/12">
<ContentRenderer v-if="contentData" class="nuxt-content mb-10" :value="contentData" />
</div>
<div class="flow">
<template v-if="error || fetchDatabaseError">
<p class="font-code">
Expand All @@ -228,8 +250,8 @@ watch(
<!-- Action bar -->
<Transition name="fade">
<ArticleListActionBar
v-model:selected-options="selectedTags" v-model:search="search" v-model:status="status"
v-model:sort="sort" :tags="databaseStore.tagList" :pending="pending || fetchDatabasePending"
v-model:selected-options="selectedTags" v-model:search="search" v-model:status="selectedStatus" v-model:type="selectedType"
v-model:sort="selectedSort" :tags="databaseStore.tagList" :pending="pending || fetchDatabasePending"
@clear-filters="clearFilters"
/>
</Transition>
Expand Down
Loading

0 comments on commit 5d99693

Please sign in to comment.