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/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 });
+}
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
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
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"