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 @@
-
![]()
+
{{ date().format(new Date(item.published_at)) }}
diff --git a/src/partials/ArticlesListPartial.vue b/src/partials/ArticlesListPartial.vue
index e85364d8..84baac5e 100644
--- a/src/partials/ArticlesListPartial.vue
+++ b/src/partials/ArticlesListPartial.vue
@@ -4,17 +4,18 @@
@@ -26,20 +27,67 @@
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);
}