diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml
index 311d660..552affd 100644
--- a/.gitea/workflows/ci.yml
+++ b/.gitea/workflows/ci.yml
@@ -1,28 +1,28 @@
-name: Bun CI
-
-on:
- push:
- branches: [main]
- pull_request:
- branches: [main]
-
-jobs:
- build-testing:
- runs-on: ubuntu-22.04
- container:
- image: oven/bun:latest
- steps:
- - name: Install Git
- run: apt update && apt install -y git
-
- - name: Clone private repo
- run: git clone "$GITEA_REPOSITORY_CLONE_URL" .
-
- - name: Install dependencies
- run: bun install
-
- - name: Running lint
- run: bun run lint
-
- - name: Running build
- run: bun run build
+name: Bun CI
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+jobs:
+ build-testing:
+ runs-on: ubuntu-22.04
+ container:
+ image: oven/bun:latest
+ steps:
+ - name: Install Git
+ run: apt update && apt install -y git
+
+ - name: Clone private repo
+ run: git clone "$GITEA_REPOSITORY_CLONE_URL" .
+
+ - name: Install dependencies
+ run: bun install
+
+ - name: Running lint
+ run: bun run lint
+
+ - name: Running build
+ run: bun run build
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d7e7ee2..dbe81b2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,31 +1,31 @@
-name: CI
-
-on:
- push:
- branches:
- - main
- pull_request:
- branches:
- - main
-
-jobs:
- build:
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v5
-
- - name: Install Bun
- uses: oven-sh/setup-bun@v2
- with:
- bun-version: latest
-
- - name: Install dependencies
- run: bun install
-
- - name: Run lint
- run: bun run lint
-
- - name: Run build
+name: CI
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v5
+
+ - name: Install Bun
+ uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
+
+ - name: Install dependencies
+ run: bun install
+
+ - name: Run lint
+ run: bun run lint
+
+ - name: Run build
run: bun run build
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index ba32ab2..56fe8b3 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,18 +1,18 @@
-image: oven/bun:latest
-
-stages:
- - lint
- - build
-
-before_script:
- - bun install
-
-lint:
- stage: lint
- script:
- - bun run lint
-
-build:
- stage: build
- script:
- - bun run build
+image: oven/bun:latest
+
+stages:
+ - lint
+ - build
+
+before_script:
+ - bun install
+
+lint:
+ stage: lint
+ script:
+ - bun run lint
+
+build:
+ stage: build
+ script:
+ - bun run build
diff --git a/app/(auth)/auth/callback/[...provider]/page.tsx b/app/(auth)/auth/callback/[...provider]/page.tsx
index faca01d..6ade1eb 100644
--- a/app/(auth)/auth/callback/[...provider]/page.tsx
+++ b/app/(auth)/auth/callback/[...provider]/page.tsx
@@ -1,8 +1,8 @@
-import OauthCallbackHandler from "@/features/oauth-callback/pages/callbackHandler";
-import React from "react";
-
-const page = () => {
- return ;
-};
-
-export default page;
+import OauthCallbackHandler from "@/features/oauth-callback/pages/callbackHandler";
+import React from "react";
+
+const page = () => {
+ return ;
+};
+
+export default page;
diff --git a/app/(auth)/login/metadata.tsx b/app/(auth)/login/metadata.tsx
index 3ca9314..da139ce 100644
--- a/app/(auth)/login/metadata.tsx
+++ b/app/(auth)/login/metadata.tsx
@@ -1,5 +1,5 @@
-import { Metadata } from "next";
-
-export const metadata: Metadata = {
- title: "Login | Nounoz TV",
-};
+import { Metadata } from "next";
+
+export const metadata: Metadata = {
+ title: "Login | Nounoz TV",
+};
diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx
index 71628a2..dd75146 100644
--- a/app/(auth)/login/page.tsx
+++ b/app/(auth)/login/page.tsx
@@ -1,11 +1,11 @@
-import LoginPage from "@/features/auth/pages/LoginPage";
-import { metadata } from "./metadata";
-export { metadata };
-
-import React from "react";
-
-const page = () => {
- return ;
-};
-
-export default page;
+import LoginPage from "@/features/auth/pages/LoginPage";
+import { metadata } from "./metadata";
+export { metadata };
+
+import React from "react";
+
+const page = () => {
+ return ;
+};
+
+export default page;
diff --git a/app/(auth)/signup/metadata.tsx b/app/(auth)/signup/metadata.tsx
index 77bb0d4..3971bf7 100644
--- a/app/(auth)/signup/metadata.tsx
+++ b/app/(auth)/signup/metadata.tsx
@@ -1,5 +1,5 @@
-import { Metadata } from "next";
-
-export const metadata: Metadata = {
- title: "Sign Up | Nounoz TV",
-};
+import { Metadata } from "next";
+
+export const metadata: Metadata = {
+ title: "Sign Up | Nounoz TV",
+};
diff --git a/app/(auth)/signup/page.tsx b/app/(auth)/signup/page.tsx
index fb33e99..ca4dc82 100644
--- a/app/(auth)/signup/page.tsx
+++ b/app/(auth)/signup/page.tsx
@@ -1,11 +1,11 @@
-import SignupPage from "@/features/auth/pages/SignupPage";
-import { metadata } from "./metadata";
-export { metadata };
-
-import React from "react";
-
-const page = () => {
- return ;
-};
-
-export default page;
+import SignupPage from "@/features/auth/pages/SignupPage";
+import { metadata } from "./metadata";
+export { metadata };
+
+import React from "react";
+
+const page = () => {
+ return ;
+};
+
+export default page;
diff --git a/app/(main)/(home)/button.tsx b/app/(main)/(home)/button.tsx
index 1048ff9..f549df8 100644
--- a/app/(main)/(home)/button.tsx
+++ b/app/(main)/(home)/button.tsx
@@ -1,10 +1,10 @@
-"use client";
-
-import { Button } from "@heroui/react";
-import React from "react";
-
-const button = () => {
- return Button ;
-};
-
-export default button;
+"use client";
+
+import { Button } from "@heroui/react";
+import React from "react";
+
+const button = () => {
+ return Button ;
+};
+
+export default button;
diff --git a/app/(main)/(home)/metadata.tsx b/app/(main)/(home)/metadata.tsx
index 6a4230a..6469e7d 100644
--- a/app/(main)/(home)/metadata.tsx
+++ b/app/(main)/(home)/metadata.tsx
@@ -1,39 +1,39 @@
-import { Metadata } from "next";
-
-export const metadata: Metadata = {
- title: "Nounoz TV - Anime Streaming Station Center",
- description:
- "Nounoz TV adalah tempat santai buat nonton anime kualitas tinggi tanpa ribet. Didukung komunitas yang aktif dan ramah, kamu nggak cuma nonton—tapi juga bisa ngobrol, sharing, dan seru-seruan bareng.",
- keywords: [
- "nonton anime",
- "streaming anime",
- "anime sub indo",
- "anime HD",
- "komunitas anime",
- "Nounoz TV",
- ],
- openGraph: {
- title: "Nounoz TV - Streaming Anime HD + Komunitas Asik",
- description:
- "Nonton anime jadi lebih seru bareng teman-teman. Kualitas jernih, tanpa iklan ganggu, dan selalu update!",
- url: "https://nounoz.tv",
- siteName: "Nounoz TV",
- images: [
- {
- url: "https://nounoz.tv/og-image.jpg",
- width: 1200,
- height: 630,
- alt: "Nounoz TV - Nonton Anime HD Bareng Komunitas",
- },
- ],
- locale: "id_ID",
- type: "website",
- },
- twitter: {
- card: "summary_large_image",
- title: "Nounoz TV - Nonton Anime HD Bareng Komunitas",
- description:
- "Streaming anime kualitas tinggi sambil ngobrol santai bareng komunitas yang aktif dan suportif.",
- images: ["https://nounoz.tv/og-image.jpg"],
- },
-};
+import { Metadata } from "next";
+
+export const metadata: Metadata = {
+ title: "Nounoz TV - Anime Streaming Station Center",
+ description:
+ "Nounoz TV adalah tempat santai buat nonton anime kualitas tinggi tanpa ribet. Didukung komunitas yang aktif dan ramah, kamu nggak cuma nonton—tapi juga bisa ngobrol, sharing, dan seru-seruan bareng.",
+ keywords: [
+ "nonton anime",
+ "streaming anime",
+ "anime sub indo",
+ "anime HD",
+ "komunitas anime",
+ "Nounoz TV",
+ ],
+ openGraph: {
+ title: "Nounoz TV - Streaming Anime HD + Komunitas Asik",
+ description:
+ "Nonton anime jadi lebih seru bareng teman-teman. Kualitas jernih, tanpa iklan ganggu, dan selalu update!",
+ url: "https://nounoz.tv",
+ siteName: "Nounoz TV",
+ images: [
+ {
+ url: "https://nounoz.tv/og-image.jpg",
+ width: 1200,
+ height: 630,
+ alt: "Nounoz TV - Nonton Anime HD Bareng Komunitas",
+ },
+ ],
+ locale: "id_ID",
+ type: "website",
+ },
+ twitter: {
+ card: "summary_large_image",
+ title: "Nounoz TV - Nonton Anime HD Bareng Komunitas",
+ description:
+ "Streaming anime kualitas tinggi sambil ngobrol santai bareng komunitas yang aktif dan suportif.",
+ images: ["https://nounoz.tv/og-image.jpg"],
+ },
+};
diff --git a/app/(main)/explore/page.tsx b/app/(main)/explore/page.tsx
index 2939c5c..f1fae19 100644
--- a/app/(main)/explore/page.tsx
+++ b/app/(main)/explore/page.tsx
@@ -1,7 +1,7 @@
-import React from "react";
-
-const page = () => {
- return
Explore Page
;
-};
-
-export default page;
+import React from "react";
+
+const page = () => {
+ return Explore Page
;
+};
+
+export default page;
diff --git a/app/(main)/genres/page.tsx b/app/(main)/genres/page.tsx
index 56d0140..f7900c9 100644
--- a/app/(main)/genres/page.tsx
+++ b/app/(main)/genres/page.tsx
@@ -1,7 +1,7 @@
-import React from "react";
-
-const page = () => {
- return Genre Page
;
-};
-
-export default page;
+import React from "react";
+
+const page = () => {
+ return Genre Page
;
+};
+
+export default page;
diff --git a/app/(main)/layout.tsx b/app/(main)/layout.tsx
index 84daa17..2b680ca 100644
--- a/app/(main)/layout.tsx
+++ b/app/(main)/layout.tsx
@@ -1,13 +1,13 @@
-import NavbarUI from "@/widgets/navbar/ui/Navbar";
-import React from "react";
-
-const mainLayout = ({ children }: Readonly<{ children: React.ReactNode }>) => {
- return (
-
-
- {children}
-
- );
-};
-
-export default mainLayout;
+import NavbarUI from "@/widgets/navbar/ui/Navbar";
+import React from "react";
+
+const mainLayout = ({ children }: Readonly<{ children: React.ReactNode }>) => {
+ return (
+
+
+ {children}
+
+ );
+};
+
+export default mainLayout;
diff --git a/app/(main)/schedule/page.tsx b/app/(main)/schedule/page.tsx
index bc18a2d..9ca7f29 100644
--- a/app/(main)/schedule/page.tsx
+++ b/app/(main)/schedule/page.tsx
@@ -1,7 +1,7 @@
-import React from "react";
-
-const page = () => {
- return Schedule Page
;
-};
-
-export default page;
+import React from "react";
+
+const page = () => {
+ return Schedule Page
;
+};
+
+export default page;
diff --git a/app/(main)/trending/page.tsx b/app/(main)/trending/page.tsx
index fcb174d..60f8285 100644
--- a/app/(main)/trending/page.tsx
+++ b/app/(main)/trending/page.tsx
@@ -1,7 +1,7 @@
-import React from "react";
-
-const page = () => {
- return Trending Page
;
-};
-
-export default page;
+import React from "react";
+
+const page = () => {
+ return Trending Page
;
+};
+
+export default page;
diff --git a/app/globals.css b/app/globals.css
index 5d70e21..1a2ec20 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -1,5 +1,5 @@
-@import "tailwindcss";
-@plugin './hero.ts';
-/* Note: You may need to change the path to fit your project structure */
-@source '../node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}';
-@custom-variant dark (&:is(.dark *));
+@import "tailwindcss";
+@plugin './hero.ts';
+/* Note: You may need to change the path to fit your project structure */
+@source '../node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}';
+@custom-variant dark (&:is(.dark *));
diff --git a/app/hero.ts b/app/hero.ts
index 4401c38..211033b 100644
--- a/app/hero.ts
+++ b/app/hero.ts
@@ -1,5 +1,5 @@
-// hero.ts
-import { heroui } from "@heroui/react";
-// or import from theme package if you are using individual packages.
-// import { heroui } from "@heroui/theme";
-export default heroui();
+// hero.ts
+import { heroui } from "@heroui/react";
+// or import from theme package if you are using individual packages.
+// import { heroui } from "@heroui/theme";
+export default heroui();
diff --git a/features/auth/lib/getOauthProviderList.ts b/features/auth/lib/getOauthProviderList.ts
index 9dfdef1..b1be391 100644
--- a/features/auth/lib/getOauthProviderList.ts
+++ b/features/auth/lib/getOauthProviderList.ts
@@ -1,13 +1,13 @@
-"use server";
-import { api } from "@/shared/lib/ky/connector";
-
-const getOauthProviderList = async () => {
- try {
- const res = await api.get(`auth/providers`);
- return res.json();
- } catch (error) {
- throw new Error("Failed to fetch OAuth providers", { cause: error });
- }
-};
-
-export default getOauthProviderList;
+"use server";
+import { api } from "@/shared/lib/ky/connector";
+
+const getOauthProviderList = async () => {
+ try {
+ const res = await api.get(`auth/providers`);
+ return res.json();
+ } catch (error) {
+ throw new Error("Failed to fetch OAuth providers", { cause: error });
+ }
+};
+
+export default getOauthProviderList;
diff --git a/features/auth/lib/requestOauthUrl.ts b/features/auth/lib/requestOauthUrl.ts
index 0b38398..9068060 100644
--- a/features/auth/lib/requestOauthUrl.ts
+++ b/features/auth/lib/requestOauthUrl.ts
@@ -1,36 +1,36 @@
-"use server";
-
-import { api } from "@/shared/lib/ky/connector";
-import { redirect } from "next/navigation";
-import { ResponseRequestOauthUrl } from "../types/responseRequestOauthUrl";
-
-const requestOauthUrl = async (providerData: {
- name: string;
- endpoint: string;
-}) => {
- // Check if requestEndpoint is provided, if not throw an error
- if (!providerData.endpoint)
- throw new Error("oAuth endpoint request not found");
-
- // Define a variable to hold the OAuth data
- let oauthData: Promise;
-
- // Fetch OAuth data from the API
- try {
- const response = await api.get(providerData.endpoint, {
- searchParams: {
- callback: `${
- process.env.APP_DOMAIN
- }/auth/callback/${providerData.name.toLocaleLowerCase()}`,
- },
- });
- oauthData = response.json();
- } catch (error) {
- throw new Error(JSON.stringify(error));
- }
-
- // Redirect to the OAuth provider's authorization page
- redirect((await oauthData).data);
-};
-
-export default requestOauthUrl;
+"use server";
+
+import { api } from "@/shared/lib/ky/connector";
+import { redirect } from "next/navigation";
+import { ResponseRequestOauthUrl } from "../types/responseRequestOauthUrl";
+
+const requestOauthUrl = async (providerData: {
+ name: string;
+ endpoint: string;
+}) => {
+ // Check if requestEndpoint is provided, if not throw an error
+ if (!providerData.endpoint)
+ throw new Error("oAuth endpoint request not found");
+
+ // Define a variable to hold the OAuth data
+ let oauthData: Promise;
+
+ // Fetch OAuth data from the API
+ try {
+ const response = await api.get(providerData.endpoint, {
+ searchParams: {
+ callback: `${
+ process.env.APP_DOMAIN
+ }/auth/callback/${providerData.name.toLocaleLowerCase()}`,
+ },
+ });
+ oauthData = response.json();
+ } catch (error) {
+ throw new Error(JSON.stringify(error));
+ }
+
+ // Redirect to the OAuth provider's authorization page
+ redirect((await oauthData).data);
+};
+
+export default requestOauthUrl;
diff --git a/features/auth/lib/submitRegisterForm.ts b/features/auth/lib/submitRegisterForm.ts
index f6dddee..6ddcd47 100644
--- a/features/auth/lib/submitRegisterForm.ts
+++ b/features/auth/lib/submitRegisterForm.ts
@@ -1,27 +1,27 @@
-"use server";
-
-import { apiErrorHandler } from "@/shared/lib/ky/errorHandler";
-import { RegisterInputs } from "../ui/components/ProvisionInput";
-import { ServerRequestCallback } from "@/shared/types/ServerRequestCallback";
-
-export const submitRegisterForm = async (
- data: RegisterInputs
-): Promise => {
- if (data.password !== data.confirmPassword)
- return apiErrorHandler([], {
- success: false,
- status: 400,
- text: { message: "Password and Confirm Password do not match" },
- });
-
- try {
- await new Promise((resolve) => setTimeout(resolve, 3000));
- return {
- success: true,
- status: 200,
- text: { message: "Registration successful" },
- };
- } catch (error) {
- return apiErrorHandler(error);
- }
-};
+"use server";
+
+import { apiErrorHandler } from "@/shared/lib/ky/errorHandler";
+import { RegisterInputs } from "../ui/components/ProvisionInput";
+import { ServerRequestCallback } from "@/shared/types/ServerRequestCallback";
+
+export const submitRegisterForm = async (
+ data: RegisterInputs
+): Promise => {
+ if (data.password !== data.confirmPassword)
+ return apiErrorHandler([], {
+ success: false,
+ status: 400,
+ text: { message: "Password and Confirm Password do not match" },
+ });
+
+ try {
+ await new Promise((resolve) => setTimeout(resolve, 3000));
+ return {
+ success: true,
+ status: 200,
+ text: { message: "Registration successful" },
+ };
+ } catch (error) {
+ return apiErrorHandler(error);
+ }
+};
diff --git a/features/auth/models/registerForm.schema.ts b/features/auth/models/registerForm.schema.ts
new file mode 100644
index 0000000..8061864
--- /dev/null
+++ b/features/auth/models/registerForm.schema.ts
@@ -0,0 +1,21 @@
+import { z } from "zod";
+
+export const registerFormSchema = z
+ .object({
+ fullname: z.string().min(1, "Full name is required"),
+ email: z.email("Invalid email address"),
+ password: z
+ .string()
+ .min(8, "Password must be at least 8 characters long")
+ .max(25, "Password must be at most 25 characters long"),
+ confirmPassword: z
+ .string()
+ .min(8, "Password must be at least 8 characters long")
+ .max(25, "Password must be at most 25 characters long"),
+ })
+ .refine((data) => data.password === data.confirmPassword, {
+ message: "Passwords confirmation does not match",
+ path: ["confirmPassword"],
+ });
+
+export type RegisterFormSchema = z.infer;
diff --git a/features/auth/pages/LoginPage.tsx b/features/auth/pages/LoginPage.tsx
index 0456b53..77054b2 100644
--- a/features/auth/pages/LoginPage.tsx
+++ b/features/auth/pages/LoginPage.tsx
@@ -1,53 +1,53 @@
-"use client";
-
-import { redirect } from "next/navigation";
-import React, { useEffect, useState } from "react";
-import Login from "@/features/auth/ui/cards/Login";
-import SecurityCheckup from "@/shared/auth/ui/SecurityCheckup";
-import SecurityCheckupFailed from "@/shared/auth/ui/SecurityCheckupFailed";
-
-const LoginPage = () => {
- /**
- * Create a lit component that will be used in popp, consisting of 3 component flows:
- * 1. When the user opens it, a browser environment check will be performed.
- * 2. If it passes, the login component will appear and the user will perform authentication.
- * 3. If it fails, stop the authentication process and display the warning component, then return the user to the homepage.
- */
- const componentFlowList = {
- securityCheckup: ,
- securityCheckupFailed: ,
- SecurityCheckupSuccessed: ,
- };
-
- // State to set the current page component
- const [componentFlow, setComponentFlow] = useState(
- componentFlowList.securityCheckup
- );
-
- useEffect(() => {
- /**
- * Check if the window has an opener (i.e., it was opened by another window)
- * If it does, the security checkup has passed.
- * If it doesn't, the security checkup has failed and user will be redirected to the homepage.
- */
- if (window.opener) {
- setComponentFlow(componentFlowList.SecurityCheckupSuccessed);
- } else {
- setComponentFlow(componentFlowList.securityCheckupFailed);
-
- const timer = setTimeout(() => {
- redirect("/");
- }, 5000);
- return () => clearTimeout(timer);
- }
- }, []);
-
- return (
- <>
- {/* show the current component flow */}
- {componentFlow}
- >
- );
-};
-
-export default LoginPage;
+"use client";
+
+import { redirect } from "next/navigation";
+import React, { useEffect, useState } from "react";
+import Login from "@/features/auth/ui/cards/Login";
+import SecurityCheckup from "@/shared/auth/ui/SecurityCheckup";
+import SecurityCheckupFailed from "@/shared/auth/ui/SecurityCheckupFailed";
+
+const LoginPage = () => {
+ /**
+ * Create a lit component that will be used in popp, consisting of 3 component flows:
+ * 1. When the user opens it, a browser environment check will be performed.
+ * 2. If it passes, the login component will appear and the user will perform authentication.
+ * 3. If it fails, stop the authentication process and display the warning component, then return the user to the homepage.
+ */
+ const componentFlowList = {
+ securityCheckup: ,
+ securityCheckupFailed: ,
+ SecurityCheckupSuccessed: ,
+ };
+
+ // State to set the current page component
+ const [componentFlow, setComponentFlow] = useState(
+ componentFlowList.securityCheckup
+ );
+
+ useEffect(() => {
+ /**
+ * Check if the window has an opener (i.e., it was opened by another window)
+ * If it does, the security checkup has passed.
+ * If it doesn't, the security checkup has failed and user will be redirected to the homepage.
+ */
+ if (window.opener) {
+ setComponentFlow(componentFlowList.SecurityCheckupSuccessed);
+ } else {
+ setComponentFlow(componentFlowList.securityCheckupFailed);
+
+ const timer = setTimeout(() => {
+ redirect("/");
+ }, 5000);
+ return () => clearTimeout(timer);
+ }
+ }, []);
+
+ return (
+ <>
+ {/* show the current component flow */}
+ {componentFlow}
+ >
+ );
+};
+
+export default LoginPage;
diff --git a/features/auth/pages/SignupPage.tsx b/features/auth/pages/SignupPage.tsx
index 257bae8..e87c23f 100644
--- a/features/auth/pages/SignupPage.tsx
+++ b/features/auth/pages/SignupPage.tsx
@@ -1,51 +1,51 @@
-"use client";
-
-import { redirect } from "next/navigation";
-import React, { JSX, useEffect, useState } from "react";
-import SecurityCheckup from "@/shared/auth/ui/SecurityCheckup";
-import SecurityCheckupFailed from "@/shared/auth/ui/SecurityCheckupFailed";
-import Signup from "../ui/cards/Signup";
-
-const SignupPage = () => {
- // State to set the current page component
- const [componentFlow, setComponentFlow] = useState(
-
- );
-
- /**
- * Create a lit component that will be used in popp, consisting of 3 component flows:
- * 1. If it passes, the login component will appear and the user will perform authentication.
- * 2. If it fails, stop the authentication process and display the warning component, then return the user to the homepage.
- */
- const componentFlowList = {
- securityCheckupFailed: ,
- SecurityCheckupSuccessed: ,
- };
-
- useEffect(() => {
- /**
- * Check if the window has an opener (i.e., it was opened by another window)
- * If it does, the security checkup has passed.
- * If it doesn't, the security checkup has failed and user will be redirected to the homepage.
- */
- if (window.opener) {
- setComponentFlow(componentFlowList.SecurityCheckupSuccessed);
- } else {
- setComponentFlow(componentFlowList.securityCheckupFailed);
-
- const timer = setTimeout(() => {
- redirect("/");
- }, 5000);
- return () => clearTimeout(timer);
- }
- }, []);
-
- return (
- <>
- {/* show the current component flow */}
- {componentFlow}
- >
- );
-};
-
-export default SignupPage;
+"use client";
+
+import { redirect } from "next/navigation";
+import React, { JSX, useEffect, useState } from "react";
+import SecurityCheckup from "@/shared/auth/ui/SecurityCheckup";
+import SecurityCheckupFailed from "@/shared/auth/ui/SecurityCheckupFailed";
+import Signup from "../ui/cards/Signup";
+
+const SignupPage = () => {
+ // State to set the current page component
+ const [componentFlow, setComponentFlow] = useState(
+
+ );
+
+ /**
+ * Create a lit component that will be used in popp, consisting of 3 component flows:
+ * 1. If it passes, the login component will appear and the user will perform authentication.
+ * 2. If it fails, stop the authentication process and display the warning component, then return the user to the homepage.
+ */
+ const componentFlowList = {
+ securityCheckupFailed: ,
+ SecurityCheckupSuccessed: ,
+ };
+
+ useEffect(() => {
+ /**
+ * Check if the window has an opener (i.e., it was opened by another window)
+ * If it does, the security checkup has passed.
+ * If it doesn't, the security checkup has failed and user will be redirected to the homepage.
+ */
+ if (window.opener) {
+ setComponentFlow(componentFlowList.SecurityCheckupSuccessed);
+ } else {
+ setComponentFlow(componentFlowList.securityCheckupFailed);
+
+ const timer = setTimeout(() => {
+ redirect("/");
+ }, 5000);
+ return () => clearTimeout(timer);
+ }
+ }, []);
+
+ return (
+ <>
+ {/* show the current component flow */}
+ {componentFlow}
+ >
+ );
+};
+
+export default SignupPage;
diff --git a/features/auth/types/oauthProvidersList.ts b/features/auth/types/oauthProvidersList.ts
index 1704e03..cb19c6f 100644
--- a/features/auth/types/oauthProvidersList.ts
+++ b/features/auth/types/oauthProvidersList.ts
@@ -1,8 +1,8 @@
-export interface OauthProviders {
- name: string;
- icon: string;
- req_endpoint: string;
- client_id: string | undefined;
- client_secret: string | undefined;
- client_callback: string;
-}
+export interface OauthProviders {
+ name: string;
+ icon: string;
+ req_endpoint: string;
+ client_id: string | undefined;
+ client_secret: string | undefined;
+ client_callback: string;
+}
diff --git a/features/auth/types/responseRequestOauthUrl.ts b/features/auth/types/responseRequestOauthUrl.ts
index af7d111..a060597 100644
--- a/features/auth/types/responseRequestOauthUrl.ts
+++ b/features/auth/types/responseRequestOauthUrl.ts
@@ -1,5 +1,5 @@
-export interface ResponseRequestOauthUrl {
- data: string;
- message: string;
- status: string;
-}
+export interface ResponseRequestOauthUrl {
+ data: string;
+ message: string;
+ status: string;
+}
diff --git a/features/auth/ui/cards/Login.tsx b/features/auth/ui/cards/Login.tsx
index 6a605ba..32ccb1e 100644
--- a/features/auth/ui/cards/Login.tsx
+++ b/features/auth/ui/cards/Login.tsx
@@ -1,42 +1,42 @@
-"use client";
-
-import React from "react";
-import { Divider, Link } from "@heroui/react";
-import { routes } from "@/shared/config/routes";
-import EmailInput from "../components/EmailInput";
-import OAuthProviders from "../components/OAuthProviders";
-
-const Login = () => {
- return (
-
-
Welcome back
-
- {/* Email form */}
-
-
-
-
- {/* Sign up link */}
-
- Don't have an account?{" "}
-
- Sign Up
-
-
-
- {/* Divider between email form and third-party login options */}
-
-
- {/* Buttons for third-party login options */}
-
-
-
-
- );
-};
-
-export default Login;
+"use client";
+
+import React from "react";
+import { Divider, Link } from "@heroui/react";
+import { routes } from "@/shared/config/routes";
+import EmailInput from "../components/EmailInput";
+import OAuthProviders from "../components/OAuthProviders";
+
+const Login = () => {
+ return (
+
+
Welcome back
+
+ {/* Email form */}
+
+
+
+
+ {/* Sign up link */}
+
+ Don't have an account?{" "}
+
+ Sign Up
+
+
+
+ {/* Divider between email form and third-party login options */}
+
+
+ {/* Buttons for third-party login options */}
+
+
+
+
+ );
+};
+
+export default Login;
diff --git a/features/auth/ui/cards/Provision.tsx b/features/auth/ui/cards/Provision.tsx
index 9b354d6..5b01c9d 100644
--- a/features/auth/ui/cards/Provision.tsx
+++ b/features/auth/ui/cards/Provision.tsx
@@ -1,22 +1,22 @@
-"use client";
-
-import React from "react";
-import ProvisionInput from "../components/ProvisionInput";
-
-type Props = {
- fullName: string;
-};
-
-const Provision = ({ fullName }: Props) => {
- return (
-
-
Hey, {fullName.split(" ")[0]}
-
- Just a few more steps to join the fun!
-
-
-
- );
-};
-
-export default Provision;
+"use client";
+
+import React from "react";
+import ProvisionInput from "../components/ProvisionInput";
+
+type Props = {
+ fullName: string;
+};
+
+const Provision = ({ fullName }: Props) => {
+ return (
+
+
Hey, {fullName.split(" ")[0]}
+
+ Just a few more steps to join the fun!
+
+
+
+ );
+};
+
+export default Provision;
diff --git a/features/auth/ui/cards/Signup.tsx b/features/auth/ui/cards/Signup.tsx
index fb7f4a2..7cf8566 100644
--- a/features/auth/ui/cards/Signup.tsx
+++ b/features/auth/ui/cards/Signup.tsx
@@ -1,46 +1,46 @@
-"use client";
-
-import React from "react";
-import { Divider, Link } from "@heroui/react";
-import { routes } from "@/shared/config/routes";
-import OAuthProviders from "../components/OAuthProviders";
-import FullNameInput from "../components/FullNameInput";
-
-type Props = {
- changeCurrentPage: React.Dispatch>;
-};
-
-const Signup = ({ changeCurrentPage }: Props) => {
- return (
-
-
Create an account
-
- {/* Email form */}
-
-
-
-
- {/* Sign up link */}
-
- Already have an account?{" "}
-
- Log in
-
-
-
- {/* Divider between email form and third-party login options */}
-
-
- {/* Buttons for third-party login options */}
-
-
-
-
- );
-};
-
-export default Signup;
+"use client";
+
+import React from "react";
+import { Divider, Link } from "@heroui/react";
+import { routes } from "@/shared/config/routes";
+import OAuthProviders from "../components/OAuthProviders";
+import FullNameInput from "../components/FullNameInput";
+
+type Props = {
+ changeCurrentPage: React.Dispatch>;
+};
+
+const Signup = ({ changeCurrentPage }: Props) => {
+ return (
+
+
Create an account
+
+ {/* Email form */}
+
+
+
+
+ {/* Sign up link */}
+
+ Already have an account?{" "}
+
+ Log in
+
+
+
+ {/* Divider between email form and third-party login options */}
+
+
+ {/* Buttons for third-party login options */}
+
+
+
+
+ );
+};
+
+export default Signup;
diff --git a/features/auth/ui/components/EmailInput.tsx b/features/auth/ui/components/EmailInput.tsx
index 053f5ef..606c6ae 100644
--- a/features/auth/ui/components/EmailInput.tsx
+++ b/features/auth/ui/components/EmailInput.tsx
@@ -1,26 +1,26 @@
-"use client";
-
-import { Button, Input } from "@heroui/react";
-import React from "react";
-
-const EmailInput = () => {
- return (
- <>
-
-
- Continue
-
- >
- );
-};
-
-export default EmailInput;
+"use client";
+
+import { Button, Input } from "@heroui/react";
+import React from "react";
+
+const EmailInput = () => {
+ return (
+ <>
+
+
+ Continue
+
+ >
+ );
+};
+
+export default EmailInput;
diff --git a/features/auth/ui/components/FullNameInput.tsx b/features/auth/ui/components/FullNameInput.tsx
index 088593a..048336e 100644
--- a/features/auth/ui/components/FullNameInput.tsx
+++ b/features/auth/ui/components/FullNameInput.tsx
@@ -1,38 +1,38 @@
-"use client";
-
-import React, { useState } from "react";
-import { Button, Input } from "@heroui/react";
-import Provision from "../cards/Provision";
-
-type Props = {
- changeCurrentPage: React.Dispatch>;
-};
-
-const FullNameInput = ({ changeCurrentPage }: Props) => {
- const [fullName, setFullName] = useState("");
-
- return (
- <>
- setFullName(e.target.value)}
- classNames={{
- input: "text-md font-light pt-4",
- inputWrapper: "flex gap-10",
- }}
- />
- changeCurrentPage( )}
- className="mt-2 w-full"
- color="primary"
- >
- Continue
-
- >
- );
-};
-
-export default FullNameInput;
+"use client";
+
+import React, { useState } from "react";
+import { Button, Input } from "@heroui/react";
+import Provision from "../cards/Provision";
+
+type Props = {
+ changeCurrentPage: React.Dispatch>;
+};
+
+const FullNameInput = ({ changeCurrentPage }: Props) => {
+ const [fullName, setFullName] = useState("");
+
+ return (
+ <>
+ setFullName(e.target.value)}
+ classNames={{
+ input: "text-md font-light pt-4",
+ inputWrapper: "flex gap-10",
+ }}
+ />
+ changeCurrentPage( )}
+ className="mt-2 w-full"
+ color="primary"
+ >
+ Continue
+
+ >
+ );
+};
+
+export default FullNameInput;
diff --git a/features/auth/ui/components/OAuthProviders.tsx b/features/auth/ui/components/OAuthProviders.tsx
index 1bfbb4e..2072282 100644
--- a/features/auth/ui/components/OAuthProviders.tsx
+++ b/features/auth/ui/components/OAuthProviders.tsx
@@ -1,85 +1,85 @@
-"use client";
-
-import React, { useEffect, useState } from "react";
-import { OauthProviders } from "../../types/oauthProvidersList";
-import { ResponseRequestOauthUrl } from "../../types/responseRequestOauthUrl";
-import { Button } from "@heroui/react";
-import { Icon } from "@iconify/react";
-import getOauthProviderList from "../../lib/getOauthProviderList";
-import requestOauthUrl from "../../lib/requestOauthUrl";
-
-const OAuthProviders = () => {
- // Set initial state for OAuth providers list
- const [oauthProvidersList, setOauthProvidersList] = useState<
- OauthProviders[]
- >([]);
-
- /**
- * Fetch the list of OAuth providers from backend API
- * and update the state if OAuth providers list is available
- */
- useEffect(() => {
- (async () => {
- try {
- const res = (await getOauthProviderList()) as OauthProviders[];
- setOauthProvidersList(res);
- } catch (err) {
- console.error(err);
- }
- })();
- }, []);
-
- const [loadingButton, setLoadingButton] = useState(false);
-
- /**
- * Start the authentication process using oAuth by sending the endpoint URL to the backend for processing.
- *
- * @param providerRequestEndpoint The request endpoint for the OAuth provider
- */
- const startOauthProcess = async (providerData: {
- name: string;
- endpoint: string;
- }) => {
- try {
- setLoadingButton(true);
-
- (await requestOauthUrl(providerData)) as ResponseRequestOauthUrl;
- } catch (err) {
- setLoadingButton(false);
- console.error(err);
- }
- };
-
- return (
-
- {/* Render OAuth provider buttons */}
- {oauthProvidersList.length > 0 ? (
- oauthProvidersList.map((provider, index) => {
- return (
- }
- onPress={() =>
- startOauthProcess({
- name: provider.name,
- endpoint: provider.req_endpoint,
- })
- }
- isLoading={loadingButton}
- >
- Continue with {provider.name}
-
- );
- })
- ) : (
-
- No login options available via third-party providers
-
- )}
-
- );
-};
-
-export default OAuthProviders;
+"use client";
+
+import React, { useEffect, useState } from "react";
+import { OauthProviders } from "../../types/oauthProvidersList";
+import { ResponseRequestOauthUrl } from "../../types/responseRequestOauthUrl";
+import { Button } from "@heroui/react";
+import { Icon } from "@iconify/react";
+import getOauthProviderList from "../../lib/getOauthProviderList";
+import requestOauthUrl from "../../lib/requestOauthUrl";
+
+const OAuthProviders = () => {
+ // Set initial state for OAuth providers list
+ const [oauthProvidersList, setOauthProvidersList] = useState<
+ OauthProviders[]
+ >([]);
+
+ /**
+ * Fetch the list of OAuth providers from backend API
+ * and update the state if OAuth providers list is available
+ */
+ useEffect(() => {
+ (async () => {
+ try {
+ const res = (await getOauthProviderList()) as OauthProviders[];
+ setOauthProvidersList(res);
+ } catch (err) {
+ console.error(err);
+ }
+ })();
+ }, []);
+
+ const [loadingButton, setLoadingButton] = useState(false);
+
+ /**
+ * Start the authentication process using oAuth by sending the endpoint URL to the backend for processing.
+ *
+ * @param providerRequestEndpoint The request endpoint for the OAuth provider
+ */
+ const startOauthProcess = async (providerData: {
+ name: string;
+ endpoint: string;
+ }) => {
+ try {
+ setLoadingButton(true);
+
+ (await requestOauthUrl(providerData)) as ResponseRequestOauthUrl;
+ } catch (err) {
+ setLoadingButton(false);
+ console.error(err);
+ }
+ };
+
+ return (
+
+ {/* Render OAuth provider buttons */}
+ {oauthProvidersList.length > 0 ? (
+ oauthProvidersList.map((provider, index) => {
+ return (
+ }
+ onPress={() =>
+ startOauthProcess({
+ name: provider.name,
+ endpoint: provider.req_endpoint,
+ })
+ }
+ isLoading={loadingButton}
+ >
+ Continue with {provider.name}
+
+ );
+ })
+ ) : (
+
+ No login options available via third-party providers
+
+ )}
+
+ );
+};
+
+export default OAuthProviders;
diff --git a/features/auth/ui/components/ProvisionInput.tsx b/features/auth/ui/components/ProvisionInput.tsx
index 7a4b2bb..66cef51 100644
--- a/features/auth/ui/components/ProvisionInput.tsx
+++ b/features/auth/ui/components/ProvisionInput.tsx
@@ -1,103 +1,118 @@
-"use client";
-
-import React, { useState } from "react";
-import { addToast, Button, Form, Input } from "@heroui/react";
-import { SubmitHandler, useForm } from "react-hook-form";
-import { submitRegisterForm } from "../../lib/submitRegisterForm";
-
-type Props = {
- fullname: string;
-};
-
-export type RegisterInputs = {
- fullname: string;
- email: string;
- password: string;
- confirmPassword: string;
-};
-
-const ProvisionInput = ({ fullname }: Props) => {
- const { register, handleSubmit, setValue } = useForm();
- setValue("fullname", fullname);
-
- const [submitStatus, setSubmitStatus] = useState(false);
- const onSubmit: SubmitHandler = async (data) => {
- setSubmitStatus(true);
-
- try {
- const returnData = await submitRegisterForm(data);
- if (!returnData.success) {
- setSubmitStatus(false);
- addToast({
- color: "danger",
- title: "😬 Oops, something went wrong!",
- description: returnData.text.message,
- });
- } else {
- setSubmitStatus(false);
- addToast({
- color: "success",
- title: "OKKE!",
- description: returnData.text.message,
- });
- }
- } catch (error) {
- setSubmitStatus(false);
- addToast({
- color: "danger",
- title: "😬 Oops, something went wrong!",
- description: "Internal server error",
- });
- }
- };
-
- return (
-
-
-
- );
-};
-
-export default ProvisionInput;
+"use client";
+
+import React, { useState } from "react";
+import { addToast, Button, Form, Input } from "@heroui/react";
+import { SubmitHandler, useForm } from "react-hook-form";
+import { submitRegisterForm } from "../../lib/submitRegisterForm";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { registerFormSchema } from "../../models/registerForm.schema";
+
+type Props = {
+ fullname: string;
+};
+
+export type RegisterInputs = {
+ fullname: string;
+ email: string;
+ password: string;
+ confirmPassword: string;
+};
+
+const ProvisionInput = ({ fullname }: Props) => {
+ const {
+ register,
+ handleSubmit,
+ setValue,
+ formState: { errors },
+ } = useForm({
+ resolver: zodResolver(registerFormSchema),
+ });
+ setValue("fullname", fullname);
+
+ const [submitStatus, setSubmitStatus] = useState(false);
+ const onSubmit: SubmitHandler = async (data) => {
+ setSubmitStatus(true);
+
+ try {
+ const returnData = await submitRegisterForm(data);
+ if (!returnData.success) {
+ setSubmitStatus(false);
+ addToast({
+ color: "danger",
+ title: "😬 Oops, something went wrong!",
+ description: returnData.text.message,
+ });
+ } else {
+ setSubmitStatus(false);
+ addToast({
+ color: "success",
+ title: "OKKE!",
+ description: returnData.text.message,
+ });
+ }
+ } catch (error) {
+ setSubmitStatus(false);
+ addToast({
+ color: "danger",
+ title: "😬 Oops, something went wrong!",
+ description: "Connection to server lost",
+ });
+ }
+ };
+
+ return (
+
+
+
+ );
+};
+
+export default ProvisionInput;
diff --git a/features/oauth-callback/lib/sendCallbackToServer.ts b/features/oauth-callback/lib/sendCallbackToServer.ts
index 2e01020..4637b41 100644
--- a/features/oauth-callback/lib/sendCallbackToServer.ts
+++ b/features/oauth-callback/lib/sendCallbackToServer.ts
@@ -1,83 +1,83 @@
-"use server";
-import { api } from "@/shared/lib/ky/connector";
-import { apiErrorHandler } from "@/shared/lib/ky/errorHandler";
-import { ServerRequestCallback } from "@/shared/types/serverRequestCallback";
-
-/**
- * @function SendCallbackToServer
- * @description Proxies OAuth callback requests from the frontend to the main backend system.
- * Acts as an intermediary between the client Next.js application and the Elysia server.
- * Handles the forwarding of OAuth provider callback data for authentication processing.
- *
- * @param {string} data - The OAuth callback data received from the provider, typically containing
- * query parameters such as authorization code, user consent, scopes, state,
- * and other OAuth-specific information. Usually obtained from
- * `window.location.search` in browser environments.
- * @param {string} provider - The name of the OAuth provider/service (e.g., "google", "github",
- * "facebook"). Used to construct the appropriate backend API endpoint.
- *
- * @returns {Promise} The response data from the backend server after processing the
- * OAuth callback. Typically contains authentication tokens, user
- * information, or session data.
- *
- * @throws {Error} If the network request fails or the backend returns an error response.
- * @throws {Error} If the required environment variable APP_DOMAIN is not defined.
- * @throws {Error} If the provided parameters are invalid or missing.
- *
- * @example
- * // Handling OAuth callback in a React component
- * useEffect(() => {
- * const handleOAuthCallback = async () => {
- * try {
- * const result = await SendCallbackToServer(window.location.search, "google");
- * // Handle successful authentication (e.g., store tokens, redirect user)
- * console.log("Authentication successful:", result);
- * } catch (error) {
- * // Handle authentication errors
- * console.error("Authentication failed:", error);
- * }
- * };
- *
- * handleOAuthCallback();
- * }, []);
- *
- * @example
- * // Usage with different providers
- * await SendCallbackToServer(window.location.search, "github");
- * await SendCallbackToServer(window.location.search, "facebook");
- * await SendCallbackToServer(window.location.search, "microsoft");
- *
- * @remarks
- * - This function is specifically designed for OAuth callback handling in a Next.js frontend
- * acting as a proxy to an Elysia backend.
- * - The `data` parameter should include the complete query string from the OAuth redirect.
- * - The callback URI is automatically constructed using the APP_DOMAIN environment variable.
- * - Ensure APP_DOMAIN is properly configured in your environment variables.
- */
-export const SendCallbackToServer = async (
- data: string,
- provider: string
-): Promise => {
- // Construct the backend and frontend handler URLs
- const backendHandlerUrl = `auth/${provider}/callback/`;
- const frontendHandlerUrl = `${process.env
- .APP_DOMAIN!}/auth/callback/${provider}`;
-
- try {
- // Forward the OAuth callback data to the backend for processing
- const response = await api.get(
- `${backendHandlerUrl}${data}&callbackURI=${frontendHandlerUrl}`
- );
-
- // Parse the JSON response from the backend and return the result
- const result = await response.json();
- return {
- success: true,
- status: response.status,
- text: { message: "Callback processed successfully" },
- data: result,
- };
- } catch (error) {
- return apiErrorHandler(error);
- }
-};
+"use server";
+import { api } from "@/shared/lib/ky/connector";
+import { apiErrorHandler } from "@/shared/lib/ky/errorHandler";
+import { ServerRequestCallback } from "@/shared/types/serverRequestCallback";
+
+/**
+ * @function SendCallbackToServer
+ * @description Proxies OAuth callback requests from the frontend to the main backend system.
+ * Acts as an intermediary between the client Next.js application and the Elysia server.
+ * Handles the forwarding of OAuth provider callback data for authentication processing.
+ *
+ * @param {string} data - The OAuth callback data received from the provider, typically containing
+ * query parameters such as authorization code, user consent, scopes, state,
+ * and other OAuth-specific information. Usually obtained from
+ * `window.location.search` in browser environments.
+ * @param {string} provider - The name of the OAuth provider/service (e.g., "google", "github",
+ * "facebook"). Used to construct the appropriate backend API endpoint.
+ *
+ * @returns {Promise} The response data from the backend server after processing the
+ * OAuth callback. Typically contains authentication tokens, user
+ * information, or session data.
+ *
+ * @throws {Error} If the network request fails or the backend returns an error response.
+ * @throws {Error} If the required environment variable APP_DOMAIN is not defined.
+ * @throws {Error} If the provided parameters are invalid or missing.
+ *
+ * @example
+ * // Handling OAuth callback in a React component
+ * useEffect(() => {
+ * const handleOAuthCallback = async () => {
+ * try {
+ * const result = await SendCallbackToServer(window.location.search, "google");
+ * // Handle successful authentication (e.g., store tokens, redirect user)
+ * console.log("Authentication successful:", result);
+ * } catch (error) {
+ * // Handle authentication errors
+ * console.error("Authentication failed:", error);
+ * }
+ * };
+ *
+ * handleOAuthCallback();
+ * }, []);
+ *
+ * @example
+ * // Usage with different providers
+ * await SendCallbackToServer(window.location.search, "github");
+ * await SendCallbackToServer(window.location.search, "facebook");
+ * await SendCallbackToServer(window.location.search, "microsoft");
+ *
+ * @remarks
+ * - This function is specifically designed for OAuth callback handling in a Next.js frontend
+ * acting as a proxy to an Elysia backend.
+ * - The `data` parameter should include the complete query string from the OAuth redirect.
+ * - The callback URI is automatically constructed using the APP_DOMAIN environment variable.
+ * - Ensure APP_DOMAIN is properly configured in your environment variables.
+ */
+export const SendCallbackToServer = async (
+ data: string,
+ provider: string
+): Promise => {
+ // Construct the backend and frontend handler URLs
+ const backendHandlerUrl = `auth/${provider}/callback/`;
+ const frontendHandlerUrl = `${process.env
+ .APP_DOMAIN!}/auth/callback/${provider}`;
+
+ try {
+ // Forward the OAuth callback data to the backend for processing
+ const response = await api.get(
+ `${backendHandlerUrl}${data}&callbackURI=${frontendHandlerUrl}`
+ );
+
+ // Parse the JSON response from the backend and return the result
+ const result = await response.json();
+ return {
+ success: true,
+ status: response.status,
+ text: { message: "Callback processed successfully" },
+ data: result,
+ };
+ } catch (error) {
+ return apiErrorHandler(error);
+ }
+};
diff --git a/features/oauth-callback/pages/callbackHandler.tsx b/features/oauth-callback/pages/callbackHandler.tsx
index e38ee3b..39f64bc 100644
--- a/features/oauth-callback/pages/callbackHandler.tsx
+++ b/features/oauth-callback/pages/callbackHandler.tsx
@@ -1,56 +1,56 @@
-"use client";
-
-import { redirect } from "next/navigation";
-import React, { useEffect, useState } from "react";
-import SecurityCheckup from "@/shared/auth/ui/SecurityCheckup";
-import SecurityCheckupFailed from "@/shared/auth/ui/SecurityCheckupFailed";
-import LoadingProcess from "../ui/LoadingProcess";
-
-const OauthCallbackHandler = () => {
- /**
- * Create a lit component that will be used in popp, consisting of 3 component flows:
- * 1. When the user opens it, a browser environment check will be performed.
- * 2. If it passes, the login component will appear and the user will perform authentication.
- * 3. If it fails, stop the authentication process and display the warning component, then return the user to the homepage.
- */
- const componentFlowList = {
- securityCheckup: ,
- securityCheckupFailed: ,
- proceedCallback: ,
- };
-
- // State to set the current page component
- const [componentFlow, setComponentFlow] = useState(
- componentFlowList.securityCheckup
- );
-
- useEffect(() => {
- // Prevent opening devtools while in authentication page
- // disableDevtool();
-
- /**
- * Check if the window has an opener (i.e., it was opened by another window)
- * If it does, the security checkup has passed.
- * If it doesn't, the security checkup has failed and user will be redirected to the homepage.
- */
- if (window.opener) {
- setComponentFlow(componentFlowList.proceedCallback);
- } else {
- setComponentFlow(componentFlowList.securityCheckupFailed);
-
- const timer = setTimeout(() => {
- redirect("/");
- }, 5000);
- return () => clearTimeout(timer);
- }
- }, []);
-
- return (
- <>
- {/* show the current component flow */}
- {componentFlow}
- >
- );
-};
-
-export default OauthCallbackHandler;
+"use client";
+
+import { redirect } from "next/navigation";
+import React, { useEffect, useState } from "react";
+import SecurityCheckup from "@/shared/auth/ui/SecurityCheckup";
+import SecurityCheckupFailed from "@/shared/auth/ui/SecurityCheckupFailed";
+import LoadingProcess from "../ui/LoadingProcess";
+
+const OauthCallbackHandler = () => {
+ /**
+ * Create a lit component that will be used in popp, consisting of 3 component flows:
+ * 1. When the user opens it, a browser environment check will be performed.
+ * 2. If it passes, the login component will appear and the user will perform authentication.
+ * 3. If it fails, stop the authentication process and display the warning component, then return the user to the homepage.
+ */
+ const componentFlowList = {
+ securityCheckup: ,
+ securityCheckupFailed: ,
+ proceedCallback: ,
+ };
+
+ // State to set the current page component
+ const [componentFlow, setComponentFlow] = useState(
+ componentFlowList.securityCheckup
+ );
+
+ useEffect(() => {
+ // Prevent opening devtools while in authentication page
+ // disableDevtool();
+
+ /**
+ * Check if the window has an opener (i.e., it was opened by another window)
+ * If it does, the security checkup has passed.
+ * If it doesn't, the security checkup has failed and user will be redirected to the homepage.
+ */
+ if (window.opener) {
+ setComponentFlow(componentFlowList.proceedCallback);
+ } else {
+ setComponentFlow(componentFlowList.securityCheckupFailed);
+
+ const timer = setTimeout(() => {
+ redirect("/");
+ }, 5000);
+ return () => clearTimeout(timer);
+ }
+ }, []);
+
+ return (
+ <>
+ {/* show the current component flow */}
+ {componentFlow}
+ >
+ );
+};
+
+export default OauthCallbackHandler;
diff --git a/features/oauth-callback/types/ParamProps.ts b/features/oauth-callback/types/ParamProps.ts
index a0c74af..a06ad16 100644
--- a/features/oauth-callback/types/ParamProps.ts
+++ b/features/oauth-callback/types/ParamProps.ts
@@ -1,9 +1,9 @@
-export interface ParamProps {
- params: { provider: string[] };
- searchParams:
- | string
- | string[][]
- | Record
- | URLSearchParams
- | undefined;
-}
+export interface ParamProps {
+ params: { provider: string[] };
+ searchParams:
+ | string
+ | string[][]
+ | Record
+ | URLSearchParams
+ | undefined;
+}
diff --git a/features/oauth-callback/ui/LoadingProcess.tsx b/features/oauth-callback/ui/LoadingProcess.tsx
index fbb3667..076e270 100644
--- a/features/oauth-callback/ui/LoadingProcess.tsx
+++ b/features/oauth-callback/ui/LoadingProcess.tsx
@@ -1,73 +1,73 @@
-"use client";
-
-import React from "react";
-import { addToast, Button, CircularProgress, Link } from "@heroui/react";
-import { SendCallbackToServer } from "../lib/sendCallbackToServer";
-import { useParams } from "next/navigation";
-import { useRunOnce } from "@/shared/hooks/useRunOnce";
-import { routes } from "@/shared/config/routes";
-
-const LoadingProcess = () => {
- // Access the URL parameters
- const params = useParams();
-
- // Forward the callback response to the backend server
- useRunOnce("forwardCallbackResponseToBackend", async () => {
- try {
- const response = await SendCallbackToServer(
- window.location.search,
- params.provider as string
- );
-
- if (response.success) {
- window.close();
- } else {
- addToast({
- title: "😬 Oops, there's a problem!",
- description: response.text.message,
- color: "danger",
- timeout: 0,
- endContent: (
- (window.location.href = routes.login)}
- >
- Try again
-
- ),
- });
- }
- } catch (error) {
- console.log(error);
- addToast({
- title: "😵💫 Oops, lost connection!",
- description: "Check your internet and try again",
- color: "danger",
- timeout: 0,
- endContent: (
- (window.location.href = routes.login)}
- >
- Reload
-
- ),
- });
- }
- });
- return (
-
-
-
-
Please wait...
-
- Your request is being processed
-
-
-
- );
-};
-
-export default LoadingProcess;
+"use client";
+
+import React from "react";
+import { addToast, Button, CircularProgress, Link } from "@heroui/react";
+import { SendCallbackToServer } from "../lib/sendCallbackToServer";
+import { useParams } from "next/navigation";
+import { useRunOnce } from "@/shared/hooks/useRunOnce";
+import { routes } from "@/shared/config/routes";
+
+const LoadingProcess = () => {
+ // Access the URL parameters
+ const params = useParams();
+
+ // Forward the callback response to the backend server
+ useRunOnce("forwardCallbackResponseToBackend", async () => {
+ try {
+ const response = await SendCallbackToServer(
+ window.location.search,
+ params.provider as string
+ );
+
+ if (response.success) {
+ window.close();
+ } else {
+ addToast({
+ title: "😬 Oops, there's a problem!",
+ description: response.text.message,
+ color: "danger",
+ timeout: 0,
+ endContent: (
+ (window.location.href = routes.login)}
+ >
+ Try again
+
+ ),
+ });
+ }
+ } catch (error) {
+ console.log(error);
+ addToast({
+ title: "😵💫 Oops, lost connection!",
+ description: "Check your internet and try again",
+ color: "danger",
+ timeout: 0,
+ endContent: (
+ (window.location.href = routes.login)}
+ >
+ Reload
+
+ ),
+ });
+ }
+ });
+ return (
+
+
+
+
Please wait...
+
+ Your request is being processed
+
+
+
+ );
+};
+
+export default LoadingProcess;
diff --git a/next.config.ts b/next.config.ts
index 1548f9e..a6b224f 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -1,42 +1,42 @@
-import {withSentryConfig} from "@sentry/nextjs";
-import type { NextConfig } from "next";
-
-const nextConfig: NextConfig = {
- /* config options here */
- env: {
- NEXT_PUBLIC_SENTRY_DSN: process.env.SENTRY_DSN,
- }
-};
-
-export default withSentryConfig(nextConfig, {
- // For all available options, see:
- // https://www.npmjs.com/package/@sentry/webpack-plugin#options
-
- org: process.env.NEXT_PUBLIC_SENTRY_ORG || "",
- project: process.env.NEXT_PUBLIC_SENTRY_PROJECT || "",
- sentryUrl: process.env.NEXT_PUBLIC_SENTRY_URL || "",
-
- // Only print logs for uploading source maps in CI
- silent: !process.env.CI,
-
- // For all available options, see:
- // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
-
- // Upload a larger set of source maps for prettier stack traces (increases build time)
- widenClientFileUpload: true,
-
- // Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.
- // This can increase your server load as well as your hosting bill.
- // Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
- // side errors will fail.
- tunnelRoute: "/monitoring",
-
- // Automatically tree-shake Sentry logger statements to reduce bundle size
- disableLogger: true,
-
- // Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.)
- // See the following for more information:
- // https://docs.sentry.io/product/crons/
- // https://vercel.com/docs/cron-jobs
- automaticVercelMonitors: true
+import {withSentryConfig} from "@sentry/nextjs";
+import type { NextConfig } from "next";
+
+const nextConfig: NextConfig = {
+ /* config options here */
+ env: {
+ NEXT_PUBLIC_SENTRY_DSN: process.env.SENTRY_DSN,
+ }
+};
+
+export default withSentryConfig(nextConfig, {
+ // For all available options, see:
+ // https://www.npmjs.com/package/@sentry/webpack-plugin#options
+
+ org: process.env.NEXT_PUBLIC_SENTRY_ORG || "",
+ project: process.env.NEXT_PUBLIC_SENTRY_PROJECT || "",
+ sentryUrl: process.env.NEXT_PUBLIC_SENTRY_URL || "",
+
+ // Only print logs for uploading source maps in CI
+ silent: !process.env.CI,
+
+ // For all available options, see:
+ // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
+
+ // Upload a larger set of source maps for prettier stack traces (increases build time)
+ widenClientFileUpload: true,
+
+ // Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.
+ // This can increase your server load as well as your hosting bill.
+ // Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
+ // side errors will fail.
+ tunnelRoute: "/monitoring",
+
+ // Automatically tree-shake Sentry logger statements to reduce bundle size
+ disableLogger: true,
+
+ // Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.)
+ // See the following for more information:
+ // https://docs.sentry.io/product/crons/
+ // https://vercel.com/docs/cron-jobs
+ automaticVercelMonitors: true
});
\ No newline at end of file
diff --git a/postcss.config.mjs b/postcss.config.mjs
index 7059fe9..f10c266 100644
--- a/postcss.config.mjs
+++ b/postcss.config.mjs
@@ -1,6 +1,6 @@
-const config = {
- plugins: {
- "@tailwindcss/postcss": {},
- },
-};
-export default config;
+const config = {
+ plugins: {
+ "@tailwindcss/postcss": {},
+ },
+};
+export default config;
diff --git a/providers/HeroUIWrapper.tsx b/providers/HeroUIWrapper.tsx
index 9eba17e..cd75ab1 100644
--- a/providers/HeroUIWrapper.tsx
+++ b/providers/HeroUIWrapper.tsx
@@ -1,32 +1,32 @@
-"use client";
-
-import React, { useEffect, useState } from "react";
-import { HeroUIProvider, ToastProvider } from "@heroui/react";
-import { ThemeProvider as NextThemesProvider } from "next-themes";
-import { useRouter } from "next/navigation";
-
-const HeroUIWrapper = ({
- children,
-}: Readonly<{ children: React.ReactNode }>) => {
- const [mounted, setMounted] = useState(false);
- useEffect(() => {
- setMounted(true);
- }, []);
-
- const router = useRouter();
-
- return (
-
-
- {mounted ? (
-
- {children}
-
- ) : (
- {children}
- )}
-
- );
-};
-
-export default HeroUIWrapper;
+"use client";
+
+import React, { useEffect, useState } from "react";
+import { HeroUIProvider, ToastProvider } from "@heroui/react";
+import { ThemeProvider as NextThemesProvider } from "next-themes";
+import { useRouter } from "next/navigation";
+
+const HeroUIWrapper = ({
+ children,
+}: Readonly<{ children: React.ReactNode }>) => {
+ const [mounted, setMounted] = useState(false);
+ useEffect(() => {
+ setMounted(true);
+ }, []);
+
+ const router = useRouter();
+
+ return (
+
+
+ {mounted ? (
+
+ {children}
+
+ ) : (
+ {children}
+ )}
+
+ );
+};
+
+export default HeroUIWrapper;
diff --git a/providers/fonts/GeistFontProvider.tsx b/providers/fonts/GeistFontProvider.tsx
index 1788c27..991c930 100644
--- a/providers/fonts/GeistFontProvider.tsx
+++ b/providers/fonts/GeistFontProvider.tsx
@@ -1,60 +1,60 @@
-import React from "react";
-import localFont from "next/font/local";
-
-const Geist = localFont({
- src: [
- {
- path: "../../fonts/Geist-Black.ttf",
- weight: "900",
- style: "normal",
- },
- {
- path: "../../fonts/Geist-ExtraBold.ttf",
- weight: "800",
- style: "normal",
- },
- {
- path: "../../fonts/Geist-Bold.ttf",
- weight: "700",
- style: "normal",
- },
- {
- path: "../../fonts/Geist-SemiBold.ttf",
- weight: "600",
- style: "normal",
- },
- {
- path: "../../fonts/Geist-Medium.ttf",
- weight: "500",
- style: "normal",
- },
- {
- path: "../../fonts/Geist-Regular.ttf",
- weight: "400",
- style: "normal",
- },
- {
- path: "../../fonts/Geist-Light.ttf",
- weight: "300",
- style: "normal",
- },
- {
- path: "../../fonts/Geist-ExtraLight.ttf",
- weight: "200",
- style: "normal",
- },
- {
- path: "../../fonts/Geist-Thin.ttf",
- weight: "100",
- style: "normal",
- },
- ],
-});
-
-const GeistFontProvider = ({
- children,
-}: Readonly<{ children: React.ReactNode }>) => {
- return {children}
;
-};
-
-export default GeistFontProvider;
+import React from "react";
+import localFont from "next/font/local";
+
+const Geist = localFont({
+ src: [
+ {
+ path: "../../fonts/Geist-Black.ttf",
+ weight: "900",
+ style: "normal",
+ },
+ {
+ path: "../../fonts/Geist-ExtraBold.ttf",
+ weight: "800",
+ style: "normal",
+ },
+ {
+ path: "../../fonts/Geist-Bold.ttf",
+ weight: "700",
+ style: "normal",
+ },
+ {
+ path: "../../fonts/Geist-SemiBold.ttf",
+ weight: "600",
+ style: "normal",
+ },
+ {
+ path: "../../fonts/Geist-Medium.ttf",
+ weight: "500",
+ style: "normal",
+ },
+ {
+ path: "../../fonts/Geist-Regular.ttf",
+ weight: "400",
+ style: "normal",
+ },
+ {
+ path: "../../fonts/Geist-Light.ttf",
+ weight: "300",
+ style: "normal",
+ },
+ {
+ path: "../../fonts/Geist-ExtraLight.ttf",
+ weight: "200",
+ style: "normal",
+ },
+ {
+ path: "../../fonts/Geist-Thin.ttf",
+ weight: "100",
+ style: "normal",
+ },
+ ],
+});
+
+const GeistFontProvider = ({
+ children,
+}: Readonly<{ children: React.ReactNode }>) => {
+ return {children}
;
+};
+
+export default GeistFontProvider;
diff --git a/scripts/create-env-example.ts b/scripts/create-env-example.ts
index 7628755..a3e228d 100644
--- a/scripts/create-env-example.ts
+++ b/scripts/create-env-example.ts
@@ -1,49 +1,49 @@
-import fs from "fs";
-import path from "path";
-
-// These keys will not be cleared in the .env.example file
-const PRESERVED_KEYS = ["APP_NAME", "APP_ENV", "APP_PORT"];
-
-/**
- * Script to create or update the .env.example file based on the .env file.
- * It preserves certain keys and clears their values in the .env.example file.
- */
-try {
- const envPath = path.join(process.cwd(), ".env");
- const envExamplePath = path.join(process.cwd(), ".env.example");
-
- if (!fs.existsSync(envPath)) {
- console.error(`.env file not found at ${envPath}`);
- process.exit(1);
- }
-
- const envContent = fs.readFileSync(envPath, "utf-8");
-
- const lines = envContent.split("\n");
- const processedLines = lines.map((line) => {
- const trimmedLine = line.trim();
-
- if (trimmedLine.startsWith("#") || trimmedLine === "") {
- return line;
- }
-
- const delimeterIndex = line.indexOf("=");
- if (delimeterIndex === -1) {
- return line;
- }
-
- const key = line.substring(0, delimeterIndex).trim();
- const value = line.substring(delimeterIndex + 1).trim();
-
- if (PRESERVED_KEYS.includes(key)) {
- return `${key}=${value}`;
- }
- return `${key}=`;
- });
-
- fs.writeFileSync(envExamplePath, processedLines.join("\n"));
- console.log("File .env.example berhasil diperbarui!");
-} catch (error) {
- console.error("Error while creating .env.example:", error);
- process.exit(1);
-}
+import fs from "fs";
+import path from "path";
+
+// These keys will not be cleared in the .env.example file
+const PRESERVED_KEYS = ["APP_NAME", "APP_ENV", "APP_PORT"];
+
+/**
+ * Script to create or update the .env.example file based on the .env file.
+ * It preserves certain keys and clears their values in the .env.example file.
+ */
+try {
+ const envPath = path.join(process.cwd(), ".env");
+ const envExamplePath = path.join(process.cwd(), ".env.example");
+
+ if (!fs.existsSync(envPath)) {
+ console.error(`.env file not found at ${envPath}`);
+ process.exit(1);
+ }
+
+ const envContent = fs.readFileSync(envPath, "utf-8");
+
+ const lines = envContent.split("\n");
+ const processedLines = lines.map((line) => {
+ const trimmedLine = line.trim();
+
+ if (trimmedLine.startsWith("#") || trimmedLine === "") {
+ return line;
+ }
+
+ const delimeterIndex = line.indexOf("=");
+ if (delimeterIndex === -1) {
+ return line;
+ }
+
+ const key = line.substring(0, delimeterIndex).trim();
+ const value = line.substring(delimeterIndex + 1).trim();
+
+ if (PRESERVED_KEYS.includes(key)) {
+ return `${key}=${value}`;
+ }
+ return `${key}=`;
+ });
+
+ fs.writeFileSync(envExamplePath, processedLines.join("\n"));
+ console.log("File .env.example berhasil diperbarui!");
+} catch (error) {
+ console.error("Error while creating .env.example:", error);
+ process.exit(1);
+}
diff --git a/scripts/git-multipush.example.ts b/scripts/git-multipush.example.ts
index 937ceed..3a54d5a 100644
--- a/scripts/git-multipush.example.ts
+++ b/scripts/git-multipush.example.ts
@@ -1,22 +1,22 @@
-import { execSync } from "child_process";
-
-/*
-This script pushes the current branch to multiple remotes in a Git repository.
-It is useful for deploying code to multiple servers or services at once.
-Make sure you've set up your remotes correctly before running this script and do commit your changes first!
-*/
-
-const remotes = ["origin"]; // Add your remote names here, e.g., "origin", "vps", etc. if you have multiple remotes
-
-// Start the push process
-for (const remote of remotes) {
- console.log(`Pushing to ${remote}...`);
- try {
- execSync(`git push ${remote} main`, { stdio: "inherit" });
- } catch (err) {
- console.error(`❌ Failed to push to ${remote}`);
- }
-}
-
-// All remotes processed
-console.log("✅ All remotes processed.");
+import { execSync } from "child_process";
+
+/*
+This script pushes the current branch to multiple remotes in a Git repository.
+It is useful for deploying code to multiple servers or services at once.
+Make sure you've set up your remotes correctly before running this script and do commit your changes first!
+*/
+
+const remotes = ["origin"]; // Add your remote names here, e.g., "origin", "vps", etc. if you have multiple remotes
+
+// Start the push process
+for (const remote of remotes) {
+ console.log(`Pushing to ${remote}...`);
+ try {
+ execSync(`git push ${remote} main`, { stdio: "inherit" });
+ } catch (err) {
+ console.error(`❌ Failed to push to ${remote}`);
+ }
+}
+
+// All remotes processed
+console.log("✅ All remotes processed.");
diff --git a/shared/auth/ui/SecurityCheckup.tsx b/shared/auth/ui/SecurityCheckup.tsx
index d778297..a794510 100644
--- a/shared/auth/ui/SecurityCheckup.tsx
+++ b/shared/auth/ui/SecurityCheckup.tsx
@@ -1,17 +1,17 @@
-import React from "react";
-
-const SecurityCheckup = () => {
- return (
-
-
-
Please wait...
-
- We want to ensure a secure authentication environment before
- proceeding for your safety.
-
-
-
- );
-};
-
-export default SecurityCheckup;
+import React from "react";
+
+const SecurityCheckup = () => {
+ return (
+
+
+
Please wait...
+
+ We want to ensure a secure authentication environment before
+ proceeding for your safety.
+
+
+
+ );
+};
+
+export default SecurityCheckup;
diff --git a/shared/auth/ui/SecurityCheckupFailed.tsx b/shared/auth/ui/SecurityCheckupFailed.tsx
index b1b195b..245d5d6 100644
--- a/shared/auth/ui/SecurityCheckupFailed.tsx
+++ b/shared/auth/ui/SecurityCheckupFailed.tsx
@@ -1,19 +1,19 @@
-import React from "react";
-
-const SecurityCheckupFailed = () => {
- return (
-
-
-
- Your browser is not secure
-
-
- Sorry, we had to stop the authentication process and return you to the
- home page because your browser environment is not secure.
-
-
-
- );
-};
-
-export default SecurityCheckupFailed;
+import React from "react";
+
+const SecurityCheckupFailed = () => {
+ return (
+
+
+
+ Your browser is not secure
+
+
+ Sorry, we had to stop the authentication process and return you to the
+ home page because your browser environment is not secure.
+
+
+
+ );
+};
+
+export default SecurityCheckupFailed;
diff --git a/shared/config/meta.ts b/shared/config/meta.ts
index c75e69d..3d08c1d 100644
--- a/shared/config/meta.ts
+++ b/shared/config/meta.ts
@@ -1,29 +1,29 @@
-type BuildMeta = {
- title?: string;
- description?: string;
- image?: string;
-};
-
-const appName = process.env.APP_NAME;
-export const defaultMeta = {
- title: appName || "Unknown App",
- description: "Interactive community",
-};
-
-export const buildMeta = ({ title, description, image }: BuildMeta) => {
- return {
- title: title ? `${title} - ${appName}` : defaultMeta.title,
- description: description || defaultMeta.description,
- openGraph: {
- title,
- description,
- images: image ? [image] : ["/default-og.png"],
- },
- twitter: {
- card: "summary_large_image",
- title,
- description,
- images: image ? [image] : ["/default-og.png"],
- },
- };
-};
+type BuildMeta = {
+ title?: string;
+ description?: string;
+ image?: string;
+};
+
+const appName = process.env.APP_NAME;
+export const defaultMeta = {
+ title: appName || "Unknown App",
+ description: "Interactive community",
+};
+
+export const buildMeta = ({ title, description, image }: BuildMeta) => {
+ return {
+ title: title ? `${title} - ${appName}` : defaultMeta.title,
+ description: description || defaultMeta.description,
+ openGraph: {
+ title,
+ description,
+ images: image ? [image] : ["/default-og.png"],
+ },
+ twitter: {
+ card: "summary_large_image",
+ title,
+ description,
+ images: image ? [image] : ["/default-og.png"],
+ },
+ };
+};
diff --git a/shared/config/routes.ts b/shared/config/routes.ts
index d822bec..7f36300 100644
--- a/shared/config/routes.ts
+++ b/shared/config/routes.ts
@@ -1,9 +1,9 @@
-export const routes = {
- home: "/",
- login: "/login",
- signup: "/signup",
- explore: "/explore",
- trending: "/trending",
- genres: "/genres",
- schedule: "/schedule",
-};
+export const routes = {
+ home: "/",
+ login: "/login",
+ signup: "/signup",
+ explore: "/explore",
+ trending: "/trending",
+ genres: "/genres",
+ schedule: "/schedule",
+};
diff --git a/shared/helper/backendApi.ts b/shared/helper/backendApi.ts
index f6a2fb9..0b2c3f6 100644
--- a/shared/helper/backendApi.ts
+++ b/shared/helper/backendApi.ts
@@ -1,21 +1,21 @@
-export const API_BASE_URL =
- process.env.MAIN_BACKEND_API_URL ?? "http://localhost";
-
-const apiFetch = async (
- path: string,
- init?: RequestInit
-): Promise => {
- const res = await fetch(`${API_BASE_URL}${path}`, {
- ...init,
- headers: {
- "Content-Type": "application/json",
- ...init?.headers,
- },
- cache: "no-store",
- });
-
- if (!res.ok) throw new Error(await res.text());
- return res.json();
-};
-
-export default apiFetch;
+export const API_BASE_URL =
+ process.env.MAIN_BACKEND_API_URL ?? "http://localhost";
+
+const apiFetch = async (
+ path: string,
+ init?: RequestInit
+): Promise => {
+ const res = await fetch(`${API_BASE_URL}${path}`, {
+ ...init,
+ headers: {
+ "Content-Type": "application/json",
+ ...init?.headers,
+ },
+ cache: "no-store",
+ });
+
+ if (!res.ok) throw new Error(await res.text());
+ return res.json();
+};
+
+export default apiFetch;
diff --git a/shared/helper/delayButtonClick.ts b/shared/helper/delayButtonClick.ts
index 64146e2..152d488 100644
--- a/shared/helper/delayButtonClick.ts
+++ b/shared/helper/delayButtonClick.ts
@@ -1,11 +1,11 @@
-import { useRouter } from "next/navigation";
-
-export const delayButtonClick = (
- router: ReturnType,
- href: string,
- timeout: number = 300
-) => {
- setTimeout(() => {
- router.push(href);
- }, timeout);
-};
+import { useRouter } from "next/navigation";
+
+export const delayButtonClick = (
+ router: ReturnType,
+ href: string,
+ timeout: number = 300
+) => {
+ setTimeout(() => {
+ router.push(href);
+ }, timeout);
+};
diff --git a/shared/hooks/useRunOnce.ts b/shared/hooks/useRunOnce.ts
index f7dbea5..f44c1c6 100644
--- a/shared/hooks/useRunOnce.ts
+++ b/shared/hooks/useRunOnce.ts
@@ -1,58 +1,58 @@
-"use client";
-import { useEffect, useRef } from "react";
-
-/**
- * @function useRunOnce
- * @description A custom React hook that ensures a function is executed only once
- * across the entire application, even in React Strict Mode or during
- * development hot reloads. Maintains a global registry to track
- * execution status using a unique key.
- *
- * Particularly useful for one-time initialization logic, analytics
- * tracking, or any operation that should not be duplicated.
- *
- * @param {string} key - A unique identifier for the process. Used to track execution
- * across component instances and prevent naming collisions.
- * Should be descriptive and unique (e.g., "user_analytics_init").
- * @param {function} fn - The function to be executed once. Should contain the logic
- * that needs to run only a single time.
- *
- * @returns {void}
- *
- * @throws {Error} If the provided key is not a string or is empty.
- * @throws {Error} If the provided function is not callable.
- *
- * @example
- * // One-time asynchronous operation
- * useRunOnce("async_operation", async () => {
- * await yourAsyncFunction();
- * });
- *
- * @example
- * // One-time synchronous operation
- * useRunOnce("sync_operation", () => {
- * yourFunction();
- * });
- *
- * @remarks
- * - The hook uses a global registry, so the same key across different components
- * will prevent duplicate execution.
- * - Safe to use in React Strict Mode and development environment with hot reload.
- * - The function will not execute if another instance with the same key has
- * already run in the application.
- */
-const registry = new Set();
-
-export function useRunOnce(key: string, fn: () => void) {
- const hasRun = useRef(false);
-
- useEffect(() => {
- if (hasRun.current) return;
- hasRun.current = true;
-
- if (registry.has(key)) return;
- registry.add(key);
-
- fn();
- }, [key, fn]);
-}
+"use client";
+import { useEffect, useRef } from "react";
+
+/**
+ * @function useRunOnce
+ * @description A custom React hook that ensures a function is executed only once
+ * across the entire application, even in React Strict Mode or during
+ * development hot reloads. Maintains a global registry to track
+ * execution status using a unique key.
+ *
+ * Particularly useful for one-time initialization logic, analytics
+ * tracking, or any operation that should not be duplicated.
+ *
+ * @param {string} key - A unique identifier for the process. Used to track execution
+ * across component instances and prevent naming collisions.
+ * Should be descriptive and unique (e.g., "user_analytics_init").
+ * @param {function} fn - The function to be executed once. Should contain the logic
+ * that needs to run only a single time.
+ *
+ * @returns {void}
+ *
+ * @throws {Error} If the provided key is not a string or is empty.
+ * @throws {Error} If the provided function is not callable.
+ *
+ * @example
+ * // One-time asynchronous operation
+ * useRunOnce("async_operation", async () => {
+ * await yourAsyncFunction();
+ * });
+ *
+ * @example
+ * // One-time synchronous operation
+ * useRunOnce("sync_operation", () => {
+ * yourFunction();
+ * });
+ *
+ * @remarks
+ * - The hook uses a global registry, so the same key across different components
+ * will prevent duplicate execution.
+ * - Safe to use in React Strict Mode and development environment with hot reload.
+ * - The function will not execute if another instance with the same key has
+ * already run in the application.
+ */
+const registry = new Set();
+
+export function useRunOnce(key: string, fn: () => void) {
+ const hasRun = useRef(false);
+
+ useEffect(() => {
+ if (hasRun.current) return;
+ hasRun.current = true;
+
+ if (registry.has(key)) return;
+ registry.add(key);
+
+ fn();
+ }, [key, fn]);
+}
diff --git a/shared/lib/ky/connector.ts b/shared/lib/ky/connector.ts
index aceb73a..6648056 100644
--- a/shared/lib/ky/connector.ts
+++ b/shared/lib/ky/connector.ts
@@ -1,12 +1,12 @@
-"use server";
-
-import ky from "ky";
-
-export const api = ky.create({
- prefixUrl: process.env.MAIN_BACKEND_API_URL,
- credentials: "include",
- headers: {
- access_token: process.env.MAIN_BACKEND_API_KEY,
- },
- retry: 0,
-});
+"use server";
+
+import ky from "ky";
+
+export const api = ky.create({
+ prefixUrl: process.env.MAIN_BACKEND_API_URL,
+ credentials: "include",
+ headers: {
+ access_token: process.env.MAIN_BACKEND_API_KEY,
+ },
+ retry: 0,
+});
diff --git a/shared/lib/ky/errorHandler.ts b/shared/lib/ky/errorHandler.ts
index e452faa..ddd17b8 100644
--- a/shared/lib/ky/errorHandler.ts
+++ b/shared/lib/ky/errorHandler.ts
@@ -1,34 +1,34 @@
-"use server";
-
-import { HTTPError } from "ky";
-
-export type CallApiErrorHandler = {
- success?: boolean;
- status?: number;
- text?: { message?: string };
-};
-
-export const apiErrorHandler = async (
- error: unknown,
- safeFail?: CallApiErrorHandler
-) => {
- if (error instanceof HTTPError) {
- return {
- success: false,
- status: error.response.status,
- text: await error.response.json(),
- };
- }
-
- if (safeFail) {
- return {
- success: safeFail.success || false,
- status: safeFail.status || 500,
- text: {
- message: safeFail.text?.message || "An unexpected error occurred",
- },
- };
- } else {
- throw error;
- }
-};
+"use server";
+
+import { HTTPError } from "ky";
+
+export type CallApiErrorHandler = {
+ success?: boolean;
+ status?: number;
+ text?: { message?: string };
+};
+
+export const apiErrorHandler = async (
+ error: unknown,
+ safeFail?: CallApiErrorHandler
+) => {
+ if (error instanceof HTTPError) {
+ return {
+ success: false,
+ status: error.response.status,
+ text: await error.response.json(),
+ };
+ }
+
+ if (safeFail) {
+ return {
+ success: safeFail.success || false,
+ status: safeFail.status || 500,
+ text: {
+ message: safeFail.text?.message || "An unexpected error occurred",
+ },
+ };
+ } else {
+ throw error;
+ }
+};
diff --git a/shared/types/ServerRequestCallback.ts b/shared/types/ServerRequestCallback.ts
index 7b33c27..b9a2cf9 100644
--- a/shared/types/ServerRequestCallback.ts
+++ b/shared/types/ServerRequestCallback.ts
@@ -1,7 +1,7 @@
-export type ServerRequestCallback = {
- success: boolean;
- status: number;
- text: { message: string };
- data?: any;
- error?: unknown;
-};
+export type ServerRequestCallback = {
+ success: boolean;
+ status: number;
+ text: { message: string };
+ data?: any;
+ error?: unknown;
+};
diff --git a/widgets/navbar/ui/LoginAndSignup.tsx b/widgets/navbar/ui/LoginAndSignup.tsx
index 617c50b..70bd638 100644
--- a/widgets/navbar/ui/LoginAndSignup.tsx
+++ b/widgets/navbar/ui/LoginAndSignup.tsx
@@ -1,37 +1,37 @@
-"use client";
-
-import { routes } from "@/shared/config/routes";
-import { Button, Link, NavbarItem } from "@heroui/react";
-import React from "react";
-
-const LoginAndSignup = () => {
- const openPopupWindow = (href: string) => {
- window.open(href, "popup", "width=500,height=600");
- };
- return (
- <>
-
- openPopupWindow(routes.login)}
- >
- Login
-
-
-
- openPopupWindow(routes.signup)}
- >
- Sign Up
-
-
- >
- );
-};
-
-export default LoginAndSignup;
+"use client";
+
+import { routes } from "@/shared/config/routes";
+import { Button, Link, NavbarItem } from "@heroui/react";
+import React from "react";
+
+const LoginAndSignup = () => {
+ const openPopupWindow = (href: string) => {
+ window.open(href, "popup", "width=500,height=600");
+ };
+ return (
+ <>
+
+ openPopupWindow(routes.login)}
+ >
+ Login
+
+
+
+ openPopupWindow(routes.signup)}
+ >
+ Sign Up
+
+
+ >
+ );
+};
+
+export default LoginAndSignup;
diff --git a/widgets/navbar/ui/Navbar.tsx b/widgets/navbar/ui/Navbar.tsx
index c5c9587..6e3c5f3 100644
--- a/widgets/navbar/ui/Navbar.tsx
+++ b/widgets/navbar/ui/Navbar.tsx
@@ -1,111 +1,111 @@
-"use client";
-
-import {
- Link,
- Navbar,
- NavbarBrand,
- NavbarContent,
- NavbarItem,
- NavbarMenu,
- NavbarMenuItem,
- NavbarMenuToggle,
-} from "@heroui/react";
-import { usePathname } from "next/navigation";
-import React, { useState } from "react";
-import { routes } from "../../../shared/config/routes";
-import LoginAndSignup from "./LoginAndSignup";
-
-export const AcmeLogo = () => {
- return (
-
-
-
- );
-};
-
-const NavbarUI = () => {
- const pathNameNow = usePathname();
-
- const [isMenuOpen, setIsMenuOpen] = useState(false);
-
- const navbarItems = [
- {
- title: "Home",
- route: routes.home,
- },
- {
- title: "Explore",
- route: routes.explore,
- },
- {
- title: "Trending",
- route: routes.trending,
- },
- {
- title: "Genres",
- route: routes.genres,
- },
- {
- title: "Schedule",
- route: routes.schedule,
- },
- ];
-
- return (
-
-
-
-
-
- ACME
-
-
-
-
- {navbarItems.map((item, index) => {
- const isActive = item.route === pathNameNow;
-
- return (
-
-
- {item.title}
-
-
- );
- })}
-
-
-
-
-
-
-
- {navbarItems.map((item, index) => (
-
-
- {item.title}
-
-
- ))}
-
-
- );
-};
-
-export default NavbarUI;
+"use client";
+
+import {
+ Link,
+ Navbar,
+ NavbarBrand,
+ NavbarContent,
+ NavbarItem,
+ NavbarMenu,
+ NavbarMenuItem,
+ NavbarMenuToggle,
+} from "@heroui/react";
+import { usePathname } from "next/navigation";
+import React, { useState } from "react";
+import { routes } from "../../../shared/config/routes";
+import LoginAndSignup from "./LoginAndSignup";
+
+export const AcmeLogo = () => {
+ return (
+
+
+
+ );
+};
+
+const NavbarUI = () => {
+ const pathNameNow = usePathname();
+
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
+
+ const navbarItems = [
+ {
+ title: "Home",
+ route: routes.home,
+ },
+ {
+ title: "Explore",
+ route: routes.explore,
+ },
+ {
+ title: "Trending",
+ route: routes.trending,
+ },
+ {
+ title: "Genres",
+ route: routes.genres,
+ },
+ {
+ title: "Schedule",
+ route: routes.schedule,
+ },
+ ];
+
+ return (
+
+
+
+
+
+ ACME
+
+
+
+
+ {navbarItems.map((item, index) => {
+ const isActive = item.route === pathNameNow;
+
+ return (
+
+
+ {item.title}
+
+
+ );
+ })}
+
+
+
+
+
+
+
+ {navbarItems.map((item, index) => (
+
+
+ {item.title}
+
+
+ ))}
+
+
+ );
+};
+
+export default NavbarUI;