Skip to content

Commit

Permalink
dynamic-list-of-posts
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex-redman committed Feb 17, 2025
1 parent 611a1dc commit ff8ff64
Show file tree
Hide file tree
Showing 5 changed files with 450 additions and 266 deletions.
91 changes: 47 additions & 44 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,63 @@
import { useState } from 'react';
import classNames from 'classnames';

import 'bulma/css/bulma.css';
import '@fortawesome/fontawesome-free/css/all.css';
import './App.scss';

import { UserSelector } from './components/UserSelector';
import { PostsList } from './components/PostsList';
import { PostDetails } from './components/PostDetails';
import { UserSelector } from './components/UserSelector';
import { Loader } from './components/Loader';

export const App = () => (
<main className="section">
<div className="container">
<div className="tile is-ancestor">
<div className="tile is-parent">
<div className="tile is-child box is-success">
<div className="block">
<UserSelector />
</div>

<div className="block" data-cy="MainContent">
<p data-cy="NoSelectedUser">No user selected</p>

<Loader />

<div
className="notification is-danger"
data-cy="PostsLoadingError"
>
Something went wrong!
import { Post } from './types/Post';

export const App = () => {
const [selectedUserId, setSelectedUserId] = useState<number | null>(null);
const [selectedPost, setSelectedPost] = useState<Post | null>(null);

const handleUserSelect = (userId: number | null) => {
setSelectedUserId(userId);
setSelectedPost(null);
};

return (
<main className="section">
<div className="container">
<div className="tile is-ancestor">
<div className="tile is-parent">
<div className="tile is-child box is-success">
<div className="block">
<UserSelector onUserSelect={handleUserSelect} />
</div>

<div className="notification is-warning" data-cy="NoPostsYet">
No posts yet
<div className="block" data-cy="MainContent">
{selectedUserId === null ? (
<p data-cy="NoSelectedUser">No user selected</p>
) : (
<PostsList
userId={selectedUserId}
selectedPost={selectedPost}
onPostSelect={setSelectedPost}
/>
)}
</div>

<PostsList />
</div>
</div>
</div>

<div
data-cy="Sidebar"
className={classNames(
'tile',
'is-parent',
'is-8-desktop',
'Sidebar',
'Sidebar--open',
)}
>
<div className="tile is-child box is-success ">
<PostDetails />
<div
data-cy="Sidebar"
className={classNames(
'tile',
'is-parent',
'is-8-desktop',
'Sidebar',
{ 'Sidebar--open': selectedPost !== null }
)}
>
<div className="tile is-child box is-success">
<PostDetails post={selectedPost} />
</div>
</div>
</div>
</div>
</div>
</main>
);
</main>
);
};
180 changes: 135 additions & 45 deletions src/components/NewCommentForm.tsx
Original file line number Diff line number Diff line change
@@ -1,99 +1,189 @@
import React from 'react';
/* eslint-disable no-console */
import React, { useState } from 'react';
import { CommentData } from '../types/Comment';

interface NewCommentFormProps {
onSubmit: (data: CommentData) => void;
}

export const NewCommentForm: React.FC<NewCommentFormProps> = ({ onSubmit }) => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [body, setBody] = useState('');
const [errors, setErrors] = useState<{
name?: string;
email?: string;
body?: string;
}>({});
const [isSubmitting, setIsSubmitting] = useState(false);
const [submitted, setSubmitted] = useState(false);

const validate = () => {
const newErrors: { name?: string; email?: string; body?: string } = {};

if (!name.trim()) {
newErrors.name = 'Name is required';
}

if (!email.trim()) {
newErrors.email = 'Email is required';
}

if (!body.trim()) {
newErrors.body = 'Enter some text';
}

return newErrors;
};

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setSubmitted(true);
const newErrors = validate();

if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);

return;
}

setIsSubmitting(true);
try {
await onSubmit({ name, email, body });
setBody('');
setErrors({});
setSubmitted(false);
} catch (err) {
console.error('Failed to add comment', err);
} finally {
setIsSubmitting(false);
}
};

