diff --git a/images/gmail-email-view-intro.png b/images/gmail-email-view-intro.png new file mode 100644 index 0000000..7817237 Binary files /dev/null and b/images/gmail-email-view-intro.png differ diff --git a/images/write-email-component.png b/images/write-email-component.png new file mode 100644 index 0000000..cacb0f4 Binary files /dev/null and b/images/write-email-component.png differ diff --git a/package-lock.json b/package-lock.json index d603209..64d575b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "0.0.0", "dependencies": { "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^7.8.2" }, "devDependencies": { "@types/react": "^18.2.15", @@ -913,6 +914,15 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "dev": true, @@ -2561,6 +2571,44 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.8.2.tgz", + "integrity": "sha512-7M2fR1JbIZ/jFWqelpvSZx+7vd7UlBTfdZqf6OSdF9g6+sfdqJDAWcak6ervbHph200ePlu+7G8LdoiC3ReyAQ==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.8.2.tgz", + "integrity": "sha512-Z4VM5mKDipal2jQ385H6UBhiiEDlnJPx6jyWsTYoZQdl5TrjxEV2a9yl3Fi60NBJxYzOTGTTHXPi0pdizvTwow==", + "license": "MIT", + "dependencies": { + "react-router": "7.8.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.4", "dev": true, @@ -2725,6 +2773,12 @@ "semver": "bin/semver.js" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.1.1", "dev": true, diff --git a/package.json b/package.json index b9d8385..af5239e 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ }, "dependencies": { "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^7.8.2" }, "devDependencies": { "@types/react": "^18.2.15", diff --git a/src/App.jsx b/src/App.jsx index c902699..7d0d724 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,6 +1,8 @@ import { useState } from 'react' import initialEmails from './data/emails' +import Emails from './components/Emails' +import ViewEmail from './components/ViewEmail' import './styles/App.css' @@ -9,9 +11,10 @@ const getReadEmails = emails => emails.filter(email => !email.read) const getStarredEmails = emails => emails.filter(email => email.starred) function App() { - const [emails, setEmails] = useState(initialEmails) + const [emails, setEmails] = useState(initialEmails || []) const [hideRead, setHideRead] = useState(false) const [currentTab, setCurrentTab] = useState('inbox') + const [selectedEmail, setSelectedEmail] = useState(null); const unreadEmails = emails.filter(email => !email.read) const starredEmails = emails.filter(email => email.starred) @@ -41,10 +44,13 @@ function App() { if (currentTab === 'starred') filteredEmails = getStarredEmails(filteredEmails) + // left-menu Gmail logo make inbox/index button return (
-
+
console.log('Div clicked!')}> @@ -87,37 +93,23 @@ function App() { -
-
    - {filteredEmails.map((email, index) => ( -
  • -
    - toggleRead(email)} - /> -
    -
    - toggleStar(email)} - /> -
    -
    {email.sender}
    -
    {email.title}
    -
  • - ))} -
-
+
+ {selectedEmail ? ( + setSelectedEmail(null)} + /> + ) : ( + + )} +
) } -export default App +export default App; \ No newline at end of file diff --git a/src/assets/icons/back-arrow.png b/src/assets/icons/back-arrow.png new file mode 100644 index 0000000..77bf292 Binary files /dev/null and b/src/assets/icons/back-arrow.png differ diff --git a/src/assets/icons/download-button.png b/src/assets/icons/download-button.png new file mode 100644 index 0000000..ddb5a11 Binary files /dev/null and b/src/assets/icons/download-button.png differ diff --git a/src/assets/icons/rate-star-button.png b/src/assets/icons/rate-star-button.png new file mode 100644 index 0000000..16d6722 Binary files /dev/null and b/src/assets/icons/rate-star-button.png differ diff --git a/src/assets/icons/rubbish-bin-delete-button.png b/src/assets/icons/rubbish-bin-delete-button.png new file mode 100644 index 0000000..7090fb1 Binary files /dev/null and b/src/assets/icons/rubbish-bin-delete-button.png differ diff --git a/src/assets/images/flaticon-welcome-image.png b/src/assets/images/flaticon-welcome-image.png new file mode 100644 index 0000000..698c431 Binary files /dev/null and b/src/assets/images/flaticon-welcome-image.png differ diff --git a/src/components/Email.jsx b/src/components/Email.jsx new file mode 100644 index 0000000..00335bd --- /dev/null +++ b/src/components/Email.jsx @@ -0,0 +1,34 @@ +function Email({ index, email, toggleRead, toggleStar, viewEmail }) { + + return ( + <> +
  • +
    + toggleRead(email)} + /> +
    +
    + toggleStar(email)} + /> +
    +
    +
    {email.sender}
    +
    {email.title}
    +
    +
  • + + ) +} + +export default Email; \ No newline at end of file diff --git a/src/components/Emails.jsx b/src/components/Emails.jsx new file mode 100644 index 0000000..a65a08f --- /dev/null +++ b/src/components/Emails.jsx @@ -0,0 +1,22 @@ +import Email from './Email' + +function Emails({ emails = [], toggleRead, toggleStar, viewEmail }) { + return ( + <> +
    +
      + {emails.map((email, index) => ( + viewEmail(email)} /> + ))} +
    +
    + + ) +} + +export default Emails; \ No newline at end of file diff --git a/src/components/ViewEmail.jsx b/src/components/ViewEmail.jsx new file mode 100644 index 0000000..1430c88 --- /dev/null +++ b/src/components/ViewEmail.jsx @@ -0,0 +1,95 @@ +import flaticonWelcomeImage from '../assets/images/flaticon-welcome-image.png' +import backArrow from '../assets/icons/back-arrow.png' +import downloadButton from '../assets/icons/download-button.png' +import rateStarButton from '../assets/icons/rate-star-button.png' +import rubbishButton from '../assets/icons/rubbish-bin-delete-button.png' + +import '../styles/Mail.css' + +function ViewEmail({ email, goBack }) { + + return ( + <> +
    + +
    +
    +

    {email.title}

    +
    +
    +
    +
    +
    +

    {email.sender}

    + <{email.sender}@email.com> +
    +
    +

    + to me <nicolas@boolean.co.uk> +

    +
    +
    +
    +

    17 March 2021, 09:33

    +
    +
    +
      +
    • + reply button +
    • +
    • + star button +
    • +
    • + delete button +
    • +
    +
    +
    +
    +
    + Flaticon welcome message +
    +
    +
    + + +
    +
    +
    + + ); +} + +export default ViewEmail; \ No newline at end of file diff --git a/src/main.jsx b/src/main.jsx index fce89a2..ef85956 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,7 +1,7 @@ import React from 'react' import ReactDOM from 'react-dom/client' import App from './App.jsx' -import './styles/index.css' +import './styles/App.css' ReactDOM.createRoot(document.getElementById('root')).render( diff --git a/src/styles/App.css b/src/styles/App.css index da375b1..9b1158b 100644 --- a/src/styles/App.css +++ b/src/styles/App.css @@ -1,3 +1,10 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: sans-serif; +} + .app { height: 100vh; diff --git a/src/styles/Mail.css b/src/styles/Mail.css new file mode 100644 index 0000000..b5e0b07 --- /dev/null +++ b/src/styles/Mail.css @@ -0,0 +1,203 @@ +/* from the gmail-react-intro repository! */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: sans-serif; +} + +.app { + height: 100vh; + + display: grid; + grid-template: + 'header header' 50px + 'left-menu emails' 1fr / + 180px 1fr; +} + +.app > .header { + grid-area: header; + + display: grid; + grid-template-columns: 180px 1fr; + + border-bottom: solid 1px #00000020; +} + +.app > .header > .left-menu { + display: grid; + grid-auto-flow: column; + align-items: center; + justify-content: left; +} + +.app > .header > .left-menu .menu-icon { + width: 25px; + height: 25px; + margin: 10px; +} + +.app > .header > .search { + display: grid; + padding: 6px; +} + +.app > .header > .search .search-bar { + border: none; + border-radius: 5px; + background-color: rgb(240, 240, 240); + padding: 0 10px; + font-size: 1rem; +} + +.app > .left-menu { + grid-area: left-menu; + padding: 10px; + padding-left: 0; +} + +.app > .left-menu .inbox-list { + display: grid; + grid-gap: 10px; +} + +.app > .left-menu .inbox-list .item { + padding: 5px 10px; + display: grid; + grid-auto-flow: column; + justify-content: space-between; + align-items: center; + + border-top-right-radius: 20px; + border-bottom-right-radius: 20px; +} + +.app > .left-menu .inbox-list .item.active { + background-color: #ff000050; +} + +.app > .left-menu .inbox-list .item.toggle { + background-color: transparent; +} + +.email-toolbar { + padding: 0.5rem 2rem; + + display: grid; + grid-template-columns: auto 1fr auto; + + border-bottom: 1px solid #eee; +} + +.email-toolbar > * { + display: grid; + grid-auto-flow: column; + align-items: center; + gap: 1rem; +} + +.email-toolbar > ul { + list-style-type: none; +} + +.email-toolbar .icon { + width: 24px; + height: 24px; +} + +.email-toolbar button { + padding: 0.25rem 0.5rem 0.4rem; + + border-radius: 0.5rem; + border: 1px solid #a1a1a1; + background-color: transparent; + + font-weight: bold; +} + +.title { + padding: 1rem; +} + +.title h1 { + padding-left: 56px; + + font-weight: normal; +} + +.email-content--header { + padding: 0.5rem 1rem; + + display: grid; + grid-template-columns: auto 1fr auto auto; + gap: 1rem; +} + +.email-content--header .avatar { + width: 56px; + height: 56px; + + border-radius: 100%; + background-color: #eee; +} + +.email-info { + padding: 0.25rem 0; +} + +.email-info .sender-info h2 { + margin-right: 1rem; + + display: inline-block; + + font-size: 0.925rem; +} + +.email-info .user-info { + margin-top: 0.5rem; +} + +.email-info .user-info p { + font-size: 0.825rem; +} + +.email-action-icons ul { + padding: 0; + margin: 0; + + display: grid; + grid-auto-flow: column; + gap: 0.25rem; + + list-style-type: none; +} + +.email-action-icons .icon { + width: 20px; + height: 20px; +} + +.email-body .content { + max-width: 600px; + margin: 0 auto; +} + +.email-body .content img { + width: 100%; +} + +.email-actions { + padding: 1rem 0.5rem; +} + +.email-actions button { + padding: 0.5rem 1rem; + margin-right: 0.5rem; + + border-radius: 0.5rem; + border: 1px solid #a1a1a1; + background-color: transparent; + + font-weight: bold; +} diff --git a/src/styles/index.css b/src/styles/index.css deleted file mode 100644 index 48ed51c..0000000 --- a/src/styles/index.css +++ /dev/null @@ -1,6 +0,0 @@ -* { - margin: 0; - padding: 0; - box-sizing: border-box; - font-family: sans-serif; -}