Skip to content

Commit b84858e

Browse files
authored
Merge pull request #29 from fraidakis/fraidakis
feat: enhance UI/UX
2 parents 104f7f6 + 85225e8 commit b84858e

50 files changed

Lines changed: 5044 additions & 1268 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cypress/e2e/auth_happy_unhappy.cy.js

Lines changed: 288 additions & 285 deletions
Large diffs are not rendered by default.

cypress/e2e/happy_paths.cy.js

Lines changed: 85 additions & 84 deletions
Large diffs are not rendered by default.

cypress/support/commands.js

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@
1010
*/
1111
Cypress.Commands.add('login', (email, password) => {
1212
cy.visit('/login');
13-
13+
1414
// Fill in login form using data-cy attributes
1515
cy.get('[data-cy="input-email"]').clear().type(email);
1616
cy.get('[data-cy="input-password"]').clear().type(password);
17-
17+
1818
// Submit the form
1919
cy.get('[data-cy="btn-submit"]').click();
20-
20+
2121
// Wait for redirect or success indication
2222
cy.url().should('not.include', '/login', { timeout: 10000 });
23-
23+
2424
// Verify token is stored
2525
cy.window().its('localStorage.token').should('exist');
2626
});
@@ -33,7 +33,7 @@ Cypress.Commands.add('login', (email, password) => {
3333
*/
3434
Cypress.Commands.add('loginViaAPI', (email, password) => {
3535
const apiUrl = Cypress.env('apiUrl') || 'http://localhost:3001';
36-
36+
3737
cy.request({
3838
method: 'POST',
3939
url: `${apiUrl}/auth/login`,
@@ -45,19 +45,19 @@ Cypress.Commands.add('loginViaAPI', (email, password) => {
4545
expect(response.status).to.eq(200);
4646
expect(response.body).to.have.property('success');
4747
expect(response.body.success).to.be.true;
48-
48+
4949
// Handle response structure: { success, data: { token, user } }
5050
const token = response.body.data?.token || response.body.token;
5151
const user = response.body.data?.user || response.body.user;
52-
52+
5353
// Visit the home page first to set up the window context
5454
cy.visit('/');
55-
55+
5656
// Store token and user info in localStorage
5757
cy.window().then((win) => {
5858
win.localStorage.setItem('token', token);
5959
win.localStorage.setItem('user', JSON.stringify(user));
60-
60+
6161
// Dispatch login event
6262
win.dispatchEvent(new CustomEvent('user:login', { detail: user }));
6363
});
@@ -72,7 +72,7 @@ Cypress.Commands.add('logout', () => {
7272
// Clear authentication data
7373
cy.clearLocalStorage();
7474
cy.clearCookies();
75-
75+
7676
// Visit home or login page
7777
cy.visit('/');
7878
});
@@ -88,19 +88,19 @@ Cypress.Commands.add('logout', () => {
8888
*/
8989
Cypress.Commands.add('register', (userData) => {
9090
cy.visit('/signup');
91-
91+
9292
// Fill in registration form using data-cy attributes
9393
cy.get('[data-cy="input-name"]').clear().type(userData.name);
9494
cy.get('[data-cy="input-email"]').clear().type(userData.email);
9595
cy.get('[data-cy="input-password"]').clear().type(userData.password);
9696
cy.get('[data-cy="input-confirmPassword"]').clear().type(userData.confirmPassword || userData.password);
97-
97+
9898
// Submit the form
9999
cy.get('[data-cy="btn-submit"]').click();
100-
100+
101101
// Wait for redirect or success indication
102102
cy.url().should('not.include', '/signup', { timeout: 10000 });
103-
103+
104104
// Verify token is stored
105105
cy.window().its('localStorage.token').should('exist');
106106
});
@@ -145,11 +145,11 @@ Cypress.Commands.add('clearAuthToken', () => {
145145
Cypress.Commands.add('loginAsTestUser', (role = 'validUser') => {
146146
cy.fixture('test-users').then((users) => {
147147
const user = users[role];
148-
148+
149149
if (!user) {
150150
throw new Error(`User role "${role}" not found in test-users fixture`);
151151
}
152-
152+
153153
// Use API login for speed
154154
cy.loginViaAPI(user.email, user.password);
155155
});
@@ -213,12 +213,12 @@ Cypress.Commands.add('interceptAPI', (endpoint, method = 'GET') => {
213213
navigation: { pattern: '**/navigation*', alias: 'getNavigation' },
214214
calculateRoute: { pattern: '**/routes*', alias: 'calculateRoute' }
215215
};
216-
216+
217217
const config = aliasMap[endpoint];
218218
if (!config) {
219219
throw new Error(`Unknown API endpoint: ${endpoint}. Available: ${Object.keys(aliasMap).join(', ')}`);
220220
}
221-
221+
222222
cy.intercept(method, config.pattern).as(config.alias);
223223
});
224224

@@ -234,22 +234,34 @@ Cypress.Commands.add('interceptAPI', (endpoint, method = 'GET') => {
234234
Cypress.Commands.add('waitForAPIAndLoading', (alias, options = {}) => {
235235
const { waitForLoading = true, timeout = 10000 } = options;
236236
const cleanAlias = alias.startsWith('@') ? alias : `@${alias}`;
237-
237+
238238
cy.wait(cleanAlias, { timeout });
239-
239+
240240
if (waitForLoading) {
241241
cy.get('[data-cy="loading-spinner"], .loading, .spinner', { timeout }).should('not.exist');
242242
}
243243
});
244244

245245
/**
246246
* Navigate to a page using navigation link and verify URL
247+
* Handles items inside user dropdown by opening it first
247248
* @param {string} dataCy - data-cy attribute value for the navigation link
248249
* @param {string} expectedPath - Expected path in URL after navigation
249250
* @example cy.navigateViaNav('nav-recommendations', '/recommendations')
250251
*/
251252
Cypress.Commands.add('navigateViaNav', (dataCy, expectedPath) => {
252-
cy.get(`[data-cy="${dataCy}"]`).click();
253+
// Items that are inside the user dropdown
254+
const dropdownItems = ['nav-preferences', 'nav-profile'];
255+
256+
if (dropdownItems.includes(dataCy)) {
257+
// First open the user dropdown
258+
cy.get('[data-cy="user-dropdown-trigger"]').should('be.visible').click();
259+
// Wait for dropdown menu to appear, then click the item
260+
cy.get(`[data-cy="${dataCy}"]`).should('be.visible').click();
261+
} else {
262+
// Regular nav item - just click it
263+
cy.get(`[data-cy="${dataCy}"]`).click();
264+
}
253265
cy.url().should('include', expectedPath);
254266
});
255267

cypress/support/helpers.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -587,8 +587,9 @@ export const verifyPlaceDetailsVisible = () => {
587587
cy.get('[data-cy="place-title"]').should('be.visible');
588588
cy.get('[data-cy="place-description-card"]').should('be.visible');
589589
cy.get('[data-cy="place-actions"]').should('be.visible');
590-
expectTextVisible(/description/i);
591-
expectTextVisible(/address|location/i);
590+
expectTextVisible(/information/i);
591+
// Address is now displayed with just an icon, verify the card contains an address value
592+
cy.get('.place-address-row').should('be.visible');
592593
};
593594

594595
/**
@@ -643,7 +644,9 @@ export const verifyAuthenticationSuccess = () => {
643644
cy.url().should('not.include', '/login');
644645
cy.url().should('not.include', '/signup');
645646
cy.window().its('localStorage.token').should('exist');
646-
cy.get('[data-cy="btn-logout"]').should('exist');
647+
// Check for user dropdown trigger which indicates logged-in state
648+
// The logout button is now inside the dropdown, not directly visible
649+
cy.get('[data-cy="user-dropdown-trigger"]').should('exist');
647650
};
648651

649652
/**

package-lock.json

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,4 @@
5050
"last 1 safari version"
5151
]
5252
}
53-
}
53+
}

src/App.js

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,56 @@
1-
import React from 'react';
1+
import React, { useState, useEffect } from 'react';
22
import { BrowserRouter as Router } from 'react-router-dom';
33
import { LanguageProvider } from './i18n';
4-
import { ToastProvider } from './components/ui';
4+
import { ThemeProvider } from './context';
5+
import { ToastProvider, BottomNavigation } from './components/ui';
56
import Header from './components/Header.jsx';
67
import Footer from './components/Footer.jsx';
78
import AppRoutes from './router/index.jsx';
89
import './App.css';
910

1011
function App() {
12+
const [isAuthenticated, setIsAuthenticated] = useState(false);
13+
14+
useEffect(() => {
15+
// Check for existing token on mount
16+
const token = localStorage.getItem('token');
17+
setIsAuthenticated(!!token);
18+
19+
// Listen for login/logout events
20+
const handleLogin = () => setIsAuthenticated(true);
21+
const handleLogout = () => setIsAuthenticated(false);
22+
23+
window.addEventListener('user:login', handleLogin);
24+
window.addEventListener('user:logout', handleLogout);
25+
window.addEventListener('storage', (e) => {
26+
if (e.key === 'token') {
27+
setIsAuthenticated(!!e.newValue);
28+
}
29+
});
30+
31+
return () => {
32+
window.removeEventListener('user:login', handleLogin);
33+
window.removeEventListener('user:logout', handleLogout);
34+
};
35+
}, []);
36+
1137
return (
12-
<LanguageProvider>
13-
<ToastProvider>
14-
<Router>
15-
<div className="App">
16-
<Header />
17-
<main className="main-content">
18-
<AppRoutes />
19-
</main>
20-
<Footer />
21-
</div>
22-
</Router>
23-
</ToastProvider>
24-
</LanguageProvider>
38+
<ThemeProvider>
39+
<LanguageProvider>
40+
<ToastProvider>
41+
<Router>
42+
<div className="App">
43+
<Header />
44+
<main className="main-content">
45+
<AppRoutes />
46+
</main>
47+
<Footer />
48+
<BottomNavigation isAuthenticated={isAuthenticated} />
49+
</div>
50+
</Router>
51+
</ToastProvider>
52+
</LanguageProvider>
53+
</ThemeProvider>
2554
);
2655
}
2756

0 commit comments

Comments
 (0)