const handleClear = () => {
setErrors({});
setBody('');
};

export const NewCommentForm: React.FC = () => {
return (
<form data-cy="NewCommentForm">
<form data-cy="NewCommentForm" onSubmit={handleSubmit}>
<div className="field" data-cy="NameField">
<label className="label" htmlFor="comment-author-name">
Author Name
</label>

<div className="control has-icons-left has-icons-right">
<input
type="text"
name="name"
id="comment-author-name"
name="name"
placeholder="Name Surname"
className="input is-danger"
className={`input ${submitted && errors.name ? 'is-danger' : ''}`}
value={name}
onChange={e => {
setName(e.target.value);
if (errors.name) {
setErrors(prev => ({ ...prev, name: undefined }));
}
}}
/>

<span className="icon is-small is-left">
<i className="fas fa-user" />
</span>

<span
className="icon is-small is-right has-text-danger"
data-cy="ErrorIcon"
>
<i className="fas fa-exclamation-triangle" />
<i className="fas fa-user"></i>
</span>
{submitted && errors.name && (
<span
className="icon is-small is-right has-text-danger"
data-cy="ErrorIcon"
>
<i className="fas fa-exclamation-triangle"></i>
</span>
)}
</div>

<p className="help is-danger" data-cy="ErrorMessage">
Name is required
</p>
{submitted && errors.name && (
<p className="help is-danger" data-cy="ErrorMessage">
{errors.name}
</p>
)}
</div>

<div className="field" data-cy="EmailField">
<label className="label" htmlFor="comment-author-email">
Author Email
</label>

<div className="control has-icons-left has-icons-right">
<input
type="text"
name="email"
id="comment-author-email"
name="email"
placeholder="[email protected]"
className="input is-danger"
className={`input ${submitted && errors.email ? 'is-danger' : ''}`}
value={email}
onChange={e => {
setEmail(e.target.value);
if (errors.email) {
setErrors(prev => ({ ...prev, email: undefined }));
}
}}
/>

<span className="icon is-small is-left">
<i className="fas fa-envelope" />
</span>

<span
className="icon is-small is-right has-text-danger"
data-cy="ErrorIcon"
>
<i className="fas fa-exclamation-triangle" />
<i className="fas fa-envelope"></i>
</span>
{submitted && errors.email && (
<span
className="icon is-small is-right has-text-danger"
data-cy="ErrorIcon"
>
<i className="fas fa-exclamation-triangle"></i>
</span>
)}
</div>

<p className="help is-danger" data-cy="ErrorMessage">
Email is required
</p>
{submitted && errors.email && (
<p className="help is-danger" data-cy="ErrorMessage">
{errors.email}
</p>
)}
</div>

<div className="field" data-cy="BodyField">
<label className="label" htmlFor="comment-body">
Comment Text
</label>

<div className="control">
<textarea
id="comment-body"
name="body"
placeholder="Type comment here"
className="textarea is-danger"
className={`textarea ${submitted && errors.body ? 'is-danger' : ''}`}
value={body}
onChange={e => {
setBody(e.target.value);
if (errors.body) {
setErrors(prev => ({ ...prev, body: undefined }));
}
}}
/>
</div>

<p className="help is-danger" data-cy="ErrorMessage">
Enter some text
</p>
{submitted && errors.body && (
<p className="help is-danger" data-cy="ErrorMessage">
{errors.body}
</p>
)}
</div>

<div className="field is-grouped">
<div className="control">
<button type="submit" className="button is-link is-loading">
<button
type="submit"
className={`button is-link ${isSubmitting ? 'is-loading' : ''}`}
>
Add
</button>
</div>

<div className="control">
{/* eslint-disable-next-line react/button-has-type */}
<button type="reset" className="button is-link is-light">
<button
type="reset"
className="button is-link is-light"
onClick={handleClear}
>
Clear
</button>
</div>
Expand Down
Loading

0 comments on commit ff8ff64

Please sign in to comment.