diff --git a/package-lock.json b/package-lock.json index 9cab21e2..939159cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "dompurify": "^3.2.6", "highlight.js": "^11.11.1", + "lodash": "^4.17.21", "marked": "^16.0.0", "pinia": "^3.0.2", "vue": "^3.5.13", @@ -20,6 +21,7 @@ "@eslint/js": "^9.25.1", "@tailwindcss/forms": "^0.5.10", "@tailwindcss/vite": "^4.1.4", + "@types/lodash": "^4.17.20", "@types/node": "^22.14.1", "@typescript-eslint/eslint-plugin": "^8.38.0", "@typescript-eslint/parser": "^8.38.0", @@ -1610,6 +1612,12 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==", + "dev": true + }, "node_modules/@types/node": { "version": "22.16.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.5.tgz", @@ -3304,6 +3312,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", diff --git a/package.json b/package.json index bfd4f6dc..4e2bfd91 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "dependencies": { "dompurify": "^3.2.6", "highlight.js": "^11.11.1", + "lodash": "^4.17.21", "marked": "^16.0.0", "pinia": "^3.0.2", "vue": "^3.5.13", @@ -23,6 +24,7 @@ "@eslint/js": "^9.25.1", "@tailwindcss/forms": "^0.5.10", "@tailwindcss/vite": "^4.1.4", + "@types/lodash": "^4.17.20", "@types/node": "^22.14.1", "@typescript-eslint/eslint-plugin": "^8.38.0", "@typescript-eslint/parser": "^8.38.0", diff --git a/src/pages/PostPage.vue b/src/pages/PostPage.vue index c13cad05..35d3c659 100644 --- a/src/pages/PostPage.vue +++ b/src/pages/PostPage.vue @@ -86,7 +86,7 @@

{{ post.excerpt }}

- +
diff --git a/src/partials/ArticleItemPartial.vue b/src/partials/ArticleItemPartial.vue index 190037d3..852ed8a0 100644 --- a/src/partials/ArticleItemPartial.vue +++ b/src/partials/ArticleItemPartial.vue @@ -1,7 +1,16 @@ diff --git a/src/stores/api/client.ts b/src/stores/api/client.ts index 93f7794b..57725708 100644 --- a/src/stores/api/client.ts +++ b/src/stores/api/client.ts @@ -80,6 +80,23 @@ export class ApiClient { return headers; } + public async post(url: string, data: object): Promise { + const headers = this.createHeaders(); + const fullUrl = new URL(url, this.basedURL); + + const response = await fetch(fullUrl.href, { + method: 'POST', + headers: headers, + body: JSON.stringify(data), + }); + + if (!response.ok) { + throw new HttpError(response, await response.text()); + } + + return await response.json(); + } + public async get(url: string): Promise { const headers = this.createHeaders(); const fullUrl = new URL(url, this.basedURL); diff --git a/src/stores/api/response/categories-response.ts b/src/stores/api/response/categories-response.ts new file mode 100644 index 00000000..9a7b712e --- /dev/null +++ b/src/stores/api/response/categories-response.ts @@ -0,0 +1,14 @@ +export interface CategoriesCollectionResponse { + page: number; + total: number; + page_size: number; + total_pages: number; + data: CategoryResponse[]; +} + +export interface CategoryResponse { + uuid: string; + name: string; + slug: string; + description: string; +} diff --git a/src/stores/api/response/posts-response.ts b/src/stores/api/response/posts-response.ts index efc07499..e04a6d26 100644 --- a/src/stores/api/response/posts-response.ts +++ b/src/stores/api/response/posts-response.ts @@ -44,3 +44,11 @@ export interface PostsTagResponse { name: string; description: string; } + +export interface PostsFilters { + text?: string; + title?: string; + author?: string; + category?: string; + tag?: string; +} diff --git a/src/stores/api/store.ts b/src/stores/api/store.ts index 3cb5ab7f..f7e2e9ad 100644 --- a/src/stores/api/store.ts +++ b/src/stores/api/store.ts @@ -2,12 +2,14 @@ import { defineStore } from 'pinia'; import { parseError } from '@api/http-error.ts'; import { ProfileResponse } from '@api/response/profile-response.ts'; import { ApiClient, ApiResponse, defaultCreds } from '@api/client.ts'; -import type { PostResponse, PostsCollectionResponse } from '@api/response/posts-response.ts'; +import type { PostResponse, PostsCollectionResponse, PostsFilters } from '@api/response/posts-response.ts'; +import { CategoriesCollectionResponse } from '@api/response/categories-response.ts'; const STORE_KEY = 'api-client-store'; export interface ApiStoreState { client: ApiClient; + searchTerm: string; } const client = new ApiClient(defaultCreds); @@ -15,8 +17,12 @@ const client = new ApiClient(defaultCreds); export const useApiStore = defineStore(STORE_KEY, { state: (): ApiStoreState => ({ client: client, + searchTerm: '', }), actions: { + setSearchTerm(term: string): void { + this.searchTerm = term; + }, boot(): void { if (this.client.isDev()) { console.log('API client booted ...'); @@ -31,11 +37,20 @@ export const useApiStore = defineStore(STORE_KEY, { return parseError(error); } }, - async getPosts(): Promise { - const url = 'posts'; + async getCategories(): Promise { + const url = 'categories?limit=5'; + + try { + return await this.client.get(url); + } catch (error) { + return parseError(error); + } + }, + async getPosts(filters: PostsFilters): Promise { + const url = 'posts?limit=5'; try { - return await this.client.get(url); + return await this.client.post(url, filters); } catch (error) { return parseError(error); }