Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
ecf16e4
started with the project
violacathrine Apr 30, 2025
1b2044f
styling
violacathrine May 1, 2025
e8be3a1
clean, styling etc
violacathrine May 5, 2025
24d1bb5
loader
violacathrine May 10, 2025
e24cb22
hearticons, styling etc
violacathrine May 10, 2025
487ac1e
env fil
violacathrine May 10, 2025
da8252e
final touches
violacathrine May 10, 2025
ee8c95c
favicon, logo, footer
violacathrine May 11, 2025
b30f561
color on isliked
violacathrine May 11, 2025
210378d
readme file
violacathrine May 11, 2025
8ddf21e
ready for handin
violacathrine May 11, 2025
599091a
last push
violacathrine May 11, 2025
84f5d57
ogtags
violacathrine May 11, 2025
e15e26e
finally done
violacathrine May 11, 2025
025540c
added attribute to icon
violacathrine May 17, 2025
a042b31
changed the apiurl to new one
violacathrine May 26, 2025
3651f3c
url
violacathrine May 28, 2025
1fafb7a
testing
violacathrine May 28, 2025
01ffee3
test
violacathrine May 28, 2025
1aff31e
test again
violacathrine May 28, 2025
546715c
changed color
violacathrine May 28, 2025
3a76df6
test
violacathrine May 28, 2025
f2d7b9a
color on timestamp
violacathrine May 28, 2025
ab9d9e9
color change
violacathrine May 28, 2025
380a437
test again
violacathrine May 28, 2025
ce2e7c9
test
violacathrine May 28, 2025
6cdf0af
new api
violacathrine Jun 11, 2025
76a7a93
testing api
violacathrine Jun 12, 2025
e5a68a7
api up n running
violacathrine Jun 12, 2025
1bdbe50
sorting messages
violacathrine Jun 12, 2025
e4ca486
wake up fetch function
violacathrine Jun 12, 2025
bdf222b
api-url
violacathrine Aug 6, 2025
631620e
test
violacathrine Aug 6, 2025
4345013
test
violacathrine Aug 7, 2025
576ed2c
styling, ready for deploy
violacathrine Sep 2, 2025
829397c
readme
violacathrine Sep 2, 2025
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ package-lock.json
*.njsproj
*.sln
*.sw?
.env
.env
50 changes: 49 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,49 @@
# Happy Thoughts
# Happy Thoughts Frontend

React app for sharing happy thoughts. Users can post thoughts, like others' posts, and manage their own content with authentication.

## Live Demo

