From c2a050a03d5b6c66a02ed88ca189830ce01d4d71 Mon Sep 17 00:00:00 2001 From: lukman Date: Thu, 25 Jul 2024 10:06:55 +0800 Subject: [PATCH 1/4] feat :update module --- src/module/_global/bin/val_global.ts | 1 + src/module/_global/index.ts | 20 +++--- src/module/auth/login/view/view_login.tsx | 64 ++++++++++++++--- .../varification/view/view_verification.tsx | 70 ++++++++++++++++--- src/module/user/profile/view/view_profile.tsx | 29 +++++++- 5 files changed, 156 insertions(+), 28 deletions(-) create mode 100644 src/module/_global/bin/val_global.ts diff --git a/src/module/_global/bin/val_global.ts b/src/module/_global/bin/val_global.ts new file mode 100644 index 0000000..17bfaca --- /dev/null +++ b/src/module/_global/bin/val_global.ts @@ -0,0 +1 @@ +export const pwd_key_config = "fchgvjknlmdfnbvghhujlaknsdvjbhknlkmsdbdyu567t8y9u30r4587638y9uipkoeghjvuyi89ipkoefmnrjbhtiu4or9ipkoemnjfbhjiuoijdklnjhbviufojkejnshbiuojijknehgruyu" \ No newline at end of file diff --git a/src/module/_global/index.ts b/src/module/_global/index.ts index 60a43da..3755cf2 100644 --- a/src/module/_global/index.ts +++ b/src/module/_global/index.ts @@ -1,3 +1,5 @@ +import prisma from "./bin/prisma"; +import { pwd_key_config } from "./bin/val_global"; import { WARNA } from "./fun/WARNA"; import LayoutDrawer from "./layout/layout_drawer"; 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 ViewFilter from "./view/view_filter"; -export { WARNA } -export { LayoutLogin } -export { LayoutNavbarHome } -export { LayoutIconBack } -export { LoadingPage } -export { LayoutDrawer } -export { LayoutNavbarNew } -export { ViewFilter } \ No newline at end of file +export { WARNA }; +export { LayoutLogin }; +export { LayoutNavbarHome }; +export { LayoutIconBack }; +export { LoadingPage }; +export { LayoutDrawer }; +export { LayoutNavbarNew }; +export { ViewFilter }; +export { prisma }; +export { pwd_key_config }; diff --git a/src/module/auth/login/view/view_login.tsx b/src/module/auth/login/view/view_login.tsx index 9e03589..8c5eb23 100644 --- a/src/module/auth/login/view/view_login.tsx +++ b/src/module/auth/login/view/view_login.tsx @@ -12,24 +12,68 @@ import { Title, } from "@mantine/core"; 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() { + const focusTrapRef = useFocusTrap() const router = useRouter() const textInfo = "Kami akan mengirim kode verifikasi melalui WhatsApp, guna mengonfirmasikan nomor Anda."; - - function onMasuk() { - // router.push("/verification") - window.location.href = "/verification" + + const [isPhone, setPhone] = useState("") + const [isOTP, setOTP] = useState(null) + const [isValPhone, setValPhone] = useState(null) + const [isUser, setUser] = useState(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 + return ( <> - + +62} placeholder="XXX XXX XXX" + onChange={(val) => { setPhone(val.target.value) }} /> {textInfo} @@ -62,7 +107,10 @@ function ViewLogin() { size="md" radius={30} fullWidth - onClick={onMasuk} + loading={isLoading} + onClick={() => { + onLogin() + }} > MASUK diff --git a/src/module/auth/varification/view/view_verification.tsx b/src/module/auth/varification/view/view_verification.tsx index 7585341..d9583f8 100644 --- a/src/module/auth/varification/view/view_verification.tsx +++ b/src/module/auth/varification/view/view_verification.tsx @@ -1,16 +1,54 @@ "use client"; 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 React from "react"; +import React, { useState } from "react"; +import toast from "react-hot-toast"; -export default function ViewVerification() { - const router = useRouter(); +export default function ViewVerification({ phone, otp, user }: IVerification) { + const router = useRouter() + const [isOTP, setOTP] = useState(otp) + const [inputOTP, setInputOTP] = useState() + const [isLoading, setLoading] = useState(false) - function onNext() { - // router.push("/welcome"); - window.location.href = "/welcome" + async function onResend() { + const code = Math.floor(Math.random() * 1000) + 1000 + + 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 ( <> @@ -24,7 +62,7 @@ export default function ViewVerification() { Masukkan kode yang kami kirimkan melalui WhatsApp - +6287701790942 + {phone} { + setInputOTP(val) + }} /> @@ -48,11 +89,22 @@ export default function ViewVerification() { size="md" radius={30} fullWidth - onClick={onNext} + onClick={() => { getVerification() }} > Lanjut + + + Didnt receive a code ? {""} + { onResend() }} + > + Resend + + + diff --git a/src/module/user/profile/view/view_profile.tsx b/src/module/user/profile/view/view_profile.tsx index 724b1fb..50000b1 100644 --- a/src/module/user/profile/view/view_profile.tsx +++ b/src/module/user/profile/view/view_profile.tsx @@ -1,3 +1,4 @@ +"use client" import { LayoutIconBack, LayoutNavbarHome, WARNA } from "@/module/_global"; import { ActionIcon, Anchor, Box, Button, Flex, Group, Stack, Text } from "@mantine/core"; import { BsInfo } from "react-icons/bs"; @@ -7,16 +8,34 @@ import { FaSquarePhone } from "react-icons/fa6"; import { MdEmail } from "react-icons/md"; import { InfoTitleProfile } from "../component/ui/ui_profile"; 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() { + 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 ( <> - {/* - - */} + + { setOpenModal(true) }} variant="light" bg={WARNA.bgIcon} size="lg" radius="lg" aria-label="Info"> + + + + setOpenModal(false)} + description="Apakah Anda yakin ingin Keluar?" + onYes={() => onLogout()} /> ) } \ No newline at end of file From 6872fb51b28c02f45bff4c58255f274039386c1a Mon Sep 17 00:00:00 2001 From: lukman Date: Thu, 25 Jul 2024 10:07:26 +0800 Subject: [PATCH 2/4] feat : add api --- src/app/api/auth/get-user-by-cookies/route.ts | 18 ++++++++++++++++++ src/app/api/auth/login/route.ts | 9 +++++---- src/app/api/auth/logout/route.ts | 7 +++++++ src/app/api/auth/set-cookies/route.ts | 16 ++++++++++++++++ 4 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 src/app/api/auth/get-user-by-cookies/route.ts create mode 100644 src/app/api/auth/logout/route.ts create mode 100644 src/app/api/auth/set-cookies/route.ts diff --git a/src/app/api/auth/get-user-by-cookies/route.ts b/src/app/api/auth/get-user-by-cookies/route.ts new file mode 100644 index 0000000..add8676 --- /dev/null +++ b/src/app/api/auth/get-user-by-cookies/route.ts @@ -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); +} diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts index 2432ae7..519f463 100644 --- a/src/app/api/auth/login/route.ts +++ b/src/app/api/auth/login/route.ts @@ -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"; export async function POST(req: NextRequest) { - const { email }: Login = await req.json(); + const { phone }: ILogin = await req.json(); const user = await prisma.user.findUnique({ - where: { email, isActive: true }, + where: { phone, isActive: true }, select: { id: true, phone: true }, }); diff --git a/src/app/api/auth/logout/route.ts b/src/app/api/auth/logout/route.ts new file mode 100644 index 0000000..c17ef71 --- /dev/null +++ b/src/app/api/auth/logout/route.ts @@ -0,0 +1,7 @@ +import { cookies } from "next/headers"; + +export async function DELETE() { + cookies().delete('sessionCookie') + + return Response.json({ success: true }) +} \ No newline at end of file diff --git a/src/app/api/auth/set-cookies/route.ts b/src/app/api/auth/set-cookies/route.ts new file mode 100644 index 0000000..b201337 --- /dev/null +++ b/src/app/api/auth/set-cookies/route.ts @@ -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 }); +} From 2f8a9ccab1ea5407ca55dba09f484e9c7d4ad0b5 Mon Sep 17 00:00:00 2001 From: lukman Date: Thu, 25 Jul 2024 10:08:01 +0800 Subject: [PATCH 3/4] feat : update type --- src/types/.gitkeep | 0 src/types/auth/login.ts | 1 - src/types/auth/varification.ts | 5 +++++ src/types/index.ts | 6 ++++++ 4 files changed, 11 insertions(+), 1 deletion(-) delete mode 100644 src/types/.gitkeep create mode 100644 src/types/auth/varification.ts create mode 100644 src/types/index.ts diff --git a/src/types/.gitkeep b/src/types/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/types/auth/login.ts b/src/types/auth/login.ts index 17ca1cb..d847120 100644 --- a/src/types/auth/login.ts +++ b/src/types/auth/login.ts @@ -1,5 +1,4 @@ export interface Login { id: string - email: string phone: string } \ No newline at end of file diff --git a/src/types/auth/varification.ts b/src/types/auth/varification.ts new file mode 100644 index 0000000..f5946dd --- /dev/null +++ b/src/types/auth/varification.ts @@ -0,0 +1,5 @@ +export interface Verification { + phone: string + otp: number + user: string +} \ No newline at end of file diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..6be00a1 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,6 @@ +import { Login } from '@/types/auth/login'; +import { Verification } from './auth/varification'; + + +export type ILogin = Login +export type IVerification = Verification \ No newline at end of file From 404734c8f631297cffb594223b96f5c1c1c1ebc0 Mon Sep 17 00:00:00 2001 From: lukman Date: Thu, 25 Jul 2024 10:08:31 +0800 Subject: [PATCH 4/4] feat : add package --- api.http | 5 ++--- package.json | 1 + src/app/(auth)/verification/page.tsx | 5 +++-- yarn.lock | 24 ++++++++++++++++++++++++ 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/api.http b/api.http index a3ab361..1d69be2 100644 --- a/api.http +++ b/api.http @@ -1,8 +1,7 @@ ### POST http://localhost:3000/api/auth/login/ HTTP/1.1 Content-Type: application/json + { - "id": "devLukman", - "phone": "6287701790942", - "email": "lukman@bip.com" + "phone": "6287701790942" } \ No newline at end of file diff --git a/package.json b/package.json index fc054e1..89d446e 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "echarts-for-react": "^3.0.2", "embla-carousel-autoplay": "^7.1.0", "embla-carousel-react": "^7.1.0", + "iron-session": "^8.0.2", "lodash": "^4.17.21", "moment": "^2.30.1", "next": "14.2.4", diff --git a/src/app/(auth)/verification/page.tsx b/src/app/(auth)/verification/page.tsx index de28044..31b693d 100644 --- a/src/app/(auth)/verification/page.tsx +++ b/src/app/(auth)/verification/page.tsx @@ -1,6 +1,7 @@ import { ViewVerification } from "@/module/auth"; +import { IVerification } from "@/types"; import React from "react"; -export default function Verification() { - return ; +export default function Page() { + return ; } diff --git a/yarn.lock b/yarn.lock index 4d4e163..b8fcbd8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1072,6 +1072,11 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 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: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -2081,6 +2086,20 @@ invariant@^2.2.4: dependencies: 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: version "1.1.1" 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" 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: version "5.26.5" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"