Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
VladBrok committed Sep 4, 2022
2 parents 560aa45 + 82f98e0 commit fd5112c
Show file tree
Hide file tree
Showing 36 changed files with 230 additions and 62 deletions.
143 changes: 143 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Payback — a fully-featured e-commerce store

![screenshot of the homepage](/public/images/self/home.png)

## Table of contents

- [Description](#description)
- [Features](#features)
- [Messenger](#messenger)
- [Buying products](#buying-products)
- [Selling products](#selling-products)
- [Leaving reviews](#leaving-reviews)
- [Authentication](#authentication)
- [License](#license)

---

## Description

Payback is an e-commerce site where people can [buy](#buying-products) and [sell](#selling-products) products. Users can communicate with each other using build-in [messenger](#messenger). To understand how reliable a seller is, users can view all [reviews](#leaving-reviews) about that particular seller (a review can be left only after the product has been purchased).
Initially, access to the site is read-only. Users must [authenticate](#authentication) in order to perform any actions, such as buying or selling products.

> _**Disclaimer**: Payback is a pet project. All users and products presented on the site are not real, their photos, descriptions, etc. are obtained from public APIs.
> The payment system is working in test mode._
---

## Features

### Messenger

You can communicate with other users directly on the site.

#### To create a chat with someone:

1. [Sign in](#to-sign-in)
2. Go to the page of the user with whom you want to create a chat. Let's say it's [user #11]
![screenshot of the user page](/public/images/self/user.png)
3. Click "Write a message"

If everything goes well, you will be redirected to the chat page
![screenshot of the chat page](/public/images/self/chat.png)
After that, you can type a message in the text field and click on the blue arrow to send it.
![screenshot of the chat with a message](/public/images/self/chat-with-message.png)
If your interlocutor writes a message, you will see it in real time.
![screenshot of the chat with two messages](/public/images/self/chat-with-two-messages.png)
A list of all chats is available on the [chats page].
![screenshot of the chats page](/public/images/self/chats.png)

---

### Buying products

The payment system works in test mode, so the real payment won't be made. You can buy a product just for proof of concept.

#### To buy a product:

1. [Sign in](#to-sign-in)
2. Go to the page of the product that you want to buy. Let's say it's [product #35]
![screenshot of the product page](/public/images/self/product.png)
3. Click "Buy" and follow the instructions provided above the "Buy" button to make a test payment

If everything goes well, you will be prompted to [leave a review](#to-leave-a-review). From now on, this product is sold. The balance of the user who put this product up for sale will be increased by the price of the product minus service charges.
![screenshot of a sold product](/public/images/self/sold-product.png)

---

### Selling products

You can put your own product up for sale so other users can buy it.

#### To put your product up for sale:

1. [Sign in](#to-sign-in)
2. Go to [sell page]
3. Specify product information:
1. Select a category by clicking on it
![screenshot of categories](/public/images/self/sell-step-categories.png)
2. Upload a photo of your product by clicking "Upload". After the photo uploads, a "Continue" button appears. Click it to proceed.
![screenshot of upload photo](/public/images/self/sell-step-photo.png)
3. Specify a title (optional), then click "Continue"
![screenshot of title](/public/images/self/sell-step-title.png)
4. Specify a description (optional), then click "Continue"
![screenshot of description](/public/images/self/sell-step-description.png)
5. Specify a price, then click "Continue"
![screenshot of price](/public/images/self/sell-step-price.png)
6. Select a product status by clicking on the appropriate button. If you want premium status, please refer to the [buying products](#buying-products) section for more information about how to make a payment
![screenshot of product statuses](/public/images/self/sell-step-status.png)

If everything goes well, you will be redirected to the [profile page] and your product will appear here. If your product has a premium status, it will also appear on the [home page].
![screenshot of the uploaded product](/public/images/self/uploaded-product.png)

---

### Leaving reviews

After buying a product, you can leave a review about it and about the seller.

#### To leave a review:

1. [Buy a product](#to-buy-a-product)
2. After the review modal appears, enter your review text and select how many stars you want to give to the seller (for example, if I want to give 4 stars, I click on the fourth star)
![screenshot of a review](/public/images/self/review.png)
3. Click "Submit"

If everything goes well, the review will be created. You can find it by clicking the "reviews" link on the seller's page.
![screenshot of all reviews](/public/images/self/all-reviews.png)

---

### Authentication

Anyone can view home page, category page, product page, user page and review page. But if you want to buy products, sell products or create a chat with someone, you first must sign in.

#### To sign in:

1. Go to [sign in page]
![screenshot of the sign in page](/public/images/self/signin.png)
2. Click on one of the auth providers
3. Follow the steps required to authenticate with the selected auth provider

If everything goes well, you will be redirected to the [profile page].
![screenshot of the profile page](/public/images/self/profile.png)

#### To sign out:

1. Go to [profile page]
2. Click "Options"
3. Click "Sign out"

## License

Payback is available under the [MIT license](https://opensource.org/licenses/MIT). Payback also includes external libraries that are available under a variety of licenses.

<!-- keys -->

[home page]: https://payback-store.vercel.app
[sign in page]: https://payback-store.vercel.app/profile/signIn
[profile page]: https://payback-store.vercel.app/profile/products
[user #11]: https://payback-store.vercel.app/users/11
[chats page]: https://payback-store.vercel.app/chats
[product #35]: https://payback-store.vercel.app/products/35
[sell page]: https://payback-store.vercel.app/sell
Binary file added public/images/self/all-reviews.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/images/self/chat-with-message.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/images/self/chat-with-two-messages.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/images/self/chat.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/images/self/chats.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/images/self/home.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/images/self/product.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/images/self/profile.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/images/self/review.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/images/self/sell-step-categories.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/images/self/sell-step-description.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/images/self/sell-step-photo.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/images/self/sell-step-price.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/images/self/sell-step-status.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/images/self/sell-step-title.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/images/self/signin.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/images/self/sold-product.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/images/self/uploaded-product.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/images/self/user.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 1 addition & 2 deletions src/components/ChatPage/ChatPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import useSessionUser from "hooks/useSessionUser";
import Head from "next/head";

function ChatPage({ id, fetchedData: chat }) {
const sessionUser = useSessionUser();
const userId = sessionUser.id;
const userId = useSessionUser().id;

return (
<>
Expand Down
33 changes: 29 additions & 4 deletions src/components/ChatsPage/ChatsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,41 @@ import { useEffect } from "react";

function ChatsPage({ fetchedData: chats, setFetchedData: setChats }) {
const chatConnector = useChatConnector();
const sessionUser = useSessionUser();
const userId = sessionUser.id;
const userId = useSessionUser().id;

useEffect(() => {
function handleChat(chat) {
setChats(cur => (cur ? [...cur, chat] : [chat]));
}

return chatConnector?.connectToChats(userId, handleChat);
}, [userId, chatConnector, setChats]);
function handleMessage(message) {
setChats(cur =>
cur.map(chat => {
if (chat.id === message.chatId) {
return {
...chat,
newMessageCount: (chat.newMessageCount ?? 0) + 1,
};
}

return chat;
})
);
}

const disposeChatConnection = chatConnector?.connectToChats(
userId,
handleChat
);
const messageConnections = chats?.map(chat =>
chatConnector?.connectToMessages(chat.id, handleMessage)
);

return () => {
disposeChatConnection?.();
messageConnections?.forEach(dispose => dispose?.());
};
}, [userId, chatConnector, setChats, chats]);

const chatList = chats?.map(chat => (
<li key={chat.id}>{<LinkToChat chat={chat} />}</li>
Expand Down
2 changes: 1 addition & 1 deletion src/components/HomePage/HomePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default function HomePage({ products, categories, productFilter }) {
return (
<>
<Head>
<title>Payback — fully-featured e-commerce store</title>
<title>Payback — a fully-featured e-commerce store</title>
</Head>

<Header />
Expand Down
2 changes: 1 addition & 1 deletion src/components/Image/Image.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { default as NextImage } from "next/image";
import { useState } from "react";

export default function Image({ className, ...props }) {
const [isLoaded, setIsLoaded] = useState(false);
const [isLoaded, setIsLoaded] = useState(props.priority);

function handleLoadingComplete() {
setIsLoaded(true);
Expand Down
9 changes: 7 additions & 2 deletions src/components/Loading/Loading.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import styles from "./Loading.module.scss";
import Image from "components/Image";

export default function Loading() {
// todo: compress ?
return (
<img src="/images/loading.svg" alt="Loading" className={styles.image} />
<Image
src="/images/loading.svg"
alt="Loading..."
className={styles.image}
priority
/>
);
}
3 changes: 1 addition & 2 deletions src/components/Menu/Menu.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

.container {
position: fixed;
top: 100%;
bottom: 0;
left: 0;
z-index: 700;
display: flex;
Expand All @@ -13,7 +13,6 @@
color: c.$text-secondary;
background: c.$background;
box-shadow: 0 0 0.15rem 0 c.$text-secondary;
transform: translateY(-100%);

.icon {
font-size: 1.5rem;
Expand Down
2 changes: 0 additions & 2 deletions src/components/Product/Product.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export default function Product({
children,
imageSize,
image,
priority = false,
flexDirection = "column",
flexDirectionWhenExpanded = "column",
alignItems = "stretch",
Expand All @@ -28,7 +27,6 @@ export default function Product({
src={image}
alt=""
objectFit="contain"
priority={priority}
/>
<div className={styles.info}>
<p className={styles.price}>{formatMoney(price)}</p>
Expand Down
2 changes: 0 additions & 2 deletions src/components/ProductList/ProductList.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ function ProductList({
id={product.category.id}
name={product.category.name}
image={product.category.image}
priority={i === 0}
/>
)}
<Link href={`/products/${product.id}`}>
Expand All @@ -31,7 +30,6 @@ function ProductList({
price={product.price}
isSold={product.isSold}
image={product.image}
priority={i === 0}
>
<h3>{product.title}</h3>
</Product>
Expand Down
1 change: 0 additions & 1 deletion src/components/ProductPage/ProductPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ function ProductPage({
<Product
price={product.price}
image={product.image}
priority
imageSize="14rem"
flexDirectionWhenExpanded="row"
>
Expand Down
1 change: 0 additions & 1 deletion src/components/ReviewList/ReviewList.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ function ReviewList({ fetchedData: reviews }) {
flexDirection="row"
flexDirectionWhenExpanded="row"
alignItems="flex-start"
priority={i === 0}
>
<h3>{product.title}</h3>
</Product>
Expand Down
1 change: 0 additions & 1 deletion src/components/withDataFetching/withDataFetching.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export default function withDataFetching(
return;
}

console.log("fetching with cursor:", pageCursor);
fetchCallback(fetchDeps, customState, pageCursor)
.then(result => {
if (result.pageData) {
Expand Down
12 changes: 6 additions & 6 deletions src/data/sellSteps.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { FcTemplate } from "react-icons/fc";

export const STEP_DATA = [
{
title: "Select category",
title: "Select a category",
property: "category",
component: (handle, { categories }) => (
<CategorySearch
Expand All @@ -23,14 +23,14 @@ export const STEP_DATA = [
),
},
{
title: "Upload photo",
title: "Upload a photo",
property: "photoBlob",
component: handle => (
<FileForm onSubmit={handle} submitButton={<ContinueButton />} />
),
},
{
title: "Specify title",
title: "Specify a title",
property: "title",
component: handle => (
<InputForm
Expand All @@ -45,7 +45,7 @@ export const STEP_DATA = [
),
},
{
title: "Specify description",
title: "Specify a description",
property: "description",
component: handle => (
<InputForm
Expand All @@ -65,7 +65,7 @@ export const STEP_DATA = [
),
},
{
title: "Specify price",
title: "Specify a price",
property: "price",
component: (handle, { minPrice, maxPrice, serviceChargesPercent }) => (
<InputForm
Expand All @@ -83,7 +83,7 @@ export const STEP_DATA = [
),
},
{
title: "Select status",
title: "Select a status",
property: "isPremium",
component: (handle, { isMakingPayment, premiumCost }) => (
<>
Expand Down
Loading

1 comment on commit fd5112c

@vercel
Copy link

@vercel vercel bot commented on fd5112c Sep 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

payback – ./

payback-git-master-vladbrok.vercel.app
payback-store.vercel.app
payback-vladbrok.vercel.app

Please sign in to comment.