Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,498 changes: 1,140 additions & 358 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"vite": "^4.4.5"
"vite": "^7.1.3"
}
}
107 changes: 74 additions & 33 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useState } from 'react'

import initialEmails from './data/emails'

import './styles/App.css'
import Emails from './components/Emails'
import EmailDetail from './components/EmailDetail'

const getReadEmails = emails => emails.filter(email => !email.read)

Expand All @@ -12,6 +12,9 @@ function App() {
const [emails, setEmails] = useState(initialEmails)
const [hideRead, setHideRead] = useState(false)
const [currentTab, setCurrentTab] = useState('inbox')
const [searchQuery, setSearchQuery] = useState('')
const [openedEmail, setOpenedEmail] = useState(null)
const [sortBy, setSortBy] = useState('newest')

const unreadEmails = emails.filter(email => !email.read)
const starredEmails = emails.filter(email => email.starred)
Expand All @@ -34,13 +37,47 @@ function App() {
setEmails(updatedEmails)
}

const openEmail = email => {
setOpenedEmail(email)
}

const toggleAllRead = () => {
const allRead = emails.every(email => email.read)
const updatedEmails = emails.map(email => ({ ...email, read: !allRead }))
setEmails(updatedEmails)
}

// filtering
let filteredEmails = emails

if (hideRead) filteredEmails = getReadEmails(filteredEmails)

if (currentTab === 'starred')
filteredEmails = getStarredEmails(filteredEmails)

// search filter for emails by title
if (searchQuery) {
filteredEmails = filteredEmails.filter(email =>
email.title.toLowerCase().includes(searchQuery.toLowerCase())
)
}

// sorting
if (sortBy === "newest") {
filteredEmails = [...filteredEmails].sort(
(a, b) => new Date(b.date) - new Date(a.date)
)
}
else if (sortBy === "oldest") {
filteredEmails = [...filteredEmails].sort(
(a, b) => new Date(a.date) - new Date(b.date)
)
} else if (sortBy === "starred") {
filteredEmails = [...filteredEmails].sort(
(a, b) => b.starred - a.starred
)
}

return (
<div className="app">
<header className="header">
Expand All @@ -56,7 +93,19 @@ function App() {
</div>

<div className="search">
<input className="search-bar" placeholder="Search mail" />
<input className="search-bar" placeholder="Search mail" onChange = {e => setSearchQuery(e.target.value)} value = {searchQuery} />
</div>

<div className="sort">
<select
value={sortBy}
onChange={(e) => setSortBy(e.target.value)}
className="sort-dropdown"
>
<option value="newest">Newest first</option>
<option value="oldest">Oldest first</option>
<option value="starred">Starred</option>
</select>
</div>
</header>
<nav className="left-menu">
Expand All @@ -65,56 +114,48 @@ function App() {
className={`item ${currentTab === 'inbox' ? 'active' : ''}`}
onClick={() => setCurrentTab('inbox')}
>
<span className="label">Inbox</span>
<span className="label"><strong>Inbox</strong></span>
<span className="count">{unreadEmails.length}</span>
</li>
<li
className={`item ${currentTab === 'starred' ? 'active' : ''}`}
onClick={() => setCurrentTab('starred')}
>
<span className="label">Starred</span>
<span className="label"><strong>Starred</strong></span>
<span className="count">{starredEmails.length}</span>
</li>

<li className="item toggle">
<label htmlFor="hide-read">Hide read</label>
<label htmlFor="hide-read"><strong>Hide read</strong></label>
<input
id="hide-read"
type="checkbox"
checked={hideRead}
onChange={e => setHideRead(e.target.checked)}
/>
</li>
<li className="item toggle">
<button className="mark-all-button" onClick={() => { toggleAllRead() }}>
{emails.every(email => email.read) ? "Mark all as unread" : "Mark all asread"}
</button>
</li>
</ul>
</nav>
<main className="emails">
<ul>
{filteredEmails.map((email, index) => (
<li
key={index}
className={`email ${email.read ? 'read' : 'unread'}`}
>
<div className="select">
<input
className="select-checkbox"
type="checkbox"
checked={email.read}
onChange={() => toggleRead(email)}
/>
</div>
<div className="star">
<input
className="star-checkbox"
type="checkbox"
checked={email.starred}
onChange={() => toggleStar(email)}
/>
</div>
<div className="sender">{email.sender}</div>
<div className="title">{email.title}</div>
</li>
))}
</ul>
{/* If an email is opened, show the detail. Otherwise, show the Emails component. */}
{openedEmail ? (
<EmailDetail
email={openedEmail}
onBack={() => setOpenedEmail(null)}
/>
) : (
<Emails
emails={filteredEmails}
onToggleRead={toggleRead}
onToggleStar={toggleStar}
onOpenEmail={openEmail}
/>
)}
</main>
</div>
)
Expand Down
32 changes: 32 additions & 0 deletions src/components/Email.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import '../styles/Email.css'

function Email ({ email, onToggleRead, onToggleStar, onOpenEmail }) {
return (
<li className={`email ${email.read ? 'read' : 'unread'}`}
onClick={() => onOpenEmail(email)}
>
<div className="select">
<input
className="select-checkbox"
type="checkbox"
checked={email.read}
onClick={(e) => {e.stopPropagation() }}
onChange={() => onToggleRead(email)}
/>
</div>
<div className="star">
<input
className="star-checkbox"
type="checkbox"
checked={email.starred}
onClick={(e) => e.stopPropagation()}
onChange={() => onToggleStar(email)}
/>
</div>
<div className="sender">{email.sender}</div>
<div className="title">{email.title}</div>
</li>
)
}

export default Email
14 changes: 14 additions & 0 deletions src/components/EmailDetail.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import '../styles/EmailDetail.css'

function EmailDetail({ email, onBack }) {
return (
<div className="email-detail">
<button className="back-button" onClick={onBack}>Back to inbox</button>
<h2>{email.title}</h2>
<div>From: {email.sender}</div>
<div>{email.body}</div>
</div>
)
}

export default EmailDetail
20 changes: 20 additions & 0 deletions src/components/Emails.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Email from './Email'

{/* Render the email list */}
function Emails ({ emails, onToggleRead, onToggleStar, onOpenEmail }) {
return (
<ul>
{emails.map((email, id) => (
<Email
key={id}
email={email}
onToggleRead={onToggleRead}
onToggleStar={onToggleStar}
onOpenEmail={onOpenEmail}
/>
))}
</ul>
)
}

export default Emails
55 changes: 50 additions & 5 deletions src/data/emails.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,79 @@ export default [
sender: `Zoom`,
title: `Cloud Recording - Nicolas Marcora's Personal Meeting Room is now available`,
starred: false,
read: true
read: true,
date: new Date("2023-12-01T09:15:00")
},
{
id: 2,
sender: `Zoom`,
title: `Sean Davison has joined your Personal Meeting Room`,
starred: false,
read: false
read: false,
date: new Date("2023-12-01T10:00:00")
},
{
id: 3,
sender: `Notion`,
title: `1 update in Boolean`,
starred: true,
read: true
read: true,
date: new Date("2023-12-02T08:45:00")
},
{
id: 4,
sender: `The Calendly Team`,
title: `Use more than one calendar?`,
starred: false,
read: false
read: false,
date: new Date("2023-12-02T14:20:00")
},
{
id: 5,
sender: `Patrick`,
title: `Updated invitation: Coding chat with Nico`,
starred: true,
read: false
read: false,
date: new Date("2023-12-03T11:30:00")
},
{
id: 6,
sender: "Dora the Explorer",
title: "Map Update: Treasure Hunt in Backyard",
starred: false,
read: false,
date: new Date("2023-12-03T16:45:00")
},
{
id: 7,
sender: "Captain Crunch",
title: "URGENT: Cereal Crisis Meeting",
starred: true,
read: true,
date: new Date("2023-12-04T07:15:00")
},
{
id: 8,
sender: "Grandma",
title: "Homemade Cookies Recipe Inside!",
starred: false,
read: true,
date: new Date("2023-12-04T18:00:00")
},
{
id: 9,
sender: "Lionel Messi",
title: "Need some help working on my freekicks",
starred: false,
read: true,
date: new Date("2023-12-05T09:00:00")
},
{
id: 10,
sender: "Wayne Rooney",
title: "Manchester United will never be the same as during my generation",
starred: true,
read: true,
date: new Date("2023-12-05T20:10:00")
}
]
Loading