Skip to content

Commit 4bb31ff

Browse files
committed
a search bar and a dropbox for tags are created, the search bar is functional. page, search and category parameters are passed via queryStrings
1 parent 63a5fa1 commit 4bb31ff

File tree

9 files changed

+174
-81
lines changed

9 files changed

+174
-81
lines changed

docs/README.es.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,24 @@ Además, se debe proporcionar un archivo db.json para el backend con los datos d
8383
- Permitir el filtrado de anuncios usando tags. Por lo que en el formulario de anuncio deberán poder incluirse tags de los mismos. Estos tags inicialmente pueden ser estáticos (siempre los mismos).
8484
- Unido al anterior, hacer que los tags sean dinámicos.
8585

86-
### API REST de Apoyo Para la Práctica
86+
### API REST de Apoyo Para la Práctica y como utilizarla
8787

88-
Se utilizará sparrest.js como API REST de apoyo para la práctica (creado por el docente de KeepCoding Alberto Casero), este proyecto está basado en la utilidad json-server, el cual nos ofrece un completo API REST para simular un backend real y adaptarse a las necesidades de esta práctica.
88+
Para emular una base de datos en esta práctica, utilizaremos **sparrest.js** como API REST de apoyo (creado por el docente de KeepCoding, Alberto Casero). Sparrest está basado en **json-server** y proporciona una API REST completa para simular un backend real y se adapta perfectamente a las necesidades de esta práctica.
8989

9090
Enlace al API REST
9191

9292
```bash
9393
git clone https://github.com/kasappeal/sparrest.js.git
9494
```
9595

96+
Una vez descargado el proyecto, deberás actualizar las dependencias. Después, reemplaza el archivo db.json generado con el archivo correspondiente del mismo nombre del proyecto, lo que permitirá cargar la base de datos. Para iniciar la base de datos, simplemente ejecuta el comando:
97+
98+
```bash
99+
npm start
100+
```
101+
102+
Esto levantará el servidor y pondrá en funcionamiento el API REST para que puedas interactuar con la base de datos simulada.
103+
96104
## Tecnologías Utilizadas
97105

98106
- **HTML**: Para la estructuración del contenido y la creación de la estructura de la página web.

index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
<body>
2727

28-
<div class="navbar"></div>
28+
<nav class="navbar"></nav>
2929

3030
<main>
3131
<h1>Posts</h1>

