Merge pull request #88 from ricardo-campos-org/fix/87-list-of-bugs-in-the-frontend
fix: list of bugs in the frontend
This commit is contained in:
@@ -1,19 +1,21 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { act, render, screen } from '@testing-library/react';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { describe, it, expect, vi, beforeAll, afterAll } from 'vitest';
|
||||
import authContextMock from '../__mocks__/authContextMock';
|
||||
import Footer from '../../components/Footer';
|
||||
import AuthContext from '../../context/AuthContext';
|
||||
|
||||
// Mock necessary dependencies
|
||||
vi.mock('../../env', () => ({
|
||||
env: {
|
||||
VITE_BUILD: 'test-build',
|
||||
},
|
||||
}));
|
||||
|
||||
describe('Footer component test', () => {
|
||||
beforeAll(() => {
|
||||
//import.meta.env.VITE_BUILD = ;
|
||||
vi.stubEnv('VITE_BUILD', 'test-build');
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
vi.unstubAllEnvs();
|
||||
});
|
||||
|
||||
it('should renders the footer with current year and build version', () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
@@ -27,7 +29,7 @@ describe('Footer component test', () => {
|
||||
|
||||
const footerText = screen.getByTestId('footer-text');
|
||||
expect(footerText).toBeDefined();
|
||||
expect(footerText.innerHTML).toBe(`TaskNote App ©${currentYear} (test-build)`);
|
||||
expect(footerText.innerHTML).toBe(`TaskNote App © ${currentYear} (Build test-build)`);
|
||||
});
|
||||
|
||||
it('should renders the user email when logged in', () => {
|
||||
@@ -63,8 +65,12 @@ describe('Footer component test', () => {
|
||||
</AuthContext.Provider>
|
||||
</BrowserRouter>,
|
||||
);
|
||||
|
||||
const logoutButton = getByText('Logout');
|
||||
logoutButton.click();
|
||||
expect(signOutMock).toHaveBeenCalledTimes(1);
|
||||
|
||||
act(() => {
|
||||
logoutButton.click();
|
||||
expect(signOutMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 355 KiB |
@@ -3,7 +3,6 @@ import {
|
||||
Button, Col, Container, Row
|
||||
} from 'react-bootstrap';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { env } from '../../env';
|
||||
import AuthContext from '../../context/AuthContext';
|
||||
import './style.css';
|
||||
|
||||
@@ -11,15 +10,14 @@ import './style.css';
|
||||
* Footer component.
|
||||
*
|
||||
* This component provides the footer section of the application,
|
||||
* providing navigation to logout.
|
||||
* It also includes the build version.
|
||||
* including logout functionality and version information.
|
||||
*
|
||||
* @returns {JSX.Element} The Footer component.
|
||||
*/
|
||||
function Footer(): JSX.Element {
|
||||
const { signOut, user } = useContext(AuthContext);
|
||||
const navigate = useNavigate();
|
||||
const build = env.VITE_BUILD;
|
||||
const build = import.meta.env.VITE_BUILD;
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
const goOut = () => {
|
||||
@@ -31,21 +29,30 @@ function Footer(): JSX.Element {
|
||||
<footer className="footer">
|
||||
<Container>
|
||||
<Row className="align-items-center">
|
||||
<Col xs={12} sm={4} className="text-center text-sm-start">
|
||||
<Col xs={12} sm={4} className="text-center text-md-start">
|
||||
<span data-testid="footer-text">
|
||||
TaskNote App ©
|
||||
{' '}
|
||||
{currentYear}
|
||||
{' '}
|
||||
(
|
||||
(Build
|
||||
{' '}
|
||||
{build}
|
||||
)
|
||||
</span>
|
||||
</Col>
|
||||
<Col xs={12} sm={4} className="text-center text-sm-end">
|
||||
|
||||
<Col xs={12} sm={4} className="text-center my-2 my-md-0">
|
||||
{user?.email}
|
||||
</Col>
|
||||
<Col xs={12} sm={4} className="text-center text-sm-end">
|
||||
<Button type="button" variant="link" onClick={() => goOut()} className="logout-button">
|
||||
|
||||
<Col xs={12} sm={4} className="text-center text-md-end">
|
||||
<Button
|
||||
type="button"
|
||||
variant="link"
|
||||
onClick={() => goOut()}
|
||||
className="logout-button"
|
||||
>
|
||||
Logout
|
||||
</Button>
|
||||
</Col>
|
||||
|
||||
@@ -2,18 +2,37 @@
|
||||
background-color: #f8f9fa;
|
||||
padding: 20px 0;
|
||||
margin-top: 50px;
|
||||
}
|
||||
border-top: 1px solid #dee2e6;
|
||||
|
||||
.link {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
}
|
||||
.footer-link {
|
||||
color: #007bff;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
transition: color 0.2s ease-in-out;
|
||||
|
||||
.link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
&:hover {
|
||||
color: #0056b3;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.logout-button {
|
||||
color: #dc3545!important;
|
||||
text-decoration: none!important;
|
||||
.logout-button {
|
||||
color: #dc3545 !important;
|
||||
text-decoration: none !important;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.footer-link {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.logout-button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
//$theme-colors: (
|
||||
// 'info': tomato,
|
||||
// 'danger': teal,
|
||||
// 'primary': green,
|
||||
//);
|
||||
$theme-colors: (
|
||||
'info': tomato,
|
||||
'danger': teal,
|
||||
'primary': #61cd8d,
|
||||
);
|
||||
|
||||
@import '~bootstrap/scss/bootstrap';
|
||||
|
||||
.bg-body-secondary {
|
||||
background-color: red;
|
||||
$primary-color: #61cd8d;
|
||||
$light-bg: #f8f9fa;
|
||||
$dark-text: #343a40;
|
||||
|
||||
body {
|
||||
background-color: $light-bg;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
type SummaryResponse = {
|
||||
pendingTaskCount: number,
|
||||
doneTaskCount: number
|
||||
doneTaskCount: number,
|
||||
notesCount: number
|
||||
}
|
||||
|
||||
export type { SummaryResponse };
|
||||
|
||||
@@ -1,11 +1,84 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Card, Col, Container, Row
|
||||
} from 'react-bootstrap';
|
||||
|
||||
/**
|
||||
* About page component.
|
||||
*
|
||||
* This component displays information about the application,
|
||||
* the technology used, and the developer.
|
||||
*
|
||||
* @returns {JSX.Element} The About page component.
|
||||
*/
|
||||
function About() {
|
||||
function About(): JSX.Element {
|
||||
return (
|
||||
<h1>This is about!</h1>
|
||||
<Container className="about-page my-5">
|
||||
<Row className="justify-content-center mb-4">
|
||||
<Col xs={12} md={10} lg={8}>
|
||||
<Card className="p-4 shadow-sm">
|
||||
<h2 className="text-center mb-4">About TaskNote</h2>
|
||||
<p>
|
||||
TaskNote is your go-to application for managing tasks and notes in one convenient
|
||||
place. Whether you're keeping track of daily to-dos or organizing notes from
|
||||
important meetings, TaskNote helps you stay organized and productive.
|
||||
</p>
|
||||
<h4 className="mt-4">Features</h4>
|
||||
<ul>
|
||||
<li>Quickly add and manage tasks and notes</li>
|
||||
<li>Search and filter notes for easy access</li>
|
||||
<li>Intuitive and clean user interface</li>
|
||||
</ul>
|
||||
<h4 className="mt-4">Help & How to Use</h4>
|
||||
<p>
|
||||
To get started, simply sign up or log in, and you'll have access to your
|
||||
personalized dashboard. From there, you can create, edit, and delete tasks and notes,
|
||||
and organize them however you like. Need assistance? Visit our Help page (in the
|
||||
future) for tutorials and FAQs.
|
||||
</p>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row className="justify-content-center mb-4">
|
||||
<Col xs={12} md={10} lg={8}>
|
||||
<Card className="p-4 shadow-sm">
|
||||
<h2 className="text-center mb-4">About the Technology</h2>
|
||||
<p>
|
||||
TaskNote is built using modern web technologies that ensure speed, reliability, and
|
||||
security.
|
||||
</p>
|
||||
<ul>
|
||||
<li>React with TypeScript for front-end development</li>
|
||||
<li>Bootstrap 5 for responsive design and components</li>
|
||||
<li>Spring Boot for backend services</li>
|
||||
<li>PostgreSQL for database management</li>
|
||||
<li>Docker for containerization and deployment</li>
|
||||
<li>GitHub Actions for CI/CD, testing and linting enforcement</li>
|
||||
<li>SonarCloud, and GitHub QL for security and improvements checks</li>
|
||||
</ul>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row className="justify-content-center">
|
||||
<Col xs={12} md={10} lg={8}>
|
||||
<Card className="p-4 shadow-sm">
|
||||
<h2 className="text-center mb-4">About the Developer</h2>
|
||||
<p>
|
||||
Hi! I'm Ricardo, the developer of TaskNote. I'm
|
||||
passionate about building applications that make life easier
|
||||
and more organized. You can reach out to me at
|
||||
<a href="mailto:ricardompcampos@gmail.com" className="text-decoration-none">
|
||||
ricardompcampos@gmail.com
|
||||
</a>
|
||||
{' '}
|
||||
for any questions or feedback.
|
||||
</p>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
.about-page {
|
||||
.card {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
|
||||
|
||||
h2 {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
p, ul {
|
||||
font-size: 1rem;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: disc;
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $primary-color;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.card {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
p, ul {
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ function Home(): JSX.Element {
|
||||
|
||||
const getSummary = async () => {
|
||||
try {
|
||||
const response = await api.getJSON(`${ApiConfig.homeUrl}/summary`);
|
||||
const response: SummaryResponse = await api.getJSON(`${ApiConfig.homeUrl}/summary`);
|
||||
setSummary(response);
|
||||
} catch (e) {
|
||||
handleError(e);
|
||||
@@ -93,6 +93,10 @@ function Home(): JSX.Element {
|
||||
{summary?.pendingTaskCount && summary?.pendingTaskCount > 0
|
||||
? 'Pending Tasks'
|
||||
: 'No pending tasks!'}
|
||||
<br />
|
||||
{summary?.doneTaskCount && summary?.doneTaskCount > 0
|
||||
? ` ${summary?.doneTaskCount} done tasks!`
|
||||
: 'No done tasks!'}
|
||||
</Card.Text>
|
||||
<Button
|
||||
variant="primary"
|
||||
@@ -107,14 +111,16 @@ function Home(): JSX.Element {
|
||||
|
||||
<Col xs={12} md={6}>
|
||||
<Card className="text-center h-100">
|
||||
<Card.Header className="bg-success text-white">
|
||||
<Card.Header className="bg-primary text-white">
|
||||
Notes Summary
|
||||
</Card.Header>
|
||||
<Card.Body className="d-flex flex-column align-items-center justify-content-center">
|
||||
<Card.Title className="display-4">10</Card.Title>
|
||||
<Card.Title className="display-4">
|
||||
{summary?.notesCount}
|
||||
</Card.Title>
|
||||
<Card.Text>Notes</Card.Text>
|
||||
<Button
|
||||
variant="success"
|
||||
variant="primary"
|
||||
type="button"
|
||||
onClick={() => navigate('/notes')}
|
||||
>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useCallback, useContext, useEffect } from 'react';
|
||||
import { Button, Container } from 'react-bootstrap';
|
||||
import './styles.scss';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import AuthContext from '../../context/AuthContext';
|
||||
import './styles.scss';
|
||||
|
||||
/**
|
||||
* Landing page component.
|
||||
@@ -45,10 +45,10 @@ function Landing(): JSX.Element {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Container fluid className="vh-100 d-flex justify-content-center align-items-center">
|
||||
<div className="text-center">
|
||||
<h1 className="display-4 fw-bold">Welcome to TaskNote App</h1>
|
||||
<p className="lead mb-4">
|
||||
<Container fluid className="vh-100 d-flex justify-content-center align-items-center landing-page">
|
||||
<div>
|
||||
<h1 className="display-4">Welcome to TaskNote</h1>
|
||||
<p className="lead">
|
||||
Your best friend to keep up with notes and tasks!
|
||||
</p>
|
||||
|
||||
@@ -71,7 +71,6 @@ function Landing(): JSX.Element {
|
||||
</Button>
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,62 @@
|
||||
body {
|
||||
background-color: #ccc;
|
||||
$primary-color: #61cd8d;
|
||||
$light-bg: #f8f9fa;
|
||||
$dark-text: #343a40;
|
||||
|
||||
.landing-page {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
color: $dark-text;
|
||||
background: url('../../assets/landing-bg.jpg') no-repeat center center;
|
||||
background-size: cover; // Ensures the image covers the whole background
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(255, 255, 255, 0.85); // Slight white overlay for text readability
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
div {
|
||||
position: relative;
|
||||
z-index: 2; // Ensures the content is above the background and overlay
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 1rem;
|
||||
color: $dark-text;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: 2rem;
|
||||
color: $dark-text;
|
||||
}
|
||||
|
||||
.btn-lg {
|
||||
font-size: 1rem;
|
||||
padding: 0.75rem 1.5rem;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: $primary-color;
|
||||
border-color: $primary-color;
|
||||
&:hover {
|
||||
background-color: darken($primary-color, 10%);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-outline-primary {
|
||||
color: $primary-color;
|
||||
border-color: $primary-color;
|
||||
&:hover {
|
||||
background-color: $primary-color;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@ import React, {
|
||||
import {
|
||||
Alert, Button, Card, Col, Container, Form, Row
|
||||
} from 'react-bootstrap';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import AuthContext from '../../context/AuthContext';
|
||||
import './styles.scss';
|
||||
|
||||
/**
|
||||
* Login page component.
|
||||
@@ -45,6 +46,7 @@ function Login(): JSX.Element {
|
||||
const form = event.currentTarget;
|
||||
if (form.checkValidity() === false) {
|
||||
setFormInvalid(true);
|
||||
setErrorMessage('Please fill in your username and password!');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -65,12 +67,12 @@ function Login(): JSX.Element {
|
||||
useEffect(() => {}, [formInvalid]);
|
||||
|
||||
return (
|
||||
<Container className="vh-100 d-flex justify-content-center align-items-center">
|
||||
<Container fluid className="login-page">
|
||||
<Row className="justify-content-center w-100">
|
||||
<Col xs={12} md={6} lg={4}>
|
||||
<Card>
|
||||
<Card.Body className="p-4">
|
||||
<h2 className="text-center mb-4">Login</h2>
|
||||
<h2 className="text-center">Login</h2>
|
||||
|
||||
{formInvalid ? (
|
||||
<Alert variant="danger">
|
||||
@@ -80,12 +82,12 @@ function Login(): JSX.Element {
|
||||
|
||||
<Form noValidate validated={validated} onSubmit={handleSubmit}>
|
||||
<Form.Group className="mb-3" controlId="formBasicEmail">
|
||||
<Form.Label>Email address</Form.Label>
|
||||
<Form.Label>Email</Form.Label>
|
||||
<Form.Control
|
||||
required
|
||||
type="email"
|
||||
name="email"
|
||||
placeholder="Enter email"
|
||||
placeholder="Type your email here"
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
@@ -95,7 +97,7 @@ function Login(): JSX.Element {
|
||||
required
|
||||
type="password"
|
||||
name="password"
|
||||
placeholder="Password"
|
||||
placeholder="Type your password here"
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
@@ -109,16 +111,16 @@ function Login(): JSX.Element {
|
||||
</Form>
|
||||
|
||||
<div className="text-center mt-3">
|
||||
Don't have an account?
|
||||
<a href="/register" className="text-decoration-none">
|
||||
Register
|
||||
</a>
|
||||
Don't have an account yet?
|
||||
<Link to="/register" className="text-decoration-none">
|
||||
Register here
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="text-center mt-3">
|
||||
<a href="/" className="text-decoration-none">
|
||||
<Link to="/" className="text-decoration-none">
|
||||
Back to home
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
$primary-color: #61cd8d;
|
||||
$light-bg: #f8f9fa;
|
||||
$dark-text: #343a40;
|
||||
|
||||
.login-page {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
background: url('../../assets/landing-bg.jpg') no-repeat center center;
|
||||
background-size: cover;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(255, 255, 255, 0.85); // White overlay for readability
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.card {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 1.5rem;
|
||||
color: $dark-text;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: $primary-color;
|
||||
border-color: $primary-color;
|
||||
&:hover {
|
||||
background-color: darken($primary-color, 10%);
|
||||
}
|
||||
}
|
||||
|
||||
.text-center a {
|
||||
color: $primary-color;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
background-color: rgba(255, 0, 0, 0.1);
|
||||
border-color: rgba(255, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import React, {
|
||||
import {
|
||||
Alert, Button, Card, Col, Container, Form, Row
|
||||
} from 'react-bootstrap';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import AuthContext from '../../context/AuthContext';
|
||||
|
||||
/**
|
||||
@@ -45,6 +45,7 @@ function Register(): JSX.Element {
|
||||
const form = event.currentTarget;
|
||||
if (form.checkValidity() === false) {
|
||||
setFormInvalid(true);
|
||||
setErrorMessage('Please fill in your username and password!');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -65,12 +66,12 @@ function Register(): JSX.Element {
|
||||
useEffect(() => {}, [formInvalid]);
|
||||
|
||||
return (
|
||||
<Container className="vh-100 d-flex justify-content-center align-items-center">
|
||||
<Container fluid className="login-page">
|
||||
<Row className="justify-content-center w-100">
|
||||
<Col xs={12} md={6} lg={4}>
|
||||
<Card>
|
||||
<Card.Body className="p-4">
|
||||
<h2 className="text-center mb-4">Create account</h2>
|
||||
<h2 className="text-center">Create account</h2>
|
||||
|
||||
{formInvalid ? (
|
||||
<Alert variant="danger">
|
||||
@@ -80,12 +81,12 @@ function Register(): JSX.Element {
|
||||
|
||||
<Form noValidate validated={validated} onSubmit={handleSubmit}>
|
||||
<Form.Group className="mb-3" controlId="formBasicEmail">
|
||||
<Form.Label>Email address</Form.Label>
|
||||
<Form.Label>Email</Form.Label>
|
||||
<Form.Control
|
||||
required
|
||||
type="email"
|
||||
name="email"
|
||||
placeholder="Enter email"
|
||||
placeholder="Type your email here"
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
@@ -95,7 +96,7 @@ function Register(): JSX.Element {
|
||||
required
|
||||
type="password"
|
||||
name="password"
|
||||
placeholder="Password"
|
||||
placeholder="Type your password here"
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
@@ -104,25 +105,21 @@ function Register(): JSX.Element {
|
||||
type="submit"
|
||||
className="w-100"
|
||||
>
|
||||
Login
|
||||
Sign-up and register
|
||||
</Button>
|
||||
</Form>
|
||||
|
||||
<div className="text-center mt-3">
|
||||
Already have an account?
|
||||
<a href="/login" className="text-decoration-none">
|
||||
Login
|
||||
</a>
|
||||
or
|
||||
<a href="/" className="text-decoration-none">
|
||||
Reset
|
||||
</a>
|
||||
<Link to="/login" className="text-decoration-none">
|
||||
Go to Login
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="text-center mt-3">
|
||||
<a href="/" className="text-decoration-none">
|
||||
<Link to="/" className="text-decoration-none">
|
||||
Back to home
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
package br.com.tasknoteapp.server.response;
|
||||
|
||||
public record SummaryResponse(Integer pendingTaskCount, Integer doneTaskCount) {}
|
||||
public record SummaryResponse(
|
||||
Integer pendingTaskCount, Integer doneTaskCount, Integer notesCount) {}
|
||||
|
||||
@@ -31,11 +31,13 @@ class HomeServiceImpl implements HomeService {
|
||||
public SummaryResponse getSummary() {
|
||||
log.info("Getting summary");
|
||||
List<TaskResponse> tasks = taskService.getAllTasks();
|
||||
List<NoteResponse> notes = noteService.getAllNotes();
|
||||
|
||||
long doneCount = tasks.stream().filter(t -> t.done() == true).count();
|
||||
long undoneCount = tasks.stream().filter(t -> t.done() == false).count();
|
||||
int taskCount = notes.size();
|
||||
|
||||
return new SummaryResponse((int) undoneCount, (int) doneCount);
|
||||
return new SummaryResponse((int) undoneCount, (int) doneCount, taskCount);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,7 +35,7 @@ class HomeControllerTest {
|
||||
@DisplayName("Get summary happy path should succeed")
|
||||
@WithMockUser(username = "user@domain.com", password = "abcde123456A@")
|
||||
void getSummary_happyPath_shouldSucceed() throws Exception {
|
||||
SummaryResponse response = new SummaryResponse(0, 6);
|
||||
SummaryResponse response = new SummaryResponse(0, 6, 2);
|
||||
|
||||
when(homeService.getSummary()).thenReturn(response);
|
||||
|
||||
@@ -48,13 +48,14 @@ class HomeControllerTest {
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.pendingTaskCount").value(response.pendingTaskCount()))
|
||||
.andExpect(jsonPath("$.doneTaskCount").value(response.doneTaskCount()))
|
||||
.andExpect(jsonPath("$.notesCount").value(response.notesCount()))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Get summary user not authorized should fail")
|
||||
void getSummary_userNotAuthorized_shouldFail() throws Exception {
|
||||
SummaryResponse response = new SummaryResponse(0, 6);
|
||||
SummaryResponse response = new SummaryResponse(0, 6, 2);
|
||||
|
||||
when(homeService.getSummary()).thenReturn(response);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user