Skip to content

Commit

Permalink
Solution
Browse files Browse the repository at this point in the history
  • Loading branch information
tania-kuzmenko committed Nov 16, 2024
1 parent 8b7ce12 commit de70568
Show file tree
Hide file tree
Showing 9 changed files with 578 additions and 248 deletions.
47 changes: 47 additions & 0 deletions src/App.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@use 'sass:math';

.Sidebar {
overflow: hidden;
opacity: 0;
Expand All @@ -21,3 +23,48 @@
.message-body {
white-space: pre-line;
}

/* missed classes */

@mixin tablet {
@media screen and (min-width: 769px), print {
@content;
}
}

$tile-spacing: 0.75rem !default;

.tile {
align-items: stretch;
display: block;
flex: 1;
min-height: min-content;

&.is-ancestor {
margin-left: $tile-spacing * -1;
margin-right: $tile-spacing * -1;
margin-top: $tile-spacing * -1;
&:last-child {
margin-bottom: $tile-spacing * -1; }
&:not(:last-child) {
margin-bottom: $tile-spacing; } }
&.is-child {
margin: 0 !important; }
&.is-parent {
padding: $tile-spacing; }
&.is-vertical {
flex-direction: column;

& > .tile.is-child:not( :last-child ) {
margin-bottom: 1.5rem !important; } }

@include tablet {
&:not(.is-child) {
display: flex;
}

@for $i from 1 through 12 {
&.is-#{$i} {
flex: none;
width: (math.div($i, 12)) * 100%; } } }
}
215 changes: 178 additions & 37 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useEffect, useState } from 'react';
import classNames from 'classnames';

import 'bulma/css/bulma.css';
Expand All @@ -9,52 +10,192 @@ 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>
import { User } from './types/User';
import { Post } from './types/Post';
import { getUsers } from './api/user';
import { getPosts } from './api/post';
import { addComment, deleteComment, getComments } from './api/comment';
import { Comment } from './types/Comment';

export const App = () => {
const [selectedUser, setSelectedUser] = useState<User | null>(null);
const [users, setUsers] = useState<User[]>([]);
const [isLoadingUsers, setIsLoadingUsers] = useState(false);
const [isLoadingPosts, setIsLoadingPosts] = useState(false);
const [isLoadingComments, setIsLoadingComments] = useState(false);
const [error, setError] = useState<string | null>(null);
const [selectedPost, setSelectedPost] = useState<Post | null>(null);
const [posts, setPosts] = useState<Post[]>([]);
const [comments, setComments] = useState<Comment[]>([]);
const [isFormOpen, setIsFormOpen] = useState(false);

useEffect(() => {
const fetchUsers = async () => {
setIsLoadingUsers(true);
try {
const usersData = await getUsers();

setUsers(usersData);
setError(null);
} catch (err) {
setError('Failed to load users');
} finally {
setIsLoadingUsers(false);
}
};

fetchUsers();
}, []);

useEffect(() => {
const fetchPosts = async () => {
setIsLoadingPosts(true);

try {
if (selectedUser) {
const userPosts = await getPosts(selectedUser.id);

setPosts(userPosts);
setError(null);
}
} catch (err) {
setError('Failed to load posts');
} finally {
setIsLoadingPosts(false);
}
};

fetchPosts();
}, [selectedUser]);

<div className="block" data-cy="MainContent">
<p data-cy="NoSelectedUser">No user selected</p>
useEffect(() => {
const fetchComments = async () => {
setIsLoadingComments(true);
try {
if (selectedPost) {
const postComments = await getComments(selectedPost.id);

<Loader />
setComments(postComments);
setError(null);
}
} catch (err) {
setError('Failed to load comments');
} finally {
setIsLoadingComments(false);
}
};

<div
className="notification is-danger"
data-cy="PostsLoadingError"
>
Something went wrong!
fetchComments();
}, [selectedPost]);

const handleDeleteComment = async (commentId: number) => {
try {
await deleteComment(commentId);
setComments(currentComments =>
currentComments.filter(comment => comment.id !== commentId),
);
} catch (err) {
setError('Unable to delete comment');
}
};

const handleAddComment = async (comment: Comment) => {
try {
const newComment: Comment = await addComment({
id: comment.id,
postId: comment.postId,
name: comment.name,
email: comment.email,
body: comment.body,
});

setComments(prevComments => [...prevComments, newComment]);
} catch (err) {
setError('Unable to add a comment');
throw err;
}
};

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">
{isLoadingUsers || isLoadingPosts ? (
<Loader />
) : (
<UserSelector
selectedUser={selectedUser}
users={users}
onSelect={setSelectedUser}
/>
)}
</div>

<div className="notification is-warning" data-cy="NoPostsYet">
No posts yet
<div className="block" data-cy="MainContent">
{selectedUser && !isLoadingPosts && !error ? (
posts.length > 0 ? (
<PostsList
posts={posts}
selectedPost={selectedPost}
onOpen={setSelectedPost}
onClose={() => {
setSelectedPost(null);
}}
/>
) : (
<div
className="notification is-warning"
data-cy="NoPostsYet"
>
No posts yet
</div>
)
) : (
<p data-cy="NoSelectedUser">No user selected</p>
)}
</div>

<PostsList />
{error && (
<div
className="notification is-danger"
data-cy="PostsLoadingError"
>
{error}
</div>
)}
</div>
</div>
</div>

<div
data-cy="Sidebar"
className={classNames(
'tile',
'is-parent',
'is-8-desktop',
'Sidebar',
'Sidebar--open',
{selectedPost && (
<div
data-cy="Sidebar"
className={classNames(
'tile',
'is-parent',
'is-8-desktop',
'Sidebar',
'Sidebar--open',
)}
>
<div className="tile is-child box is-success ">
<PostDetails
post={selectedPost}
error={error}
comments={comments}
formOpen={setIsFormOpen}
isFormOpen={isFormOpen}
deleteComment={handleDeleteComment}
addComment={handleAddComment}
isLoadingComments={isLoadingComments}
commentLoading={() => setIsLoadingComments}
/>
</div>
</div>
)}
>
<div className="tile is-child box is-success ">
<PostDetails />
</div>
</div>
</div>
</div>
</main>
);
</main>
);
};
14 changes: 14 additions & 0 deletions src/api/comment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Comment } from '../types/Comment';
import { client } from '../utils/fetchClient';

export const getComments = (postId: number) => {
return client.get<Comment[]>(`/comments?postId=${postId}`);
};

export function deleteComment(commentId: number) {
return client.delete(`/comments/${commentId}`);
}

export function addComment({ postId, name, email, body }: Comment) {
return client.post<Comment>('/comments', { postId, name, email, body });
}
6 changes: 6 additions & 0 deletions src/api/post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Post } from '../types/Post';
import { client } from '../utils/fetchClient';

export const getPosts = (userId: number) => {
return client.get<Post[]>(`/posts?userId=${userId}`);
};
6 changes: 6 additions & 0 deletions src/api/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { User } from '../types/User';
import { client } from '../utils/fetchClient';

export const getUsers = () => {
return client.get<User[]>('/users');
};
Loading

0 comments on commit de70568

Please sign in to comment.