Merge pull request #67 from bipproduction/lukman/25-juli-2024

Lukman/25 juli 2024
This commit is contained in:
Amalia
2024-07-25 10:25:40 +08:00
committed by GitHub
17 changed files with 243 additions and 38 deletions

View File

@@ -1,8 +1,7 @@
### ###
POST http://localhost:3000/api/auth/login/ HTTP/1.1 POST http://localhost:3000/api/auth/login/ HTTP/1.1
Content-Type: application/json Content-Type: application/json
{ {
"id": "devLukman", "phone": "6287701790942"
"phone": "6287701790942",
"email": "lukman@bip.com"
} }

View File

@@ -38,6 +38,7 @@
"echarts-for-react": "^3.0.2", "echarts-for-react": "^3.0.2",
"embla-carousel-autoplay": "^7.1.0", "embla-carousel-autoplay": "^7.1.0",
"embla-carousel-react": "^7.1.0", "embla-carousel-react": "^7.1.0",
"iron-session": "^8.0.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"moment": "^2.30.1", "moment": "^2.30.1",
"next": "14.2.4", "next": "14.2.4",

View File

@@ -1,6 +1,7 @@
import { ViewVerification } from "@/module/auth"; import { ViewVerification } from "@/module/auth";
import { IVerification } from "@/types";
import React from "react"; import React from "react";
export default function Verification() { export default function Page() {
return <ViewVerification />; return <ViewVerification phone={""} otp={0} user={''} />;
} }

View File

@@ -0,0 +1,18 @@
import { prisma, pwd_key_config } from "@/module/_global";
import { unsealData } from "iron-session";
import { cookies } from "next/headers";
export async function GET() {
const sessionCookie = cookies().get("sessionCookie");
const userId = await unsealData(sessionCookie!.value, {
password: pwd_key_config,
});
const user = await prisma.user.findUnique({
where: {
id: String(userId),
},
});
return Response.json(user);
}

View File

