This commit is contained in:
익희 김 2024-12-08 21:05:09 +09:00
parent b1f0627611
commit c8447d1acb
18 changed files with 302 additions and 117 deletions

View File

@ -1,9 +1,10 @@
BASE_URL=http://localhost:5100
BASE_URL=http://101.79.10.128:5100
# BASE_URL=http://localhost:5100
STRIPE_PUBLIC_KEY=
STRIPE_SECRET_KEY=
PAYPAL_CLIENT_ID=
CLOUDINARY_CLOUD_NAME="dmjztlmj4"
BASE_CURRENCY=KRW
SHIPPING_FEE=
JWT_SECRET=
JWT_SECRET="123456"

View File

@ -6,4 +6,4 @@ PAYPAL_CLIENT_ID=
CLOUDINARY_CLOUD_NAME="dmjztlmj4"
BASE_CURRENCY=KRW
SHIPPING_FEE=
JWT_SECRET=
JWT_SECRET="123456"

View File

@ -1,47 +1,39 @@
import React from 'react';
'use client';
import React, { useEffect, useState } from 'react';
// guard
import GuestGuard from 'src/guards/guest';
// mui
import { Card, Stack, Container, Typography } from '@mui/material';
// components
import LoginMain from 'src/components/_main/auth/login';
import { lang } from 'src/components/lang/kr';
// Meta information
export const metadata = {
title: 'Login to Nextall | Your Gateway to Seamless Shopping and Secure Transactions',
description:
'Log in to Nextall for secure access to your account. Enjoy seamless shopping, personalized experiences, and hassle-free transactions. Your trusted portal to a world of convenience awaits. Login now!',
applicationName: 'Nextall',
authors: 'Nextall',
keywords: 'ecommerce, Nextall, Commerce, Login Nextall, LoginFrom Nextall'
};
export default function Login() {
export default async function Login() {
return (
<>
<GuestGuard>
<Container maxWidth="sm">
<Card
sx={{
maxWidth: 560,
m: 'auto',
my: '80px',
flexDirection: 'column',
justifyContent: 'center',
p: 3
}}
>
<Stack mb={5}>
<Typography textAlign="center" variant="h4" component="h1" gutterBottom>
{lang.login}
</Typography>
</Stack>
<GuestGuard>
<Container maxWidth="sm">
<Card
sx={{
maxWidth: 560,
m: 'auto',
my: '80px',
flexDirection: 'column',
justifyContent: 'center',
p: 3
}}
>
<Stack mb={5}>
<Typography textAlign="center" variant="h4" component="h1" gutterBottom>
{lang.login}
</Typography>
</Stack>
<LoginMain />
</Card>
</Container>
</GuestGuard>
</>
<LoginMain />
</Card>
</Container>
</GuestGuard>
);
}

View File

@ -1,21 +1,33 @@
'use client'
// guard
import GuestGuard from 'src/guards/guest';
import { useEffect, useState } from 'react';
// mui
import { Card, Container, Typography } from '@mui/material';
// components
import RegisterMain from 'src/components/_main/auth/register';
import { lang } from 'src/components/lang/kr';
// Meta information
// export const metadata = {
// title: 'Create Your Nextall Account | Join Us for Exclusive Deals and Seamless Shopping',
// description:
// 'Register with Nextall today to unlock a world of exclusive deals, personalized recommendations, and secure transactions. Join our community for a seamless shopping experience. Sign up now and elevate your online shopping journey!',
// applicationName: 'Nextall',
// authors: 'Nextall',
// keywords: 'ecommerce, Nextall, Commerce, Register Nextall, RegisterFrom Nextall'
// };
export default async function Register() {
// Next.js navigation API
import { useSearchParams } from 'next/navigation';
export default function Register() {
const searchParams = useSearchParams(); // Next.js navigation API
const [segmentTitle, setSegmentTitle] = useState('');
useEffect(() => {
const redirectParam = searchParams.get('redirect');
console.log('Redirect Param:', redirectParam);
if (redirectParam === '/create-shop') {
setSegmentTitle(lang.seller);
} else {
setSegmentTitle(lang.buyer);
}
}, [searchParams]);
return (
<>
<GuestGuard>
@ -31,7 +43,7 @@ export default async function Register() {
}}
>
<Typography variant="h4" component="h1" gutterBottom textAlign="center">
{lang['Create your account']}
{segmentTitle} {lang['Create your account']}
</Typography>
<RegisterMain />
</Card>

View File

@ -5,7 +5,7 @@ import { Toolbar } from '@mui/material';
// components
import Navbar from 'src/layout/_main/navbar';
import Footer from 'src/layout/_main/footer';
// import Footer from 'src/layout/_main/footer';
import Topbar from 'src/layout/_main/topbar';
import ActionBar from 'src/layout/_main/actionbar';
@ -32,7 +32,7 @@ export default async function RootLayout({ children }) {
<ActionBar />
{children}
<Toolbar sx={{ display: { xs: 'block', md: 'none' } }} />
<Footer />
{/* <Footer /> */}
</>
);
}

View File

@ -33,7 +33,8 @@ export default async function ShopComponent() {
<Grid container spacing={2} justifyContent="center" alignItems="center">
{(data?.data).map((inner) => (
<React.Fragment key={Math.random()}>
<Grid item lg={4} md={6} sm={6} xs={12}>
{/* <Grid item lg={4} md={6} sm={6} xs={12}> */}
<Grid item>
<ShopCard shop={inner} isLoading={false} />
</Grid>
</React.Fragment>

View File

@ -1,12 +1,13 @@
'use client';
import * as Yup from 'yup';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { useMutation } from 'react-query';
import RouterLink from 'next/link';
import { useSearchParams } from 'next/navigation';
import { useRouter } from 'next-nprogress-bar';
import toast from 'react-hot-toast';
import { lang } from '../lang/kr';
import { lang, langen, langjp } from '../lang/kr';
import getCookies from '../lang/langUtil'
// formik
import { useFormik, Form, FormikProvider } from 'formik';
// cookies
@ -26,25 +27,35 @@ import {
TextField,
IconButton,
InputAdornment,
FormControlLabel
FormControl,
FormControlLabel,
Select,
MenuItem,
InputLabel
} from '@mui/material';
import { LoadingButton } from '@mui/lab';
// icons
import { MdOutlineVisibility, MdLock, MdOutlineVisibilityOff } from 'react-icons/md';
import { IoMdMail } from 'react-icons/io';
export default function LoginForm() {
const langIs = getCookies('lang');
const { push } = useRouter();
const dispatch = useDispatch();
const searchParam = useSearchParams();
const redirect = searchParam.get('redirect');
const [loading, setloading] = useState(false);
const [showPassword, setShowPassword] = useState(false);
const [langState, setLangState] = useState(langIs ? langIs : 'kr')
const { mutate } = useMutation(api.login, {
onSuccess: async (data) => {
dispatch(setLogin(data.user));
dispatch(setWishlist(data.user.wishlist));
await createCookies('token', data.token);
// await createCookies('token', data.token);
setloading(false);
toast.success(lang['Logged in successfully!']);
const isAdmin = data.user.role.includes('admin');
@ -77,14 +88,30 @@ export default function LoginForm() {
}
});
const { errors, touched, values, handleSubmit, getFieldProps } = formik;
useEffect(() => {
createCookies('lang', langState)
}, [langState]);
return (
<>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">SELECT LANGUAGE</InputLabel>
<Select
onChange={(e) => setLangState(e.target.value)}
value={langIs}
>
<MenuItem value="kr">KOREAN</MenuItem>
<MenuItem value="jp">JAPANESE</MenuItem>
<MenuItem value="en">ENGLISH</MenuItem>
</Select>
</FormControl>
<FormikProvider value={formik}>
<Form autoComplete="off" noValidate onSubmit={handleSubmit}>
<Stack spacing={3}>
<Stack gap={0.5} width={1}>
<Typography variant="overline" color="text.primary" htmlFor="email" component={'label'}>
{lang.Email}
{({ kr: lang, en: langen, jp: langjp }[langIs] || lang).Email}
</Typography>
<TextField
id="email"
@ -106,7 +133,7 @@ export default function LoginForm() {
<Stack gap={0.5} width={1}>
<Typography variant="overline" color="text.primary" htmlFor="password" component={'label'}>
{lang.Password}
{({ kr: lang, en: langen, jp: langjp }[langIs] || lang).Password}
</Typography>
<TextField
id="password"
@ -136,18 +163,20 @@ export default function LoginForm() {
<Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ my: 2 }}>
<FormControlLabel
control={<Checkbox {...getFieldProps('remember')} checked={values.remember} />}
label={lang['Remember me']}
label={({ kr: lang, en: langen, jp: langjp }[langIs] || lang)['Remember me']}
/>
<Link component={RouterLink} variant="subtitle2" href="/auth/forget-password">
{lang['Forgot password']}
{({ kr: lang, en: langen, jp: langjp }[langIs] || lang)['Forgot password']}
</Link>
</Stack>
<LoadingButton fullWidth size="large" type="submit" variant="contained" loading={loading}>
{lang.login}
{({ kr: lang, en: langen, jp: langjp }[langIs] || lang).login}
</LoadingButton>
<Typography variant="subtitle2" mt={3} textAlign="center">
<Link href={`/auth/register${redirect ? '?redirect=' + redirect : ''}`} component={RouterLink}>
{lang.Register}
{({ kr: lang, en: langen, jp: langjp }[langIs] || lang).Register}
</Link>
</Typography>
</Form>

View File

@ -67,10 +67,11 @@ export default function RegisterForm() {
const { mutate } = useMutation(api.register, {
onSuccess: async (data) => {
dispatch(setLogin(data.user));
await createCookies('token', data.token);
toast.success('OTP sent to your email' + ' ' + data.user.firstName);
// await createCookies('token', data.token);
// toast.success('OTP sent to your email' + ' ' + data.user.firstName);
setloading(false);
router.push(redirect ? `/auth/verify-otp?redirect=${redirect}` : `/auth/verify-otp`);
// router.push(redirect ? `/auth/verify-otp?redirect=${redirect}` : `/auth/verify-otp`);
router.push(`/auth/login`);
},
onError: (err) => {
const message = JSON.stringify(err.response.data.message);

View File

@ -60,47 +60,47 @@ export default function CreateShopSettingFrom() {
const ShopSettingScema = Yup.object().shape({
title: Yup.string().required('title is required'),
cover: Yup.mixed().required('Cover is required'),
logo: Yup.mixed().required('logo is required'),
// cover: Yup.mixed().required('Cover is required'),
// logo: Yup.mixed().required('logo is required'),
slug: Yup.string().required('Slug is required'),
description: Yup.string().required('Description is required'),
metaTitle: Yup.string().required('Meta title is required'),
metaDescription: Yup.string().required('Meta description is required'),
// description: Yup.string().required('Description is required'),
// metaTitle: Yup.string().required('Meta title is required'),
// metaDescription: Yup.string().required('Meta description is required'),
phone: Yup.string().required('Phone Number is required'),
paymentInfo: Yup.object().shape({
holderName: Yup.string().required('Holder Name is required'),
holderEmail: Yup.string().required('Holder email is required'),
bankName: Yup.string().required('Bank name is required'),
AccountNo: Yup.number().required('Account No is required')
}),
address: Yup.object().shape({
city: Yup.string().required('City is required'),
state: Yup.string().required('State is required'),
streetAddress: Yup.string().required('Street Address is required')
})
// paymentInfo: Yup.object().shape({
// holderName: Yup.string().required('Holder Name is required'),
// holderEmail: Yup.string().required('Holder email is required'),
// bankName: Yup.string().required('Bank name is required'),
// AccountNo: Yup.number().required('Account No is required')
// }),
// address: Yup.object().shape({
// city: Yup.string().required('City is required'),
// state: Yup.string().required('State is required'),
// streetAddress: Yup.string().required('Street Address is required')
// })
});
const formik = useFormik({
initialValues: {
title: '',
metaTitle: '',
metaTitle: 'test metatitle',
cover: null,
logo: null,
description: '',
metaDescription: '',
description: 'test desc',
metaDescription: 'test metade',
file: '',
slug: '',
phone: '',
paymentInfo: {
holderName: '',
holderEmail: '',
bankName: '',
AccountNo: ''
holderName: 'test holderName',
holderEmail: 'test holderEmail',
bankName: 'test bankName',
AccountNo: 'test AccountNo'
},
address: {
country: '',
city: '',
state: '',
streetAddress: ''
country: 'test',
city: 'test',
state: 'test',
streetAddress: 'test'
}
},
enableReinitialize: true,

View File

@ -17,5 +17,56 @@ export const lang = {
'Best Shops': '상점 목록',
'View More': '더보기',
'Products not found': '상품이 준비되지 않았습니다',
Products: '상품'
Products: '상품',
'seller': '셀러',
'buyer': '바이어',
'Register':'가입'
};
export const langjp = {
"Don't you have an account?": 'アカウントをお持ちではありませんか?',
Register: '登録',
'Forgot password': 'パスワードをお忘れですか?',
login: 'ログイン',
'Remember me': 'ログイン情報を保存する',
Password: 'パスワード',
Email: 'メールアドレス',
'Enter valid email': '有効なメールアドレスを入力してください',
'Email is required': 'メールアドレスは必須です',
'Password is required': 'パスワードは必須です',
'Password should be 8 characters or longer': 'パスワードは8文字以上でなければなりません',
'Logged in successfully!': 'ログイン成功!',
'Create your account': 'アカウントを作成',
'First Name': '名前',
'Best Shops': 'ベストショップ',
'View More': 'もっと見る',
'Products not found': '商品が見つかりませんでした',
Products: '商品',
seller: '販売者',
buyer: '購入者'
};
export const langen = {
"Don't you have an account?": "Don't you have an account?",
Register: 'Register',
'Forgot password': 'Forgot password',
login: 'login',
'Remember me': 'Remember me',
Password: 'Password',
Email: 'Email',
'Enter valid email': 'Enter valid email',
'Email is required': 'Email is required',
'Password is required': 'Password is required',
'Password should be 8 characters or longer': 'Password should be 8 characters or longer',
'Logged in successfully!': 'Logged in successfully!',
'Create your account': 'Create your account',
'First Name': 'First Name',
Register: 'Register',
'Best Shops': 'Best Shops',
'View More': 'View More',
'Products not found': 'Products not found',
Products: 'Products',
'seller': 'seller',
'buyer': 'buyer',
};

View File

@ -0,0 +1,9 @@
import { lang, langen, langjp } from '../lang/kr';
export default function getCookies(name) {
const cookies = document.cookie
.split('; ')
.find(row => row.startsWith(`${name}=`));
return cookies ? cookies.split('=')[1] : null;
}

View File

@ -14,6 +14,7 @@ import { UserList } from 'src/components/lists';
import BlurImageAvatar from 'src/components/avatar';
// redux
import { useSelector } from 'react-redux';
import { lang } from 'src/components/lang/kr';
function getKeyByValue(object, value) {
return Object.keys(object).find((key) => object[key] === value);
@ -56,7 +57,7 @@ export default function UserSelect({ isAdmin }) {
component={Link}
fontSize={14}
>
Login
{lang.login}
</Typography>
<Divider orientation="vertical" flexItem />
<Typography
@ -66,7 +67,7 @@ export default function UserSelect({ isAdmin }) {
href={`/auth/register${isAuthPath || isHomePath ? '' : `?redirect=${pathname}`}`}
fontSize={14}
>
Register
{lang.Register}
</Typography>
</Stack>
) : (

View File

@ -13,3 +13,7 @@ export async function createCookies(name, token) {
export async function deleteCookies(name) {
cookies().delete(name);
}

View File

@ -38,7 +38,6 @@ function MenuDesktopItem({ ...props }) {
const { title, path, isDropdown } = item;
const anchorRef = React.useRef(null);
const isActive = pathname === path;
if (isDropdown) {
return (
<>

View File

@ -0,0 +1,101 @@
{
"menu": [
{
"title": "Categories",
"path": "",
"isDropdown": true
},
{
"title": "Home",
"path": "/"
},
{
"title": "Products",
"path": "/products"
},
{
"title": "Shops",
"path": "/shops"
},
{
"title": "Compaigns",
"path": "/compaigns"
},
{
"title": "Contact",
"path": "/contact"
},
{
"title": "About",
"path": "/about"
}
],
"mobile_menu": [
{
"name": "홈",
"href": "/"
},
{
"name": "검색",
"href": "/search"
},
{
"name": "상품",
"href": "/products"
},
{
"name": "카트",
"href": "/cart"
},
{
"name": "사용자",
"isUser": true,
"href": "/auth/login"
}
],
"social": [
{ "name": "FaceBook", "color": "#1672E6", "href": "#" },
{ "name": "Instagram", "color": "#F40052", "href": "#" },
{ "name": "Linkedin", "color": "#0962B7", "href": "#" },
{ "name": "Pinterest", "color": "#B7081B", "href": "#" }
],
"company": {
"company_name": "Nextall",
"address": "123 Main Street Apartment 567",
"email": "test@example.com",
"phone": "555-1234-2222"
},
"footer_links": [
{
"headline": "Product",
"children": [
{ "name": "application", "href": "#" },
{ "name": "features", "href": "#" },
{ "name": "Requirements", "href": "#" },
{ "name": "Technology", "href": "#" }
]
},
{
"headline": "Resources",
"children": [
{ "name": "Changelogy", "href": "#" },
{ "name": "Updates", "href": "#" },
{ "name": "Providers", "href": "#" },
{ "name": "Affiliat", "href": "#" },
{ "name": "Roadmap", "href": "#" }
]
},
{
"headline": "Company",
"children": [
{ "name": "Blog", "href": "#" },
{ "name": "about us", "href": "#" },
{ "name": "contact us", "href": "#" },
{ "name": "Jobs", "href": "#" },
{ "name": "Team", "href": "#" }
]
}
]
}

View File

@ -5,11 +5,6 @@
"path": "",
"isDropdown": true
},
{
"title": "Home",
"path": "/"
},
{
"title": "Products",
"path": "/products"
@ -18,18 +13,6 @@
{
"title": "Shops",
"path": "/shops"
},
{
"title": "Compaigns",
"path": "/compaigns"
},
{
"title": "Contact",
"path": "/contact"
},
{
"title": "About",
"path": "/about"
}
],
"mobile_menu": [

View File

@ -97,9 +97,9 @@ export default function Navbar() {
<Stack gap={2} direction="row" alignItems={'center'}>
{/* <LanguageSelect /> */}
<SettingMode />
<WishlistPopover />
<CompareWidget />
<CartWidget checkout={checkout} />
{/* <WishlistPopover />
<CompareWidget /> */}
{/* <CartWidget checkout={checkout} /> */}
</Stack>
</Toolbar>
</Container>

View File

@ -3,6 +3,7 @@ import React from 'react';
import dynamic from 'next/dynamic';
import NextLink from 'next/link';
import { useSelector } from 'react-redux';
import { lang } from 'src/components/lang/kr';
// mui
import { Toolbar, Container, Stack, useTheme, Link, Divider, Skeleton } from '@mui/material';
@ -49,7 +50,7 @@ export default function UserTopbar() {
href={isAuthenticated ? '/create-shop' : '/auth/register?redirect=/create-shop'}
sx={{ color: 'text.primary', fontSize: 14 }}
>
Become a seller
{lang.seller}
</Link>
</>
)
@ -61,7 +62,7 @@ export default function UserTopbar() {
href={isAuthenticated ? '/create-shop' : '/auth/register?redirect=/create-shop'}
sx={{ color: 'text.primary', fontSize: 14 }}
>
Become a seller
{lang.seller}
</Link>
</>
)}