👉 [View the live site here](https://happythoughtsbyc.netlify.app/)

## Features

- Post thoughts (anonymous or logged in)
- Like and unlike thoughts
- User authentication (register/login)
- Edit and delete your own thoughts
- Character counter (5-140 chars)
- Responsive design with styled components

## Tech Stack

- React with hooks
- Styled Components
- Vite
- localStorage for auth tokens
- Custom Happy Thoughts API

## Installation

1. Clone and install:
```bash
npm install
```

2. Create `.env` file:
```
VITE_API_URL=http://localhost:8080
```

3. Start development server:
```bash
npm run dev
```

## Deployment

Deployed on Netlify, connected to custom backend API.

---

Created with ❤️ by **Cathi**
25 changes: 19 additions & 6 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,28 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="./vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/svg+xml" href="./happy-thoughts.png" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap"
rel="stylesheet"
/>
<title>Happy Thoughts</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta property="og:title" content="Happy Thoughts by Cathi" />
<meta
property="og:description"
content="A happy place for happy thoughts! 💬✨"
/>
<meta
property="og:image"
content="https://happythoughtsbyc.netlify.app/thumbnail-ht.png"
/>
<meta property="og:type" content="website" />
</head>
<body>
<div id="root"></div>
<script
type="module"
src="./src/main.jsx">
</script>
<script type="module" src="./src/main.jsx"></script>
</body>
</html>
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
},
"dependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0"
"react-dom": "^19.0.0",
"react-icons": "^5.5.0",
"styled-components": "^6.1.17"
},
"devDependencies": {
"@eslint/js": "^9.21.0",
Expand Down
Binary file added public/happy-thoughts.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/thumbnail-ht.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion public/vite.svg

This file was deleted.

3 changes: 2 additions & 1 deletion pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
Please include your Netlify link here.
Please include your Netlify link here.
https://happythoughtsbyc.netlify.app/
151 changes: 148 additions & 3 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,150 @@
// src/App.jsx
import React, { useContext, useState } from "react";
import { AuthContext } from "./context/AuthContext";
import { useThoughts } from "./hooks/useThoughts";
import { RegisterForm } from "./components/RegisterForm";
import { LoginForm } from "./components/LoginForm";
import { EditThought } from "./components/EditThought";
import { Form } from "./components/Form";
import { MessageList } from "./components/MessageList";
import { GlobalStyles } from "./GlobalStyles";
import { Logo } from "./components/Logo";
import { Footer } from "./components/Footer";
import { Loader } from "./components/Loader";
import {
AppContainer,
PageTitle,
WelcomeSection,
WelcomeTitle,
WelcomeText,
LoginButton,
UserSection,
UserInfo,
UserEmail,
LogoutButton,
AuthFormSection,
AuthFormBox,
AuthFormTitle,
ToggleText,
ToggleLink
} from "./App.styles";

export const App = () => {
const { user, logout } = useContext(AuthContext);
const {
messages,
loading,
posting,
likeCount,
addMessage,
toggleLike,
removeMessage,
saveMessage,
} = useThoughts();
const [editingId, setEditingId] = useState(null);
const [showAuthForms, setShowAuthForms] = useState(false);
const [authMode, setAuthMode] = useState("login"); // "login" or "register"

return (
<h1>Happy Thoughts</h1>
)
}
<>
<GlobalStyles />
<Logo />
<PageTitle>Happy Thoughts</PageTitle>

<AppContainer>
{/* Welcome text - always visible */}
<WelcomeSection>
<WelcomeTitle>
Welcome to Happy Thoughts!
</WelcomeTitle>
{!user && (
<>
<WelcomeText>
Share your happy thoughts for the day and enjoy reading what makes others happy.
If you want to edit or delete your thoughts, you can login below.
</WelcomeText>
<LoginButton onClick={() => setShowAuthForms(!showAuthForms)}>
{showAuthForms ? "Close" : "Login"}
</LoginButton>
</>
)}
</WelcomeSection>

{/* Logout button for logged in users */}
{user && (
<UserSection>
<UserInfo>
<UserEmail>{user.email}</UserEmail>
<LogoutButton onClick={logout}>
Logout
</LogoutButton>
</UserInfo>
</UserSection>
)}

{/* Auth forms - only show when toggled */}
{!user && showAuthForms && (
<AuthFormSection>
<AuthFormBox>
{authMode === "login" ? (
<>
<AuthFormTitle>Login</AuthFormTitle>
<LoginForm onSuccess={() => {
setShowAuthForms(false);
setAuthMode("login");
}} />
<ToggleText>
Don't have an account?{" "}
<ToggleLink onClick={() => setAuthMode("register")}>
Sign up here
</ToggleLink>
</ToggleText>
</>
) : (
<>
<AuthFormTitle>Register</AuthFormTitle>
<RegisterForm onSuccess={() => {
setShowAuthForms(false);
setAuthMode("login");
}} />
<ToggleText>
Already have an account?{" "}
<ToggleLink onClick={() => setAuthMode("login")}>
Login here
</ToggleLink>
</ToggleText>
</>
)}
</AuthFormBox>
</AuthFormSection>
)}

{/* Form is always visible for everyone */}
<Form onSubmitMessage={addMessage} posting={posting} />
{!loading && posting && <Loader />}

{editingId && user ? (
<EditThought
thought={messages.find((m) => m._id === editingId)}
onSave={(id, fields) => {
saveMessage(id, fields);
setEditingId(null);
}}
onCancel={() => setEditingId(null)}
/>
) : (
<MessageList
messages={messages}
loading={loading}
onLike={toggleLike} // Everyone can like
onDelete={user ? removeMessage : null} // Only logged in can delete
onEdit={user ? (id) => setEditingId(id) : null} // Only logged in can edit
currentUserId={user?.id}
/>
)}
</AppContainer>

<Footer likeCount={likeCount} />
</>
);
};
125 changes: 125 additions & 0 deletions src/App.styles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import styled from "styled-components";

export const AppContainer = styled.div`
width: 100%;
max-width: 500px;
margin: 0 auto;
`;

export const PageTitle = styled.h1`
text-align: center;
margin: 20px 0;
color: #333333;
`;

export const WelcomeSection = styled.div`
text-align: center;
margin-bottom: 30px;
padding: 0 20px;
`;

export const WelcomeTitle = styled.h2`
font-size: 18px;
font-weight: normal;
color: #333;
line-height: 1.5;
font-family: inherit;
`;

export const WelcomeText = styled.p`
font-size: 14px;
color: #666;
line-height: 1.6;
margin-top: 10px;
font-family: inherit;
`;

export const LoginButton = styled.button`
margin-top: 15px;
background: transparent;
border: 1px solid #333;
border-radius: 20px;
padding: 6px 16px;
cursor: pointer;
font-size: 14px;
font-family: inherit;
color: #333;
transition: all 0.2s ease;

&:hover {
background: #f5f5f5;
}
`;

export const UserSection = styled.div`
display: flex;
justify-content: flex-end;
margin-bottom: 16px;
`;

export const UserInfo = styled.div`
display: flex;
align-items: center;
gap: 10px;
`;

export const UserEmail = styled.span`
font-size: 14px;
color: #666;
`;

export const LogoutButton = styled.button`
background: transparent;
border: 1px solid #333;
border-radius: 20px;
padding: 6px 16px;
cursor: pointer;
font-size: 14px;
font-family: inherit;
color: #333;
transition: all 0.2s ease;

&:hover {
background: #f5f5f5;
}
`;

export const AuthFormSection = styled.section`
max-width: 450px;
margin: 5px auto 30px auto;
`;

export const AuthFormBox = styled.div`
padding: 20px;
border: 1px solid #333;
box-shadow: 7px 7px 0px rgba(0, 0, 0, 1);
background: white;
box-sizing: border-box;
width: 100%;
`;

export const AuthFormTitle = styled.h2`
margin-bottom: 15px;
`;

export const ToggleText = styled.p`
text-align: center;
margin-top: 20px;
font-size: 14px;
color: #666;
`;

export const ToggleLink = styled.button`
background: none;
border: none;
color: #333;
text-decoration: underline;
cursor: pointer;
font-size: 14px;
font-family: inherit;
padding: 0;

&:hover {
color: #000;
}
`;
Loading