mirror of
https://github.com/vercel/commerce.git
synced 2025-07-22 12:24:18 +00:00
✨ feat: Account Page
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
@import '../../../../styles/utilities';
|
||||||
|
|
||||||
|
.accountPage {
|
||||||
|
@apply flex spacing-horizontal;
|
||||||
|
background-color: #F5F4F2;
|
||||||
|
margin-top: -3.2rem;
|
||||||
|
padding-bottom: 3.2rem;
|
||||||
|
|
||||||
|
.pageLeft {
|
||||||
|
padding-top: 5.6rem;
|
||||||
|
margin-right: 12.4rem;
|
||||||
|
|
||||||
|
.accNavi{
|
||||||
|
margin-top: 3.8rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pageRight {
|
||||||
|
padding-top: 5.6rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
134
src/components/modules/account/AccountPage/AccountPage.tsx
Normal file
134
src/components/modules/account/AccountPage/AccountPage.tsx
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
import React, { useEffect, useState } from "react"
|
||||||
|
import s from './AccountPage.module.scss'
|
||||||
|
|
||||||
|
import AccountNavigation from '../AccountNavigation/AccountNavigation'
|
||||||
|
import HeadingCommon from '../../../common/HeadingCommon/HeadingCommon'
|
||||||
|
import AccountInfomation from "./components/AccountInfomation/AccountInfomation"
|
||||||
|
import OrderInfomation from './components/OrderInformation/OrderInformation'
|
||||||
|
import EditInfoModal from './components/EditInfoModal/EditInfoModal'
|
||||||
|
|
||||||
|
interface AccountPageProps {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const AccountPage = ({} : AccountPageProps) => {
|
||||||
|
const waiting = [
|
||||||
|
{
|
||||||
|
id: "NO 123456",
|
||||||
|
products: ["Tomato", "Fish", "Pork", "Onion"],
|
||||||
|
totalPrice : 1000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "NO 123456",
|
||||||
|
products: ["Tomato", "Fish", "Pork", "Onion"],
|
||||||
|
totalPrice : 1000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "NO 123456",
|
||||||
|
products: ["Tomato", "Fish", "Pork", "Onion"],
|
||||||
|
totalPrice : 1000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const delivering = [
|
||||||
|
{
|
||||||
|
id: "NO 123456",
|
||||||
|
products: ["Tomato", "Fish", "Pork", "Onion"],
|
||||||
|
totalPrice : 1000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "NO 123456",
|
||||||
|
products: ["Tomato", "Fish", "Pork", "Onion"],
|
||||||
|
totalPrice : 1000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "NO 123456",
|
||||||
|
products: ["Tomato", "Fish", "Pork", "Onion"],
|
||||||
|
totalPrice : 1000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const delivered = [
|
||||||
|
{
|
||||||
|
id: "NO 123456",
|
||||||
|
products: ["Tomato", "Fish", "Pork", "Onion"],
|
||||||
|
totalPrice : 1000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "NO 123456",
|
||||||
|
products: ["Tomato", "Fish", "Pork", "Onion"],
|
||||||
|
totalPrice : 1000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "NO 123456",
|
||||||
|
products: ["Tomato", "Fish", "Pork", "Onion"],
|
||||||
|
totalPrice : 1000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const account = {
|
||||||
|
name: "vu duong",
|
||||||
|
email: "vuduong@gmail.com",
|
||||||
|
address: "234 Dien Bien Phu Bis, Dakao ward, District 1, HCMC",
|
||||||
|
postalCode: "700000",
|
||||||
|
phoneNumber: "(+84) 937 937 195"
|
||||||
|
}
|
||||||
|
|
||||||
|
const [accountInfoActive, setAccountInfoActive] = useState(false);
|
||||||
|
const [orderInfoActive, setOrderInfoActive] = useState(true);
|
||||||
|
const [favoritesActive, setFavoritesActive] = useState(false);
|
||||||
|
|
||||||
|
function accountActive() {
|
||||||
|
setAccountInfoActive(true);
|
||||||
|
setOrderInfoActive(false);
|
||||||
|
setFavoritesActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function orderActive() {
|
||||||
|
setAccountInfoActive(false);
|
||||||
|
setOrderInfoActive(true);
|
||||||
|
setFavoritesActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function favActive() {
|
||||||
|
setAccountInfoActive(false);
|
||||||
|
setOrderInfoActive(false);
|
||||||
|
setFavoritesActive(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [modalVisible, setModalVisible] = useState(false);
|
||||||
|
|
||||||
|
function showEditForm() {
|
||||||
|
setModalVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeModal() {
|
||||||
|
setModalVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<section className={s.accountPage}>
|
||||||
|
<div className={s.pageLeft}>
|
||||||
|
<HeadingCommon>Account</HeadingCommon>
|
||||||
|
<div className={s.accNavi}>
|
||||||
|
<AccountNavigation setAccountActive={accountActive} setOrderActive={orderActive} setFavActive={favActive} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={s.pageRight}>
|
||||||
|
<AccountInfomation active={accountInfoActive} account={account} clickShowEditForm={showEditForm} />
|
||||||
|
<OrderInfomation active={orderInfoActive} waiting={waiting} delivering={delivering} delivered={delivered} />
|
||||||
|
|
||||||
|
{/* Thieu cai favorite */}
|
||||||
|
{/* <FavoriteProduct active={favoritesActive} favProducts={favProducts} /> */}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</section>
|
||||||
|
<EditInfoModal closeModal={closeModal} visible={modalVisible} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AccountPage
|
BIN
src/components/modules/account/AccountPage/assets/avatar.png
Normal file
BIN
src/components/modules/account/AccountPage/assets/avatar.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
@@ -0,0 +1,49 @@
|
|||||||
|
@import '../../../../../../styles/utilities';
|
||||||
|
|
||||||
|
.accountInfomation {
|
||||||
|
transition: opacity 6s;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
height: 22rem;
|
||||||
|
width: 22rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-bottom: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accountName {
|
||||||
|
@apply heading-3 font-heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontalSeparator{
|
||||||
|
border: 1px solid var(--disabled);
|
||||||
|
max-width: 39.2rem;
|
||||||
|
min-width: 30rem;
|
||||||
|
margin-top: 2.4rem;
|
||||||
|
margin-bottom: 2.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shippingInfo {
|
||||||
|
@apply heading-3 font-heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accountAddress {
|
||||||
|
max-width: 31rem;
|
||||||
|
min-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editInfoBtn {
|
||||||
|
@apply text-center font-bold;
|
||||||
|
margin-top: 2.4rem;
|
||||||
|
margin-bottom: 2.4rem;
|
||||||
|
padding: .8rem 1.6rem;
|
||||||
|
color: #141414;
|
||||||
|
border: 1px solid #141414;
|
||||||
|
max-width: 8.8rem;
|
||||||
|
border-radius: 25%;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
@apply cursor-pointer;
|
||||||
|
background-color: #FBFBFB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,50 @@
|
|||||||
|
import React, { useState } from "react"
|
||||||
|
import s from './AccountInfomation.module.scss'
|
||||||
|
|
||||||
|
import Image from "next/image"
|
||||||
|
import avatar from '../../assets/avatar.png';
|
||||||
|
|
||||||
|
interface AccountInfomationProps {
|
||||||
|
account: {name: string, email: string, address: string, postalCode: string, phoneNumber: string};
|
||||||
|
active: boolean;
|
||||||
|
clickShowEditForm: ()=>void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AccountInfomation = ({ account, active=false, clickShowEditForm } : AccountInfomationProps) => {
|
||||||
|
return (
|
||||||
|
<section className={s.accountInfomation}>
|
||||||
|
{
|
||||||
|
active && <div>
|
||||||
|
<div className={s.avatar}>
|
||||||
|
<Image src={avatar} alt="avatar" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={s.accountName}>
|
||||||
|
{account.name}
|
||||||
|
</div>
|
||||||
|
<div className={s.accountEmail}>
|
||||||
|
{account.email}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={s.horizontalSeparator}></div>
|
||||||
|
|
||||||
|
<div className={s.shippingInfo}>Shipping Infomation</div>
|
||||||
|
|
||||||
|
<div className={s.accountAddress}>
|
||||||
|
{account.address + `, ${account.postalCode}`}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={s.accountPhoneNumber}>
|
||||||
|
{account.phoneNumber}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div onClick={clickShowEditForm} className={s.editInfoBtn}>Edit</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AccountInfomation
|
@@ -0,0 +1,67 @@
|
|||||||
|
@import '../../../../../../styles/utilities';
|
||||||
|
|
||||||
|
.editInfoModal {
|
||||||
|
.input {
|
||||||
|
@apply bg-white;
|
||||||
|
margin-bottom: 1.6rem;
|
||||||
|
width: 100%;
|
||||||
|
border: 2px solid #EBEBEB;
|
||||||
|
border-radius: .8rem;
|
||||||
|
padding: 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputState {
|
||||||
|
@apply bg-white;
|
||||||
|
margin-bottom: 1.6rem;
|
||||||
|
margin-right: 1.6rem;
|
||||||
|
border: 2px solid #EBEBEB;
|
||||||
|
border-radius: .8rem;
|
||||||
|
padding: 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputPostalCode {
|
||||||
|
@apply bg-white;
|
||||||
|
margin-bottom: 1.6rem;
|
||||||
|
border: 2px solid #EBEBEB;
|
||||||
|
border-radius: .8rem;
|
||||||
|
padding: 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputPhoneNumber {
|
||||||
|
@apply bg-white;
|
||||||
|
margin-bottom: 4rem;
|
||||||
|
width: 100%;
|
||||||
|
border: 2px solid #EBEBEB;
|
||||||
|
border-radius: .8rem;
|
||||||
|
padding: 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
@apply flex;
|
||||||
|
|
||||||
|
.buttonCancel {
|
||||||
|
@apply bg-white text-center font-bold;
|
||||||
|
color: #141414;
|
||||||
|
border: 1px solid #141414;
|
||||||
|
border-radius: 2.5rem;
|
||||||
|
padding: 1.6rem;
|
||||||
|
margin-right: 1.6rem;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
@apply cursor-pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonSave {
|
||||||
|
@apply text-center font-bold;
|
||||||
|
background-color: var(--primary);
|
||||||
|
color: white;
|
||||||
|
border-radius: 2.5rem;
|
||||||
|
padding: 1.6rem;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
@apply cursor-pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,52 @@
|
|||||||
|
import classNames from "classnames"
|
||||||
|
import React from "react"
|
||||||
|
import s from './EditInfoModal.module.scss'
|
||||||
|
|
||||||
|
import {ModalCommon, MenuDropdown} from '../../../../../common'
|
||||||
|
|
||||||
|
interface EditInfoModalProps {
|
||||||
|
visible: boolean;
|
||||||
|
closeModal: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditInfoModal = ({ visible = false, closeModal }: EditInfoModalProps) => {
|
||||||
|
|
||||||
|
function saveInfo() {
|
||||||
|
console.log("saved !!!");
|
||||||
|
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = [
|
||||||
|
{name: "hihi"},
|
||||||
|
{name: "hihi"},
|
||||||
|
{name: "hihi"}
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalCommon onClose={closeModal} visible={visible} title="Edit Infomation">
|
||||||
|
<section className={s.editInfoModal}>
|
||||||
|
<div><input className={s.input} type="text" name="" placeholder="Name" /></div>
|
||||||
|
<div><input className={s.input} type="text" name="" placeholder="Email" /></div>
|
||||||
|
<div><input className={s.input} type="text" name="" placeholder="Address" /></div>
|
||||||
|
<div><input className={s.input} type="text" name="" placeholder="City" /></div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{/* <MenuDropdown options={options} isHasArrow={false} > */}
|
||||||
|
<input className={s.inputState} type="text" name="" placeholder="State" />
|
||||||
|
{/* </MenuDropdown> */}
|
||||||
|
<input className={s.inputPostalCode} type="text" name="" placeholder="hehe" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div><input className={s.inputPhoneNumber} type="text" name="" placeholder="Phone number" /></div>
|
||||||
|
|
||||||
|
<div className={s.buttons}>
|
||||||
|
<div onClick={closeModal} className={s.buttonCancel}>Cancel</div>
|
||||||
|
<div onClick={saveInfo} className={s.buttonSave}>Save</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</ModalCommon>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditInfoModal
|
@@ -0,0 +1,16 @@
|
|||||||
|
@import '../../../../../../styles/utilities';
|
||||||
|
|
||||||
|
.orderInformation {
|
||||||
|
.title {
|
||||||
|
@apply heading-3 font-heading;
|
||||||
|
margin-top: 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs {
|
||||||
|
margin-top: 3.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabPanes {
|
||||||
|
margin-top: 2.4rem;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,68 @@
|
|||||||
|
import React, {useState} from "react"
|
||||||
|
import s from './OrderInformation.module.scss'
|
||||||
|
|
||||||
|
import { TabCommon } from '../../../../../common'
|
||||||
|
import TabPane from '../../components/TabPane/TabPane'
|
||||||
|
import DeliveryItem from '../../../DeliveryItem/DeliveryItem'
|
||||||
|
|
||||||
|
|
||||||
|
interface OrderInformationProps {
|
||||||
|
waiting: {id: string, products: string[], totalPrice: number}[],
|
||||||
|
delivering: {id: string, products: string[], totalPrice: number}[],
|
||||||
|
delivered: {id: string, products: string[], totalPrice: number}[],
|
||||||
|
active: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const OrderInformation = ({ waiting, delivering, delivered, active=true } : OrderInformationProps) => {
|
||||||
|
const [activeTabPane, setActiveTabPane] = useState("waiting");
|
||||||
|
|
||||||
|
function changeTabPane(tab: string) {
|
||||||
|
setActiveTabPane(tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={s.orderInformation}>
|
||||||
|
{
|
||||||
|
active && <div>
|
||||||
|
<div className={s.title}>Order Information</div>
|
||||||
|
|
||||||
|
<div className={s.tabs}>
|
||||||
|
<TabCommon changeTabPane={changeTabPane} />
|
||||||
|
|
||||||
|
<div className={s.tabPanes}>
|
||||||
|
<TabPane active={activeTabPane==="waiting" ? `active` : ""}>
|
||||||
|
{
|
||||||
|
waiting.map((order, i) => {
|
||||||
|
return (
|
||||||
|
<DeliveryItem key={i} id={order.id} status="waiting" products={order.products} totalPrice={order.totalPrice} />
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</TabPane>
|
||||||
|
<TabPane active={activeTabPane==="delivering" ? `active` : ""}>
|
||||||
|
{
|
||||||
|
delivering.map((order, i) => {
|
||||||
|
return (
|
||||||
|
<DeliveryItem key={i} id={order.id} status="delivering" products={order.products} totalPrice={order.totalPrice} />
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</TabPane>
|
||||||
|
<TabPane active={activeTabPane==="delivered" ?`active` : ""}>
|
||||||
|
{
|
||||||
|
delivered.map((order, i) => {
|
||||||
|
return (
|
||||||
|
<DeliveryItem key={i} id={order.id} status="delivered" products={order.products} totalPrice={order.totalPrice} />
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</TabPane>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OrderInformation
|
@@ -0,0 +1,23 @@
|
|||||||
|
@import '../../../../../../styles/utilities';
|
||||||
|
|
||||||
|
.tabPane {
|
||||||
|
@apply hidden;
|
||||||
|
animation-duration: 0.6s;
|
||||||
|
animation-name: appear;
|
||||||
|
@keyframes appear {
|
||||||
|
from {
|
||||||
|
margin-left: 100%;
|
||||||
|
width: 200%;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
margin-left: 0%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
@apply block;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,20 @@
|
|||||||
|
import classNames from "classnames"
|
||||||
|
import React from "react"
|
||||||
|
import s from './TabPane.module.scss'
|
||||||
|
|
||||||
|
interface TabPaneProps {
|
||||||
|
active: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TabPane = ({ active="", children } : TabPaneProps) => {
|
||||||
|
return (
|
||||||
|
<section className={classNames(s.tabPane, {
|
||||||
|
[s[active]] : active
|
||||||
|
})}>
|
||||||
|
{children}
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TabPane
|
Reference in New Issue
Block a user