src/js/components/navbar/navbar.js

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import { isAuthenticated } from '../../auth/auth.js';
2-
import { navbarView } from './navbarView.js';
1+
import { notificationsController } from "../../components/notifications/notificationsController.js";
2+
3+
import { navbarController } from "../../components/navbar/navbarController.js";
4+
35
import {
46
handleLogout,
57
goToLogin,
@@ -10,37 +12,26 @@ import {
1012

1113
export function navbar() {
1214
const container = document.querySelector('.navbar');
13-
if (!container) return;
15+
const notifications = document.querySelector(".notifications")
16+
const { showNotification } = notificationsController(notifications)
1417

15-
container.innerHTML = navbarView();
18+
navbarController(container)
1619

20+
const indexBtn = document.getElementById('index-btn');
1721
const logoutBtn = document.getElementById('logout-btn');
1822
const loginBtn = document.getElementById('login-btn');
1923
const signupBtn = document.getElementById('signup-btn');
2024
const createPostBtn = document.getElementById('create-post-btn');
21-
const indexBtn = document.getElementById('index-btn');
22-
23-
// Button visibility logic based on authentication state
24-
if (isAuthenticated()) {
2525

26-
// Show "Logout" and "Create Post", hide "Login" and "Sign Up"
27-
if (logoutBtn) logoutBtn.style.display = 'block'; // Make sure it's visible
28-
if (createPostBtn) createPostBtn.style.display = 'block'; // Make sure it's visible
29-
if (loginBtn) loginBtn.style.display = 'none'; // Hide if there's a token
30-
if (signupBtn) signupBtn.style.display = 'none'; // Hide if there's a token
31-
} else {
32-
33-
// Show "Login" and "Sign Up", hide "Logout" and "Create Post"
34-
if (logoutBtn) logoutBtn.style.display = 'none'; // Hide if there's no token
35-
if (createPostBtn) createPostBtn.style.display = 'none'; // Hide if there's no token
36-
if (loginBtn) loginBtn.style.display = 'block'; // Make sure it's visible
37-
if (signupBtn) signupBtn.style.display = 'block'; // Make sure it's visible
38-
}
39-
40-
// Assign events to buttons
4126
if (logoutBtn) logoutBtn.addEventListener('click', handleLogout);
4227
if (loginBtn) loginBtn.addEventListener('click', goToLogin);
4328
if (signupBtn) signupBtn.addEventListener('click', goToSignup);
4429
if (createPostBtn) createPostBtn.addEventListener('click', goToCreatePost);
4530
if (indexBtn) indexBtn.addEventListener('click', goToIndex);
31+
32+
container.addEventListener("search-error", (event) => {
33+
const message = event.detail;
34+
showNotification(message)
35+
})
36+
4637
}

src/js/components/navbar/navbarController.js

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,82 @@
1+
import { navbarView } from './navbarView.js';
12
import { isAuthenticated } from '../../auth/auth.js';
23

4+
export function navbarController(container) {
5+
container.innerHTML = navbarView();
6+
7+
handleVisibility();
8+
searchForm();
9+
}
10+
11+
//-------------------------------------------------------------------------------------------------------------------
12+
function handleVisibility() {
13+
const path = window.location.pathname;
14+
15+
const searchBtn = document.querySelector('.nav-center');
16+
const logoutBtn = document.getElementById('logout-btn');
17+
const loginBtn = document.getElementById('login-btn');
18+
const signupBtn = document.getElementById('signup-btn');
19+
const createPostBtn = document.getElementById('create-post-btn');
20+
21+
// Button visibility logic based on path
22+
if (searchBtn) searchBtn.style.display = 'none';
23+
24+
if (path.endsWith('index.html')) {
25+
if (searchBtn) searchBtn.style.display = 'block'; // Make sure it's visible
26+
}
27+
28+
// Button visibility logic based on authentication state
29+
if (isAuthenticated()) {
30+
31+
// Show "Logout" and "Create Post", hide "Login" and "Sign Up"
32+
if (logoutBtn) logoutBtn.style.display = 'block'; // Make sure it's visible
33+
if (loginBtn) loginBtn.style.display = 'none'; // Hide if there's a token
34+
if (signupBtn) signupBtn.style.display = 'none'; // Hide if there's a token
35+
if (createPostBtn) createPostBtn.style.display = 'block'; // Make sure it's visible
36+
37+
} else {
38+
39+
// Show "Login" and "Sign Up", hide "Logout" and "Create Post"
40+
if (logoutBtn) logoutBtn.style.display = 'none'; // Hide if there's no token
41+
if (createPostBtn) createPostBtn.style.display = 'none'; // Hide if there's no token
42+
if (loginBtn) loginBtn.style.display = 'block'; // Make sure it's visible
43+
if (signupBtn) signupBtn.style.display = 'block'; // Make sure it's visible
44+
}
45+
46+
}
47+
48+
//-------------------------------------------------------------------------------------------------------------------
49+
function searchForm() {
50+
const searchForm = document.getElementById('search-form');
51+
52+
searchForm.addEventListener('submit', function (event) {
53+
event.preventDefault();
54+
55+
const searchQuery = document.getElementById('search').value.trim();
56+
const selectedCategory = document.getElementById('category-select').value;
57+
58+
if (!searchQuery && !selectedCategory) {
59+
dispatchErrorNotification("Enter a word or select a category.");
60+
return;
61+
}
62+
63+
const params = new URLSearchParams();
64+
if (searchQuery) params.set('search', searchQuery);
65+
if (selectedCategory) params.set('category', selectedCategory);
66+
67+
window.location.href = `/index.html?${params.toString()}`;
68+
});
69+
}
70+
71+
//-------------------------------------------------------------------------------------------------------------------
372
export function handleLogout() {
4-
// Remove the token from localStorage and redirect to index
5-
localStorage.removeItem('accessToken');
73+
// Remove the token from localStorage and redirect to index
74+
localStorage.removeItem('accessToken');
675
window.location.href = '/index.html';
776
}
877

978
export function goToLogin() {
10-
// Redirigir a la página de Login solo si no hay token
79+
// Redirect to Login page only if no token is present
1180
if (!isAuthenticated()) {
1281
window.location.href = '/views/login.html';
1382
} else {
@@ -16,7 +85,7 @@ export function goToLogin() {
1685
}
1786

1887
export function goToSignup() {
19-
// Redirect to Login page only if no token is present
88+
// Redirect to Login page only if no token is present
2089
if (!isAuthenticated()) {
2190
window.location.href = '/views/signup.html';
2291
} else {
@@ -36,4 +105,16 @@ export function goToCreatePost() {
36105
export function goToIndex() {
37106
// Always redirect to index
38107
window.location.href = '/index.html';
108+
}
109+
110+
//-------------------------------------------------------------------------------------------------------------------
111+
function dispatchErrorNotification(message) {
112+
113+
const container = document.querySelector('.navbar');
114+
115+
const event = new CustomEvent('search-error', {
116+
detail: message
117+
})
118+
119+
container.dispatchEvent(event)
39120
}

src/js/components/navbar/navbarView.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,26 @@
11

22
export function navbarView() {
33
return `
4-
<a id="index-btn">Home</a>
4+
<a id="index-btn" class="nav-left">Home</a>
5+
6+
<div class="nav-center">
7+
<form id="search-form">
8+
9+
<input id="search" name="search" type="text" placeholder="Search" />
10+
11+
<select id="category-select" name="category">
12+
<option value="">All Categories</option>
13+
<option value="tech">Tech</option>
14+
<option value="lifestyle">Lifestyle</option>
15+
<option value="education">Education</option>
16+
<option value="news">News</option>
17+
</select>
18+
19+
<button type="submit">Search</button>
20+
21+
</form>
22+
</div>
23+
524
<div class="nav-right">
625
<a id="create-post-btn">Create Post</a>
726
<a id="login-btn">Login</a>

src/js/main.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ document.addEventListener("DOMContentLoaded", () => {
2121

2222
const path = window.location.pathname;
2323

24-
if (path === '/index.html' || path === '/' || path === '/index') {
24+
if (path.endsWith('index.html')) {
2525
initShowPosts();
2626
}
2727

src/js/modules/show-posts/showPosts.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ export function initShowPosts() {
1313
const loader = document.querySelector(".loader")
1414
const notifications = document.querySelector(".notifications")
1515
const session = document.querySelector(".session")
16-
1716

17+
//-------------------------------------------------------------------------------------------------------------------
1818

1919
const { show, hide } = loaderController(loader);
2020
const { showNotification } = notificationsController(notifications)

src/js/modules/show-posts/showPostsController.js

Lines changed: 29 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,20 @@ export async function showPostsController(container) { // the container is ".pos
66

77
const pages = document.getElementById('pages');
88

9-
const POSTS_PER_PAGE = 10;
10-
let currentPage = 1;
9+
const urlParams = new URLSearchParams(window.location.search);
10+
const search = urlParams.get('search') || '';
11+
const category = urlParams.get('category') || '';
12+
let currentPage = parseInt(urlParams.get('page')) || 1;
13+
14+
const POSTS_PER_PAGE = 12;
1115

1216
try {
1317
//container.dispatchEvent(new CustomEvent("load-posts-started"))
1418
const event = new CustomEvent("load-posts-started");
1519
container.dispatchEvent(event); // --> This "event" goes to "load-posts-started".
1620

1721
//===================================
18-
const { posts, totalPosts } = await getPosts(currentPage);
22+
const { posts, totalPosts } = await getPosts(search, category, currentPage, POSTS_PER_PAGE);
1923
//===================================
2024

2125
drawPosts(posts, container);
@@ -47,64 +51,44 @@ export async function showPostsController(container) { // the container is ".pos
4751

4852
container.innerHTML = ''; // I clean all displayed posts if any.
4953

50-
if (posts.length === 0) {
51-
//alert("Sorry, no posts available!")
52-
} else { // I add this "else" to have more clarity of the code.
53-
54-
posts.forEach((post) => { // If "posts" is an empty array, the "forEach" will not be executed.
54+
posts.forEach((post) => { // If "posts" is an empty array, the "forEach" will not be executed.
5555

56-
const postHtml = document.createElement("a");
57-
postHtml.setAttribute("href", `./views/post-detail.html?id=${post.id}`)
58-
// Creates an empty HTML element in memory (postHtml), which is not yet in the DOM.
56+
const postHtml = document.createElement("a");
57+
postHtml.setAttribute("href", `./views/post-detail.html?id=${post.id}`)
58+
// Creates an empty HTML element in memory (postHtml), which is not yet in the DOM.
5959

60-
postHtml.innerHTML = buildPost(post)
61-
// In an already cleaned container,
62-
// I assign to "postHtml" whatever the buildPost(post) function returns.
60+
postHtml.innerHTML = buildPost(post)
61+
// In an already cleaned container,
62+
// I assign to "postHtml" whatever the buildPost(post) function returns.
6363

64-
container.appendChild(postHtml)
65-
// I insert this new div in the container.
66-
// Requires you to pass a node object (postHtml), not plain text or HTML.
67-
})
68-
}
64+
container.appendChild(postHtml)
65+
// I insert this new div in the container.
66+
// Requires you to pass a node object (postHtml), not plain text or HTML.
67+
})
6968
}
7069

7170
//-------------------------------------------------------------------------------------------------------------------
7271
function drawPages(totalPosts) {
73-
7472
pages.innerHTML = '';
75-
73+
7674
const pageCount = Math.ceil(totalPosts / POSTS_PER_PAGE);
77-
75+
7876
for (let i = 1; i <= pageCount; i++) {
79-
8077
const btn = document.createElement('button');
8178
btn.textContent = i;
82-
79+
8380
if (i === currentPage) {
8481
btn.classList.add('active');
8582
}
86-
87-
btn.addEventListener('click', async () => {
88-
89-
currentPage = i;
90-
91-
try {
92-
//===================================
93-
const { posts } = await getPosts(currentPage);
94-
//===================================
95-
96-
drawPosts(posts, container);
97-
drawPages(totalPosts);
98-
99-
} catch (error) {
100-
101-
const errorEvent = new CustomEvent("load-posts-error", {
102-
detail: error.message
103-
});
104-
container.dispatchEvent(errorEvent);
105-
}
83+
84+
btn.addEventListener('click', () => {
85+
const urlParams = new URLSearchParams(window.location.search);
86+
urlParams.set('page', i);
87+
88+
const newUrl = `${window.location.pathname}?${urlParams.toString()}`;
89+
window.location.href = newUrl;
10690
});
107-
91+
10892
pages.appendChild(btn);
10993
}
11094
}

src/js/modules/show-posts/showPostsModel.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
1-
export async function getPosts(page) {
1+
export async function getPosts(search = '', category = '', page = 1, postsPerPage = 10) {
22

33
let posts = [];
44

55
try {
6-
const response = await fetch(`http://localhost:8000/api/posts?_page=${page}&_limit=12`)
6+
let url = `http://localhost:8000/api/posts?_page=${page}&_limit=${postsPerPage}`;
7+
8+
if (search) {
9+
url += `&q=${search}`;
10+
}
11+
12+
if (category) {
13+
url += `&category=${category}`;
14+
}
15+
16+
const response = await fetch(url);
17+
718
const totalPosts = parseInt(response.headers.get('X-Total-Count'), 10);
819

920
if (!response.ok) {
@@ -27,6 +38,5 @@ export async function getPosts(page) {
2738
throw new Error('Oops... There was a problem with the server connection.');
2839
}
2940
throw error
30-
3141
}
3242
}

0 commit comments

Comments
 (0)