@@ -1,11 +1,12 @@
import prisma from "@/module/_global/bin/prisma";
import { Login } from "@/types/auth/login"; import { prisma } from "@/module/_global";
import { ILogin } from "@/types";
import { NextRequest } from "next/server"; import { NextRequest } from "next/server";
export async function POST(req: NextRequest) { export async function POST(req: NextRequest) {
const { email }: Login = await req.json(); const { phone }: ILogin = await req.json();
const user = await prisma.user.findUnique({ const user = await prisma.user.findUnique({
where: { email, isActive: true }, where: { phone, isActive: true },
select: { id: true, phone: true }, select: { id: true, phone: true },
}); });

View File

@@ -0,0 +1,7 @@
import { cookies } from "next/headers";
export async function DELETE() {
cookies().delete('sessionCookie')
return Response.json({ success: true })
}

View File

@@ -0,0 +1,16 @@
import { pwd_key_config } from "@/module/_global";
import { sealData } from "iron-session";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
export async function POST(req: Request) {
const { user } = await req.json();
const encryptedUserData = await sealData(user, { password: pwd_key_config });
cookies().set({
name: "sessionCookie",
value: encryptedUserData,
});
return Response.json({ success: true });
}

View File

@@ -0,0 +1 @@
export const pwd_key_config = "fchgvjknlmdfnbvghhujlaknsdvjbhknlkmsdbdyu567t8y9u30r4587638y9uipkoeghjvuyi89ipkoefmnrjbhtiu4or9ipkoemnjfbhjiuoijdklnjhbviufojkejnshbiuojijknehgruyu"

View File

@@ -1,3 +1,5 @@
import prisma from "./bin/prisma";
import { pwd_key_config } from "./bin/val_global";
import { WARNA } from "./fun/WARNA"; import { WARNA } from "./fun/WARNA";
import LayoutDrawer from "./layout/layout_drawer"; import LayoutDrawer from "./layout/layout_drawer";
import LayoutIconBack from "./layout/layout_icon_back"; import LayoutIconBack from "./layout/layout_icon_back";
@@ -7,11 +9,13 @@ import LayoutNavbarHome from "./layout/layout_navbar_home";
import LayoutNavbarNew from "./layout/layout_navbar_new"; import LayoutNavbarNew from "./layout/layout_navbar_new";
import ViewFilter from "./view/view_filter"; import ViewFilter from "./view/view_filter";
export { WARNA } export { WARNA };
export { LayoutLogin } export { LayoutLogin };
export { LayoutNavbarHome } export { LayoutNavbarHome };
export { LayoutIconBack } export { LayoutIconBack };
export { LoadingPage } export { LoadingPage };
export { LayoutDrawer } export { LayoutDrawer };
export { LayoutNavbarNew } export { LayoutNavbarNew };
export { ViewFilter } export { ViewFilter };
export { prisma };
export { pwd_key_config };

View File

@@ -12,24 +12,68 @@ import {
Title, Title,
} from "@mantine/core"; } from "@mantine/core";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import React from "react"; import React, { useState } from "react";
import toast from "react-hot-toast";
import ViewVerification from "../../varification/view/view_verification";
import { useFocusTrap } from "@mantine/hooks";
function ViewLogin() { function ViewLogin() {
const focusTrapRef = useFocusTrap()
const router = useRouter() const router = useRouter()
const textInfo = const textInfo =
"Kami akan mengirim kode verifikasi melalui WhatsApp, guna mengonfirmasikan nomor Anda."; "Kami akan mengirim kode verifikasi melalui WhatsApp, guna mengonfirmasikan nomor Anda.";
function onMasuk() { const [isPhone, setPhone] = useState("")
// router.push("/verification") const [isOTP, setOTP] = useState<any>(null)
window.location.href = "/verification" const [isValPhone, setValPhone] = useState<any>(null)
const [isUser, setUser] = useState<any>(null)
const [isVerif, setVerif] = useState(false)
const [isLoading, setLoading] = useState(false)
async function onLogin() {
if (isPhone == "")
return toast.error('Please fill in completely')
const cek = await fetch('/api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ phone: isPhone })
})
const json = await cek.json()
console.log(json)
const code = Math.floor(Math.random() * 1000) + 1000
setLoading(true)
const res = await fetch(`https://wa.wibudev.com/code?nom=${json.phone}&text=${code}`).then(
async (res) => {
if (res.status == 200) {
setValPhone(json.phone)
setOTP(code)
setUser(json.id)
setVerif(true)
setLoading(false)
toast.success('OTP sent successfully')
} else {
toast.error('OTP not sent')
setLoading(false)
}
console.log("code", code)
}
)
} }
if (isVerif) return <ViewVerification otp={isOTP} phone={isValPhone} user={isUser} />
return ( return (
<> <>
<Box p={10}> <Box p={10}>
<LayoutLogin> <LayoutLogin>
<Stack pt={30}> <Stack pt={30}>
<Box p={10}> <Box p={10} ref={focusTrapRef}>
<TextInput <TextInput
styles={{ styles={{
input: { input: {
@@ -43,6 +87,7 @@ function ViewLogin() {
radius={30} radius={30}
leftSection={<Text>+62</Text>} leftSection={<Text>+62</Text>}
placeholder="XXX XXX XXX" placeholder="XXX XXX XXX"
onChange={(val) => { setPhone(val.target.value) }}
/> />
<Text fz={10} mt={10} c={WARNA.biruTua}> <Text fz={10} mt={10} c={WARNA.biruTua}>
{textInfo} {textInfo}
@@ -62,7 +107,10 @@ function ViewLogin() {
size="md" size="md"
radius={30} radius={30}
fullWidth fullWidth
onClick={onMasuk} loading={isLoading}
onClick={() => {
onLogin()
}}
> >
MASUK MASUK
</Button> </Button>

View File

@@ -1,16 +1,54 @@
"use client"; "use client";
import { LayoutLogin, WARNA } from "@/module/_global"; import { LayoutLogin, WARNA } from "@/module/_global";
import { Box, Button, PinInput, Stack, Text, Title } from "@mantine/core"; import { IVerification } from "@/types";
import { Anchor, Box, Button, Group, PinInput, Stack, Text, Title } from "@mantine/core";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import React from "react"; import React, { useState } from "react";
import toast from "react-hot-toast";
export default function ViewVerification() { export default function ViewVerification({ phone, otp, user }: IVerification) {
const router = useRouter(); const router = useRouter()
const [isOTP, setOTP] = useState(otp)
const [inputOTP, setInputOTP] = useState<any>()
const [isLoading, setLoading] = useState(false)
function onNext() { async function onResend() {
// router.push("/welcome"); const code = Math.floor(Math.random() * 1000) + 1000
window.location.href = "/welcome"
const res = await fetch(`https://wa.wibudev.com/code?nom=${phone}&text=${code}`)
.then(
async (res) => {
if (res.status == 200) {
toast.success('Verification code has been sent')
setOTP(code)
} else {
toast.error('Error')
}
}
);
} }
async function getVerification() {
setLoading(true)
if (isOTP == inputOTP) {
setLoading(false)
const res = await fetch('/api/auth/set-cookies', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ user: user })
})
router.push('/welcome')
toast.success("Verification code is correct")
setLoading(false)
} else {
toast.error("Verification code is incorrect")
setLoading(false)
}
}
return ( return (
<> <>
<Box p={10}> <Box p={10}>
@@ -24,7 +62,7 @@ export default function ViewVerification() {
Masukkan kode yang kami kirimkan melalui WhatsApp Masukkan kode yang kami kirimkan melalui WhatsApp
</Text> </Text>
<Text fz={12} c={WARNA.biruTua} fw={"bold"}> <Text fz={12} c={WARNA.biruTua} fw={"bold"}>
+6287701790942 {phone}
</Text> </Text>
<Box pt={30}> <Box pt={30}>
<PinInput <PinInput
@@ -39,6 +77,9 @@ export default function ViewVerification() {
borderColor: WARNA.biruTua, borderColor: WARNA.biruTua,
}, },
}} }}
onChange={(val) => {
setInputOTP(val)
}}
/> />
</Box> </Box>
<Box mt={40}> <Box mt={40}>
@@ -48,11 +89,22 @@ export default function ViewVerification() {
size="md" size="md"
radius={30} radius={30}
fullWidth fullWidth
onClick={onNext} onClick={() => { getVerification() }}
> >
Lanjut Lanjut
</Button> </Button>
</Box> </Box>
<Group justify="center" >
<Text fz={12} c={WARNA.biruTua}>
Didnt receive a code ? {""}
<Anchor c={WARNA.biruTua}
fz={12}
onClick={() => { onResend() }}
>
Resend
</Anchor>
</Text>
</Group>
</Box> </Box>
</Stack> </Stack>
</LayoutLogin> </LayoutLogin>

View File

@@ -1,3 +1,4 @@
"use client"
import { LayoutIconBack, LayoutNavbarHome, WARNA } from "@/module/_global"; import { LayoutIconBack, LayoutNavbarHome, WARNA } from "@/module/_global";
import { ActionIcon, Anchor, Box, Button, Flex, Group, Stack, Text } from "@mantine/core"; import { ActionIcon, Anchor, Box, Button, Flex, Group, Stack, Text } from "@mantine/core";
import { BsInfo } from "react-icons/bs"; import { BsInfo } from "react-icons/bs";
@@ -7,16 +8,34 @@ import { FaSquarePhone } from "react-icons/fa6";
import { MdEmail } from "react-icons/md"; import { MdEmail } from "react-icons/md";
import { InfoTitleProfile } from "../component/ui/ui_profile"; import { InfoTitleProfile } from "../component/ui/ui_profile";
import { IoMaleFemale } from "react-icons/io5"; import { IoMaleFemale } from "react-icons/io5";
import toast from "react-hot-toast";
import { LuLogOut } from "react-icons/lu";
import LayoutModal from "@/module/_global/layout/layout_modal";
import { useState } from "react";
export default function ViewProfile() { export default function ViewProfile() {
const [openModal, setOpenModal] = useState(false);
async function onLogout() {
try {
await fetch('/api/auth/logout', {
method: 'DELETE',
});
toast.success('Logout Success')
window.location.href = '/';
} catch (error) {
console.error(error);
}
}
return ( return (
<> <>
<LayoutNavbarHome> <LayoutNavbarHome>
<Group justify="space-between"> <Group justify="space-between">
<LayoutIconBack /> <LayoutIconBack />
{/* <ActionIcon variant="light" bg={WARNA.bgIcon} size="lg" radius="lg" aria-label="Info">
<BsInfo size={20} color='white' /> <ActionIcon onClick={() => { setOpenModal(true) }} variant="light" bg={WARNA.bgIcon} size="lg" radius="lg" aria-label="Info">
</ActionIcon> */} <LuLogOut size={20} color='white' />
</ActionIcon>
</Group> </Group>
<Stack <Stack
align="center" align="center"
@@ -60,6 +79,10 @@ export default function ViewProfile() {
</Group> </Group>
</Box> </Box>
<LayoutModal opened={openModal} onClose={() => setOpenModal(false)}
description="Apakah Anda yakin ingin Keluar?"
onYes={() => onLogout()} />
</> </>
) )
} }

View File

View File

@@ -1,5 +1,4 @@
export interface Login { export interface Login {
id: string id: string
email: string
phone: string phone: string
} }

View File

@@ -0,0 +1,5 @@
export interface Verification {
phone: string
otp: number
user: string
}

6
src/types/index.ts Normal file
View File

@@ -0,0 +1,6 @@
import { Login } from '@/types/auth/login';
import { Verification } from './auth/varification';
export type ILogin = Login
export type IVerification = Verification

View File

@@ -1072,6 +1072,11 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
cookie@0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051"
integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==
create-require@^1.1.0: create-require@^1.1.0:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
@@ -2081,6 +2086,20 @@ invariant@^2.2.4:
dependencies: dependencies:
loose-envify "^1.0.0" loose-envify "^1.0.0"
iron-session@^8.0.2:
version "8.0.2"
resolved "https://registry.yarnpkg.com/iron-session/-/iron-session-8.0.2.tgz#9802e080206a8ba41911b53d29ff7de11161d036"
integrity sha512-p4Yf1moQr6gnCcXu5vCaxVKRKDmR9PZcQDfp7ZOgbsSHUsgaNti6OgDB2BdgxC2aS6V/6Hu4O0wYlj92sbdIJg==
dependencies:
cookie "0.6.0"
iron-webcrypto "1.2.1"
uncrypto "0.1.3"
iron-webcrypto@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz#aa60ff2aa10550630f4c0b11fd2442becdb35a6f"
integrity sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==
is-arguments@^1.1.1: is-arguments@^1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b"
@@ -3527,6 +3546,11 @@ unbox-primitive@^1.0.2:
has-symbols "^1.0.3" has-symbols "^1.0.3"
which-boxed-primitive "^1.0.2" which-boxed-primitive "^1.0.2"
uncrypto@0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/uncrypto/-/uncrypto-0.1.3.tgz#e1288d609226f2d02d8d69ee861fa20d8348ef2b"
integrity sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==
undici-types@~5.26.4: undici-types@~5.26.4:
version "5.26.5" version "5.26.5"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"