From ae0106b8d13954a63d04fc780e0ea0a004ca86cd Mon Sep 17 00:00:00 2001
From: Guillermo Zevallos
Date: Tue, 27 Dec 2022 19:25:12 -0500
Subject: [PATCH 01/20] added sendmessage with formik and fetch
---
client/src/App.tsx | 3 ++
client/src/components/Board.tsx | 2 +-
client/src/components/SendMessage.tsx | 67 +++++++++++++++++++++++++++
client/src/service/message.ts | 24 ++++++++--
4 files changed, 91 insertions(+), 5 deletions(-)
create mode 100644 client/src/components/SendMessage.tsx
diff --git a/client/src/App.tsx b/client/src/App.tsx
index d93e2ed..ebb4232 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -1,6 +1,7 @@
import { useState } from 'react'
import { GlobalStyle } from './theme/GlobalStyle'
import Board from './components/Board'
+import SendMessage from './components/SendMessage'
function App() {
@@ -9,6 +10,8 @@ function App() {
Mini Message Board
+
+
)
}
diff --git a/client/src/components/Board.tsx b/client/src/components/Board.tsx
index d429a20..eb297f7 100644
--- a/client/src/components/Board.tsx
+++ b/client/src/components/Board.tsx
@@ -7,7 +7,7 @@ const Board = () => {
useEffect(() => {
getMessage().then((data)=>setMessages(data))
- console.log(messages);
+ console.log(messages,33);
}, [])
diff --git a/client/src/components/SendMessage.tsx b/client/src/components/SendMessage.tsx
new file mode 100644
index 0000000..043fa28
--- /dev/null
+++ b/client/src/components/SendMessage.tsx
@@ -0,0 +1,67 @@
+import { Formik, Field, Form } from "formik";
+import { FormikHelpers } from "formik/dist/types";
+import { createMessage } from "../service/message";
+
+type FormValues = {
+ username: string;
+ text: string;
+};
+
+const SendMessage = () => {
+ const initialValues: FormValues = { username: "", text: "" };
+
+ const handleSubmit = async (
+ values: FormValues,
+ actions: FormikHelpers
+ ) => {
+ await createMessage(values)
+ .then(res => console.log(res))
+ .catch(err => console.log(err));
+ };
+
+ return (
+
+
Formi
+
{
+ const errors: { username?: string, text?:string } = {};
+
+ if (!values.username) {
+ errors.username = "Required";
+ }
+
+ if (!values.text) {
+ errors.text = "Required";
+ }
+
+ return errors;
+ }}
+ onSubmit={handleSubmit}>
+ {({ isSubmitting, errors }) => (
+
+ )}
+
+
+ );
+};
+
+export default SendMessage;
diff --git a/client/src/service/message.ts b/client/src/service/message.ts
index 34abf48..d6df902 100644
--- a/client/src/service/message.ts
+++ b/client/src/service/message.ts
@@ -1,8 +1,24 @@
import { Message } from "../types/messageType";
-export const getMessage = async() => {
- const res = await fetch('https://mini-message-board-server.onrender.com/new');
- const data = await res.json();
-
+export const getMessage = async () => {
+ const res = await fetch('https://mini-message-board-server.onrender.com/new');
+ const data = await res.json();
+
return [...data.messages] as Message[]
}
+
+export const createMessage = async (data: Omit) => {
+
+ const res = await fetch('https://mini-message-board-server.onrender.com/new', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(data)
+ });
+ const json = await res.json();
+
+ return json;
+}
+
+
From 5f9ef657e08c2699f32e2ecc0f37f258826c319c Mon Sep 17 00:00:00 2001
From: Guillermo Zevallos
Date: Wed, 4 Jan 2023 18:25:41 -0500
Subject: [PATCH 02/20] Added fonts
---
client/index.html | 1 +
1 file changed, 1 insertion(+)
diff --git a/client/index.html b/client/index.html
index e0d1c84..a38bab6 100644
--- a/client/index.html
+++ b/client/index.html
@@ -5,6 +5,7 @@
Vite + React + TS
+
From ad2b5d83e4091b20c5f74549b5d113edd2a55c4e Mon Sep 17 00:00:00 2001
From: Guillermo Zevallos
Date: Wed, 4 Jan 2023 18:26:35 -0500
Subject: [PATCH 03/20] Added globalstyles
---
client/src/theme/GlobalStyle.tsx | 50 ++++++++++++++++++++++----------
1 file changed, 34 insertions(+), 16 deletions(-)
diff --git a/client/src/theme/GlobalStyle.tsx b/client/src/theme/GlobalStyle.tsx
index b9ee507..04aabb1 100644
--- a/client/src/theme/GlobalStyle.tsx
+++ b/client/src/theme/GlobalStyle.tsx
@@ -1,22 +1,40 @@
import { createGlobalStyle } from "styled-components";
-
+export const palette = ["#FACCAA", "#A1BCE7", "#9ED9B3", "#DAB3B4", "#AAA2C9"];
export const GlobalStyle = createGlobalStyle`
+ :root{
+ --white-bg: #F2F3F7;
+ --white: #FFFFFF;
+ --dark: #00132B;
+ }
- * {
- box-sizing: border-box;
- line-height: normal;
- }
-
- body {
- height: 100vh;
- overflow-x: hidden;
- -webkit-text-size-adjust: 100%;
- -webkit-font-smoothing: antialiased;
- font-family: 'Outfit', sans-serif;
- font-size: 14px;
- line-height: 17.64px;
- }
+ * {
+ padding: 0;
+ margin: 0;
+ box-sizing: border-box;
+ line-height: normal;
+ }
+
+ body {
+ min-height: 100vh;
+ overflow-x: hidden;
+ -webkit-text-size-adjust: 100%;
+ -webkit-font-smoothing: antialiased;
+ font-family: 'Outfit', sans-serif;
+ font-size: 14px;
+ line-height: 17.64px;
+ background-color: var(--dark);
+ font-family: 'Lato', sans-serif;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ h1,h2,h3,h4,h5,h6{
+ /* color: #6F718A; */
+ font-family: 'Syne', sans-serif;
+ }
+
-`;
\ No newline at end of file
+`;
From 07548e47dbf461ddc971eb9fa222787d3b7477eb Mon Sep 17 00:00:00 2001
From: Guillermo Zevallos
Date: Wed, 4 Jan 2023 18:26:52 -0500
Subject: [PATCH 04/20] Created components
---
client/src/components/Button.tsx | 46 +++++++++++++
client/src/components/Card.tsx | 80 ++++++++++++++++++++++
client/src/components/Input.tsx | 74 ++++++++++++++++++++
client/src/components/Loading.tsx | 108 ++++++++++++++++++++++++++++++
4 files changed, 308 insertions(+)
create mode 100644 client/src/components/Button.tsx
create mode 100644 client/src/components/Card.tsx
create mode 100644 client/src/components/Input.tsx
create mode 100644 client/src/components/Loading.tsx
diff --git a/client/src/components/Button.tsx b/client/src/components/Button.tsx
new file mode 100644
index 0000000..91c6639
--- /dev/null
+++ b/client/src/components/Button.tsx
@@ -0,0 +1,46 @@
+import styled from "styled-components";
+
+type ButtonProps = {
+ type: "button" | "submit" | "reset" | undefined;
+ disabled: boolean | undefined;
+};
+
+const Button = ({ type, disabled }: ButtonProps) => {
+ return (
+
+
+
+ );
+};
+
+const StyledButton = styled.button`
+ background: none;
+ border: none;
+ padding: 15px 15px;
+ border-radius: 10px;
+ background-color: black;
+
+ &:hover {
+ background: rgba(116, 77, 77, 0.774);
+ transition: 0.5s;
+ }
+
+ & svg {
+ color: #fff;
+ }
+`;
+
+export default Button;
diff --git a/client/src/components/Card.tsx b/client/src/components/Card.tsx
new file mode 100644
index 0000000..dffcfb3
--- /dev/null
+++ b/client/src/components/Card.tsx
@@ -0,0 +1,80 @@
+import styled from "styled-components";
+
+type Cardprops = {
+ children: JSX.Element | JSX.Element[];
+};
+
+const Card = ({ children }: Cardprops) => {
+ return (
+
+
+ {children}
+
+ );
+};
+
+const StyledCard = styled.div`
+ width: 80vw;
+ height: 90vh;
+ position: relative;
+ margin: 0 auto;
+ background-color: #f8fbfe;
+ border-radius: 8px;
+ z-index: 1;
+
+ .tools {
+ position: absolute;
+ border-radius: 8px 8px 0 0 ;
+ background: white;
+ width: 100%;
+ display: flex;
+ align-items: center;
+ padding: 9px;
+ .circle {
+ padding: 0 4px;
+ }
+
+ .box {
+ display: inline-block;
+ align-items: center;
+ width: 10px;
+ height: 10px;
+ padding: 1px;
+ border-radius: 50%;
+ }
+
+ .red {
+ background-color: #ff605c;
+ }
+
+ .yellow {
+ background-color: #ffbd44;
+ }
+
+ .green {
+ background-color: #00ca4e;
+ }
+ }
+
+ .card__content {
+ padding: 2.5rem 9px 9px;
+ height: 100%;
+ border-radius: 8px;
+ }
+
+ @media (min-width: 900px) {
+ width: 50vw;
+ }
+`;
+
+export default Card;
diff --git a/client/src/components/Input.tsx b/client/src/components/Input.tsx
new file mode 100644
index 0000000..f67d079
--- /dev/null
+++ b/client/src/components/Input.tsx
@@ -0,0 +1,74 @@
+import { Field } from "formik";
+import styled from "styled-components";
+
+type InputProps = {
+ id: string,
+ name: string,
+ placeholder: string
+}
+
+const Input = ({id, name, placeholder}:InputProps) => {
+ return (
+
+
+
+
+ );
+};
+
+const StyledInput = styled.div`
+ --width-of-input: 200px;
+ --border-height: 1px;
+ --border-before-color: rgba(221, 221, 221, 0.39);
+ --border-after-color: #5891ff;
+ --input-hovered-color: #4985e01f;
+ position: relative;
+ width: var(--width-of-input);
+
+ /* styling of Input */
+ .input {
+ color: var(--dark);
+ font-size: 0.9rem;
+ background-color: transparent;
+ width: 100%;
+ box-sizing: border-box;
+ padding-inline: 0.5em;
+ padding-block: 0.7em;
+ border: none;
+ border-bottom: var(--border-height) solid var(--border-before-color);
+ }
+ /* styling of animated border */
+ .input-border {
+ position: absolute;
+ background: var(--border-after-color);
+ width: 0%;
+ height: 2px;
+ bottom: 0;
+ left: 0;
+ transition: 0.3s;
+ }
+ /* Hover on Input */
+ input:hover {
+ background: var(--input-hovered-color);
+ }
+
+ input:focus {
+ outline: none;
+ }
+ /* here is code of animated border */
+ input:focus ~ .input-border {
+ width: 100%;
+ }
+ /* === if you want to do animated border on typing === */
+ /* remove input:focus code and uncomment below code */
+ /* input:valid ~ .input-border{
+ width: 100%;
+} */
+`;
+
+export default Input;
diff --git a/client/src/components/Loading.tsx b/client/src/components/Loading.tsx
new file mode 100644
index 0000000..d6aba9c
--- /dev/null
+++ b/client/src/components/Loading.tsx
@@ -0,0 +1,108 @@
+import React from "react";
+import styled from "styled-components";
+
+
+const Loading = () => {
+ return (
+
+
+
+
+ );
+};
+
+const StyledLoading = styled.div`
+ .jelly {
+ --uib-size: 40px;
+ --uib-speed: 0.8s;
+ --uib-color: #2c2b2b;
+ position: relative;
+ height: calc(var(--uib-size) / 2);
+ width: var(--uib-size);
+ filter: url("#uib-jelly-ooze");
+ animation: rotate72317 calc(var(--uib-speed) * 2) linear infinite;
+ }
+
+ .jelly::before,
+ .jelly::after {
+ content: "";
+ position: absolute;
+ top: 0%;
+ left: 25%;
+ width: 50%;
+ height: 100%;
+ background: var(--uib-color);
+ border-radius: 100%;
+ }
+
+ .jelly::before {
+ animation: shift-left var(--uib-speed) ease infinite;
+ }
+
+ .jelly::after {
+ animation: shift-right var(--uib-speed) ease infinite;
+ }
+
+ .jelly-maker {
+ width: 0;
+ height: 0;
+ position: absolute;
+ }
+
+ @keyframes rotate72317 {
+ 0%,
+ 49.999%,
+ 100% {
+ transform: none;
+ }
+
+ 50%,
+ 99.999% {
+ transform: rotate(90deg);
+ }
+ }
+
+ @keyframes shift-left {
+ 0%,
+ 100% {
+ transform: translateX(0%);
+ }
+
+ 50% {
+ transform: scale(0.65) translateX(-75%);
+ }
+ }
+
+ @keyframes shift-right {
+ 0%,
+ 100% {
+ transform: translateX(0%);
+ }
+
+ 50% {
+ transform: scale(0.65) translateX(75%);
+ }
+ }
+`;
+
+export default Loading;
From 805f2c91800f2aa117f36e3900f7d953ebab2167 Mon Sep 17 00:00:00 2001
From: Guillermo Zevallos
Date: Wed, 4 Jan 2023 18:27:06 -0500
Subject: [PATCH 05/20] Update Board and layout
---
client/src/App.tsx | 65 ++++++++++++++++++----
client/src/components/Board.tsx | 77 ++++++++++++++++++---------
client/src/components/SendMessage.tsx | 28 ++++++----
3 files changed, 125 insertions(+), 45 deletions(-)
diff --git a/client/src/App.tsx b/client/src/App.tsx
index ebb4232..0d63184 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -1,19 +1,62 @@
-import { useState } from 'react'
-import { GlobalStyle } from './theme/GlobalStyle'
-import Board from './components/Board'
-import SendMessage from './components/SendMessage'
+import { useEffect, useState } from "react";
+import { GlobalStyle } from "./theme/GlobalStyle";
+import Board from "./components/Board";
+import SendMessage from "./components/SendMessage";
+import { Message } from "./types/messageType";
+import { getMessage } from "./service/message";
+import Loading from "./components/Loading";
+import Card from "./components/Card";
+import styled from "styled-components";
function App() {
+ const [messages, setMessages] = useState();
+ const [loading, setLoading] = useState(false);
+
+ useEffect(() => {
+ setLoading(true);
+ getMessage().then(data => {
+ setMessages(data);
+ setLoading(false);
+ });
+ }, []);
return (
-
- Mini Message Board
-
-
-
+
+
+
+ Mini Message Board
+ {!loading ? (
+
+ ) : (
+
+ )}
+
+
+
- )
+ );
}
-export default App
+const StyledConent = styled.main`
+ height: 100%;
+
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+
+ .main-content{
+ flex: 1;
+ }
+ .loading{
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+`;
+
+export default App;
diff --git a/client/src/components/Board.tsx b/client/src/components/Board.tsx
index eb297f7..d7e9fe2 100644
--- a/client/src/components/Board.tsx
+++ b/client/src/components/Board.tsx
@@ -1,30 +1,59 @@
-import { useEffect, useState } from 'react';
-import { getMessage } from "../service/message"
-import { Message } from '../types/messageType';
+import { useEffect, useState } from "react";
+import styled from "styled-components";
+import { getMessage } from "../service/message";
+import { Message } from "../types/messageType";
-const Board = () => {
- const [messages, setMessages] = useState()
-
- useEffect(() => {
- getMessage().then((data)=>setMessages(data))
- console.log(messages,33);
-
- }, [])
-
+type BoardProps = {
+ messages: Message[] | undefined;
+};
+const Board = ({ messages }: BoardProps) => {
return (
-
-
Messages
+
+ Messages
+ {!!messages && (
- {messages?.map(message => (
-
-
{message.username}
-
{message.text}
-
- ))}
+ {messages.map(message => (
+
+
{message.username}
+
{message.text}
+
+ ))}
-
- )
-}
+ )}
+
+ );
+};
+
+const StyledBoard = styled.main`
+ .messages {
+ }
+ .messages-animate {
+ -webkit-animation: slide-top 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
+ animation: slide-top 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
+ }
+ @-webkit-keyframes slide-top {
+ 0% {
+ -webkit-transform: translateY(100px);
+ transform: translateY(100px);
+ }
+ 100% {
+ -webkit-transform: translateY(0px);
+ transform: translateY(0px);
+ }
+ }
+ @keyframes slide-top {
+ 0% {
+ -webkit-transform: translateY(100px);
+ transform: translateY(100px);
+ }
+ 100% {
+ -webkit-transform: translateY(0px);
+ transform: translateY(0px);
+ }
+ }
+`;
-export default Board
\ No newline at end of file
+export default Board;
diff --git a/client/src/components/SendMessage.tsx b/client/src/components/SendMessage.tsx
index 043fa28..0970994 100644
--- a/client/src/components/SendMessage.tsx
+++ b/client/src/components/SendMessage.tsx
@@ -1,13 +1,20 @@
import { Formik, Field, Form } from "formik";
import { FormikHelpers } from "formik/dist/types";
import { createMessage } from "../service/message";
+import { Message } from "../types/messageType";
+import Button from "./Button";
+import Input from "./Input";
type FormValues = {
username: string;
text: string;
};
-const SendMessage = () => {
+type SendMessageProps = {
+ setMessages: React.Dispatch>;
+};
+
+const SendMessage = ({ setMessages }: SendMessageProps) => {
const initialValues: FormValues = { username: "", text: "" };
const handleSubmit = async (
@@ -15,17 +22,19 @@ const SendMessage = () => {
actions: FormikHelpers
) => {
await createMessage(values)
- .then(res => console.log(res))
+ .then(res => {
+ console.log(res);
+ setMessages(m => [...m!, res.messageDB]);
+ })
.catch(err => console.log(err));
};
return (
-
Formi
{
- const errors: { username?: string, text?:string } = {};
+ const errors: { username?: string; text?: string } = {};
if (!values.username) {
errors.username = "Required";
@@ -40,23 +49,22 @@ const SendMessage = () => {
onSubmit={handleSubmit}>
{({ isSubmitting, errors }) => (
)}
From 9f791e6bebb6e0190b6c6ccc4d6e2573705e0f5a Mon Sep 17 00:00:00 2001
From: Guillermo Zevallos
Date: Thu, 5 Jan 2023 12:22:30 -0500
Subject: [PATCH 06/20] Added patterns and colors to messages
---
client/src/App.tsx | 6 +++++-
client/src/components/Board.tsx | 6 +++++-
client/src/components/Button.tsx | 12 +++---------
client/src/components/Card.tsx | 7 ++++---
client/src/components/SvgPattern.tsx | 26 ++++++++++++++++++++++++++
client/src/theme/GlobalStyle.tsx | 13 ++++++++++++-
client/src/utils/getColor.ts | 6 ++++++
7 files changed, 61 insertions(+), 15 deletions(-)
create mode 100644 client/src/components/SvgPattern.tsx
create mode 100644 client/src/utils/getColor.ts
diff --git a/client/src/App.tsx b/client/src/App.tsx
index 0d63184..3a00af7 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -7,6 +7,7 @@ import { getMessage } from "./service/message";
import Loading from "./components/Loading";
import Card from "./components/Card";
import styled from "styled-components";
+import SvgPattern from "./components/SvgPattern";
function App() {
const [messages, setMessages] = useState();
@@ -23,6 +24,8 @@ function App() {
return (
+
+
Mini Message Board
@@ -44,11 +47,12 @@ function App() {
const StyledConent = styled.main`
height: 100%;
-
display: flex;
flex-direction: column;
justify-content: space-between;
+
+
.main-content{
flex: 1;
}
diff --git a/client/src/components/Board.tsx b/client/src/components/Board.tsx
index d7e9fe2..85c743a 100644
--- a/client/src/components/Board.tsx
+++ b/client/src/components/Board.tsx
@@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
import styled from "styled-components";
import { getMessage } from "../service/message";
import { Message } from "../types/messageType";
+import { getRandomColor } from "../utils/getColor";
type BoardProps = {
messages: Message[] | undefined;
@@ -16,7 +17,10 @@ const Board = ({ messages }: BoardProps) => {
{messages.map(message => (
+ className="messages messages-animate"
+ style={{
+ backgroundColor: getRandomColor()
+ }}>
{message.username}
{message.text}
diff --git a/client/src/components/Button.tsx b/client/src/components/Button.tsx
index 91c6639..5968b69 100644
--- a/client/src/components/Button.tsx
+++ b/client/src/components/Button.tsx
@@ -13,14 +13,8 @@ const Button = ({ type, disabled }: ButtonProps) => {
);
@@ -39,7 +33,7 @@ const StyledButton = styled.button`
}
& svg {
- color: #fff;
+ fill: #fff;
}
`;
diff --git a/client/src/components/Card.tsx b/client/src/components/Card.tsx
index dffcfb3..cfa450b 100644
--- a/client/src/components/Card.tsx
+++ b/client/src/components/Card.tsx
@@ -28,14 +28,15 @@ const StyledCard = styled.div`
height: 90vh;
position: relative;
margin: 0 auto;
- background-color: #f8fbfe;
+ background-color: var(--white);
border-radius: 8px;
z-index: 1;
+ box-shadow: rgba(50, 50, 93, 0.25) 0px 6px 12px -2px, rgba(0, 0, 0, 0.3) 0px 3px 7px -3px;
.tools {
position: absolute;
- border-radius: 8px 8px 0 0 ;
- background: white;
+ border-radius: 8px 8px 0 0;
+ background: #2F283B;
width: 100%;
display: flex;
align-items: center;
diff --git a/client/src/components/SvgPattern.tsx b/client/src/components/SvgPattern.tsx
new file mode 100644
index 0000000..464b463
--- /dev/null
+++ b/client/src/components/SvgPattern.tsx
@@ -0,0 +1,26 @@
+type SvgPatternProps = {
+ className?: string;
+};
+
+const SvgPattern = ({ className = "" }: SvgPatternProps) => {
+ return (
+
+ );
+};
+
+export default SvgPattern;
diff --git a/client/src/theme/GlobalStyle.tsx b/client/src/theme/GlobalStyle.tsx
index 04aabb1..99b7c66 100644
--- a/client/src/theme/GlobalStyle.tsx
+++ b/client/src/theme/GlobalStyle.tsx
@@ -24,11 +24,12 @@ export const GlobalStyle = createGlobalStyle`
font-family: 'Outfit', sans-serif;
font-size: 14px;
line-height: 17.64px;
- background-color: var(--dark);
+ background-color: var(--white-bg);
font-family: 'Lato', sans-serif;
display: flex;
align-items: center;
justify-content: center;
+ position: relative;
}
h1,h2,h3,h4,h5,h6{
@@ -36,5 +37,15 @@ export const GlobalStyle = createGlobalStyle`
font-family: 'Syne', sans-serif;
}
+ .pattern{
+ position: absolute;
+ bottom: 5rem;
+ right: -6rem;
+ }
+ .pattern-2{
+ position: absolute;
+ top: -5rem;
+ left: -5rem;
+ }
`;
diff --git a/client/src/utils/getColor.ts b/client/src/utils/getColor.ts
new file mode 100644
index 0000000..1bf3d1d
--- /dev/null
+++ b/client/src/utils/getColor.ts
@@ -0,0 +1,6 @@
+import { palette } from "../theme/GlobalStyle"
+
+export const getRandomColor = () => {
+ const randomIndex = Math.floor(Math.random() * palette.length);
+ return palette[randomIndex];
+}
\ No newline at end of file
From 83bfed72598529e4474df6ce42ce2b19b0e25f8e Mon Sep 17 00:00:00 2001
From: Guillermo Zevallos
Date: Thu, 5 Jan 2023 21:24:56 -0500
Subject: [PATCH 07/20] Refactor styles
---
client/index.html | 1 +
client/src/App.tsx | 5 +-
client/src/components/Board.tsx | 69 +++++++++++++++++++++------
client/src/components/Button.tsx | 27 ++++++++---
client/src/components/Card.tsx | 32 +++++++++++--
client/src/components/Input.tsx | 42 ++++++++++------
client/src/components/SendMessage.tsx | 42 ++++++++++------
client/src/theme/GlobalStyle.tsx | 22 ++++++++-
8 files changed, 185 insertions(+), 55 deletions(-)
diff --git a/client/index.html b/client/index.html
index a38bab6..b1b706a 100644
--- a/client/index.html
+++ b/client/index.html
@@ -6,6 +6,7 @@
Vite + React + TS
+
diff --git a/client/src/App.tsx b/client/src/App.tsx
index 3a00af7..8e10793 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -51,10 +51,13 @@ const StyledConent = styled.main`
flex-direction: column;
justify-content: space-between;
-
+ h1{
+ padding: 1rem 0;
+ }
.main-content{
flex: 1;
+ overflow: auto;
}
.loading{
display: flex;
diff --git a/client/src/components/Board.tsx b/client/src/components/Board.tsx
index 85c743a..69f0279 100644
--- a/client/src/components/Board.tsx
+++ b/client/src/components/Board.tsx
@@ -9,30 +9,69 @@ type BoardProps = {
};
const Board = ({ messages }: BoardProps) => {
+ const showMessages = messages?.map(message => {
+ let color = getRandomColor();
+ return (
+
+
{message.username}
+
+ {message.text}
+
+
+ );
+ });
+
return (
- Messages
{!!messages && (
-
- {messages.map(message => (
-
-
{message.username}
-
{message.text}
-
- ))}
-
+
)}
);
};
const StyledBoard = styled.main`
- .messages {
+ .messages-section {
+ padding: 0.5rem 1rem;
+ overflow: hidden;
+ }
+ h3{
+ padding-bottom:.5rem ;
+ }
+ .message {
+ color: #363f4d;
+ padding: 18px 20px;
+ line-height: 26px;
+ font-size: 16px;
+ border-radius: 7px;
+ margin-bottom: 30px;
+ width: fit-content;
+ max-width: 100%;
+ position: relative;
+ white-space: initial;
+ border: none;
+
+ &::after {
+ content: "";
+ bottom: 98%;
+ left: .7rem;
+ border: solid transparent;
+ content: " ";
+ height: 0;
+ width: 0;
+ position: absolute;
+ pointer-events: none;
+ border-bottom-color: inherit;
+ border-width: 7px;
+ margin-left: -7px;
+ }
}
.messages-animate {
-webkit-animation: slide-top 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
diff --git a/client/src/components/Button.tsx b/client/src/components/Button.tsx
index 5968b69..d254c2b 100644
--- a/client/src/components/Button.tsx
+++ b/client/src/components/Button.tsx
@@ -23,17 +23,32 @@ const Button = ({ type, disabled }: ButtonProps) => {
const StyledButton = styled.button`
background: none;
border: none;
- padding: 15px 15px;
+ padding: 20px 20px;
border-radius: 10px;
- background-color: black;
+ background-color: var(--white);
+ cursor: pointer;
+ transition: 0.5s all ease-out;
&:hover {
- background: rgba(116, 77, 77, 0.774);
- transition: 0.5s;
+ background: #ffffff;
+ box-shadow: 10px 10px 20px #ededed, -10px -10px 20px #ffffff;
}
-
& svg {
- fill: #fff;
+ fill: var(--dark);
+ }
+
+ &:hover svg {
+ fill: #504464;
+ }
+
+ &:disabled {
+ background: linear-gradient(145deg, #f0efef, #ffffff);
+ box-shadow: 6px 6px 12px #f3f3f3, -6px -6px 12px #f1eded;
+ }
+
+ &:disabled svg {
+ cursor: wait;
+ fill: #cac9c9;
}
`;
diff --git a/client/src/components/Card.tsx b/client/src/components/Card.tsx
index cfa450b..1ea15a0 100644
--- a/client/src/components/Card.tsx
+++ b/client/src/components/Card.tsx
@@ -18,25 +18,39 @@ const Card = ({ children }: Cardprops) => {
+
{children}
);
};
const StyledCard = styled.div`
- width: 80vw;
+ width: 95vw;
height: 90vh;
position: relative;
margin: 0 auto;
background-color: var(--white);
border-radius: 8px;
z-index: 1;
- box-shadow: rgba(50, 50, 93, 0.25) 0px 6px 12px -2px, rgba(0, 0, 0, 0.3) 0px 3px 7px -3px;
+ box-shadow: rgba(50, 50, 93, 0.25) 0px 6px 12px -2px,
+ rgba(0, 0, 0, 0.3) 0px 3px 7px -3px;
.tools {
position: absolute;
border-radius: 8px 8px 0 0;
- background: #2F283B;
+ background: #2f283b;
width: 100%;
display: flex;
align-items: center;
@@ -66,6 +80,18 @@ const StyledCard = styled.div`
background-color: #00ca4e;
}
}
+ .attribution {
+ position: absolute;
+ top: 9px;
+ left: 50%;
+ transform: translateX(-50%);
+ color: #aaa2c9;
+ a {
+ padding-left: .2rem;
+ text-decoration: none;
+ color: #dab3b4;
+ }
+ }
.card__content {
padding: 2.5rem 9px 9px;
diff --git a/client/src/components/Input.tsx b/client/src/components/Input.tsx
index f67d079..b45d36b 100644
--- a/client/src/components/Input.tsx
+++ b/client/src/components/Input.tsx
@@ -2,16 +2,17 @@ import { Field } from "formik";
import styled from "styled-components";
type InputProps = {
- id: string,
- name: string,
- placeholder: string
-}
+ id: string;
+ name: string;
+ placeholder: string;
+ className?: string;
+};
-const Input = ({id, name, placeholder}:InputProps) => {
+const Input = ({ id, name, placeholder, className="" }: InputProps) => {
return (
-
+
{
};
const StyledInput = styled.div`
- --width-of-input: 200px;
+ --width-of-input: 100%;
--border-height: 1px;
--border-before-color: rgba(221, 221, 221, 0.39);
- --border-after-color: #5891ff;
- --input-hovered-color: #4985e01f;
+ --border-after-color: #434b5c;
+ --input-hovered-color: #6062641f;
position: relative;
width: var(--width-of-input);
+ &.error::after{
+ content: 'Required';
+ position: absolute;
+ right: .5rem;
+ top: 50%;
+ transform: translateY(-50%);
+ color: salmon;
+ opacity: .7;
+ }
+
/* styling of Input */
.input {
color: var(--dark);
@@ -42,6 +53,12 @@ const StyledInput = styled.div`
border: none;
border-bottom: var(--border-height) solid var(--border-before-color);
}
+ .input.error{
+ border-bottom: var(--border-height) solid salmon;
+ }
+ .input.error::after{
+ content: 'd';
+ }
/* styling of animated border */
.input-border {
position: absolute;
@@ -64,11 +81,6 @@ const StyledInput = styled.div`
input:focus ~ .input-border {
width: 100%;
}
- /* === if you want to do animated border on typing === */
- /* remove input:focus code and uncomment below code */
- /* input:valid ~ .input-border{
- width: 100%;
-} */
`;
export default Input;
diff --git a/client/src/components/SendMessage.tsx b/client/src/components/SendMessage.tsx
index 0970994..7e0bb79 100644
--- a/client/src/components/SendMessage.tsx
+++ b/client/src/components/SendMessage.tsx
@@ -1,5 +1,6 @@
import { Formik, Field, Form } from "formik";
import { FormikHelpers } from "formik/dist/types";
+import styled from "styled-components";
import { createMessage } from "../service/message";
import { Message } from "../types/messageType";
import Button from "./Button";
@@ -30,7 +31,7 @@ const SendMessage = ({ setMessages }: SendMessageProps) => {
};
return (
-
+
{
@@ -49,18 +50,21 @@ const SendMessage = ({ setMessages }: SendMessageProps) => {
onSubmit={handleSubmit}>
{({ isSubmitting, errors }) => (
)}
-
+
);
};
+const StyledSendMessage = styled.div`
+ padding: 1rem 0;
+ form{
+ display: flex;
+ }
+ .inputs{
+ flex: 1;
+ }
+`;
+
export default SendMessage;
diff --git a/client/src/theme/GlobalStyle.tsx b/client/src/theme/GlobalStyle.tsx
index 99b7c66..39859dc 100644
--- a/client/src/theme/GlobalStyle.tsx
+++ b/client/src/theme/GlobalStyle.tsx
@@ -6,7 +6,27 @@ export const GlobalStyle = createGlobalStyle`
:root{
--white-bg: #F2F3F7;
--white: #FFFFFF;
- --dark: #00132B;
+ --dark: #2F283B;
+ --thumb-bg: #d2d2e0;
+ }
+
+ ::-webkit-scrollbar {
+ width: 5px;
+ height: 5px;
+ }
+
+ ::-webkit-scrollbar-thumb {
+ background: var(--thumb-bg);
+ border-radius: var(--radius);
+ }
+
+ ::-webkit-scrollbar-thumb:active,
+ ::-webkit-scrollbar-thumb:hover {
+ background: var(--dark);
+ }
+
+ ::-webkit-scrollbar-track {
+ background: #80808014;
}
* {
From 1a2267e5f7bdafcf43c607e20ff56e02a66d8d66 Mon Sep 17 00:00:00 2001
From: Guillermo Zevallos
Date: Thu, 5 Jan 2023 21:35:36 -0500
Subject: [PATCH 08/20] Add variables
---
client/.env.example | 1 +
client/.gitignore | 2 +-
client/src/service/message.ts | 4 ++--
3 files changed, 4 insertions(+), 3 deletions(-)
create mode 100644 client/.env.example
diff --git a/client/.env.example b/client/.env.example
new file mode 100644
index 0000000..ce41a35
--- /dev/null
+++ b/client/.env.example
@@ -0,0 +1 @@
+VITE_SERVER=...
\ No newline at end of file
diff --git a/client/.gitignore b/client/.gitignore
index 7a9d1bc..b7adbe3 100644
--- a/client/.gitignore
+++ b/client/.gitignore
@@ -11,7 +11,7 @@ node_modules
dist
dist-ssr
*.local
-
+.env
# Editor directories and files
.vscode/*
!.vscode/extensions.json
diff --git a/client/src/service/message.ts b/client/src/service/message.ts
index d6df902..9203d9c 100644
--- a/client/src/service/message.ts
+++ b/client/src/service/message.ts
@@ -1,7 +1,7 @@
import { Message } from "../types/messageType";
export const getMessage = async () => {
- const res = await fetch('https://mini-message-board-server.onrender.com/new');
+ const res = await fetch(import.meta.env.VITE_SERVER);
const data = await res.json();
return [...data.messages] as Message[]
@@ -9,7 +9,7 @@ export const getMessage = async () => {
export const createMessage = async (data: Omit) => {
- const res = await fetch('https://mini-message-board-server.onrender.com/new', {
+ const res = await fetch(import.meta.env.VITE_SERVER, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
From 68f521ae844e7a1179842a209b0403085fe4cd7c Mon Sep 17 00:00:00 2001
From: Guillermo Zevallos
Date: Thu, 5 Jan 2023 22:04:22 -0500
Subject: [PATCH 09/20] added date to messages
---
client/src/components/Board.tsx | 31 ++++++++++++++++++++++++-------
1 file changed, 24 insertions(+), 7 deletions(-)
diff --git a/client/src/components/Board.tsx b/client/src/components/Board.tsx
index 69f0279..bca8b72 100644
--- a/client/src/components/Board.tsx
+++ b/client/src/components/Board.tsx
@@ -1,9 +1,6 @@
-import { useEffect, useState } from "react";
import styled from "styled-components";
-import { getMessage } from "../service/message";
import { Message } from "../types/messageType";
import { getRandomColor } from "../utils/getColor";
-
type BoardProps = {
messages: Message[] | undefined;
};
@@ -15,7 +12,18 @@ const Board = ({ messages }: BoardProps) => {
-
{message.username}
+
+
{message.username}
+
+ {new Date(message.added).toLocaleDateString("en-US", {
+ year: "numeric",
+ month: "2-digit",
+ day: "2-digit",
+ hour: "2-digit",
+ minute: "2-digit",
+ })}
+
+
Date: Thu, 5 Jan 2023 22:10:36 -0500
Subject: [PATCH 10/20] Update README.md
---
README.md | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/README.md b/README.md
index e69de29..3a9850d 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1,34 @@
+
+# Mini Message Board
+
+A mini message board is a simple web application that allows users to leave messages on a public board. Users can post messages and view a list of all messages that have been posted.
+
+# Demo
+
+[Live Demo](https://mini-message-frontend.vercel.app/) 😎
+## Environment Variables
+
+To run this project, you will need to add the following environment variables to your .env file
+
+**Frontend**
+
+` VITE_SERVER`
+
+**Backend**
+
+`PORT`
+
+`MONGODB_CNN`
+
+
+## Screenshots
+
+
+
+
+## Tech Stack
+
+**Client:** React, Typescript, Styled Components
+
+**Server:** Node, Express, MongoDB
+
From 9a2383f6caba28a3049232ebc8ffcea0cc55ca8a Mon Sep 17 00:00:00 2001
From: Guillermo Zevallos
Date: Thu, 5 Jan 2023 22:12:43 -0500
Subject: [PATCH 11/20] Screenshot upload
---
screenshot.png | Bin 0 -> 34740 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 screenshot.png
diff --git a/screenshot.png b/screenshot.png
new file mode 100644
index 0000000000000000000000000000000000000000..bba1f9ef132c58df76bb2e043996e120f2ca6445
GIT binary patch
literal 34740
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