= ({
setError(null);
try {
- // await onVerify(code);
+ await onVerify(code);
handleClose();
} catch {
setError('Invalid verification code. Please try again.');
diff --git a/src/app/auth/callback/AuthCallbackContent.tsx b/src/app/auth/callback/AuthCallbackContent.tsx
index 12afc63..51636c3 100644
--- a/src/app/auth/callback/AuthCallbackContent.tsx
+++ b/src/app/auth/callback/AuthCallbackContent.tsx
@@ -49,8 +49,6 @@ export default function AuthCallbackContent() {
// Clear any persisted auth state to prevent old user ID from being used
localStorage.removeItem('persist:root');
- console.log('[AuthCallback] Setting user with ID:', parsedUser._id);
-
dispatch(
setCredentials({
csrfToken,
diff --git a/src/components/VerificationGuard.tsx b/src/components/VerificationGuard.tsx
new file mode 100644
index 0000000..c693cb6
--- /dev/null
+++ b/src/components/VerificationGuard.tsx
@@ -0,0 +1,66 @@
+'use client';
+
+import { Box, CircularProgress, Typography } from '@mui/material';
+import { useRouter } from 'next/navigation';
+import { useEffect } from 'react';
+
+import { useVerificationCheck } from '@/features/settings/hooks/useVerificationCheck';
+
+interface VerificationGuardProps {
+ children: React.ReactNode;
+ redirectTo?: string;
+}
+
+export default function VerificationGuard({
+ children,
+ redirectTo = '/admin/settings',
+}: VerificationGuardProps) {
+ const router = useRouter();
+ const { isFullyVerified, isLoading } = useVerificationCheck();
+
+ useEffect(() => {
+ if (!isLoading && !isFullyVerified) {
+ router.push(redirectTo);
+ }
+ }, [isLoading, isFullyVerified, router, redirectTo]);
+
+ if (isLoading) {
+ return (
+
+
+
+ Checking verification status...
+
+
+ );
+ }
+
+ if (!isFullyVerified) {
+ return (
+
+
+ Verification Required
+
+
+ Redirecting to settings...
+
+
+ );
+ }
+
+ return <>{children}>;
+}
diff --git a/src/components/common/VerificationGuard.tsx b/src/components/common/VerificationGuard.tsx
new file mode 100644
index 0000000..14d9bd0
--- /dev/null
+++ b/src/components/common/VerificationGuard.tsx
@@ -0,0 +1,54 @@
+'use client';
+
+import React from 'react';
+
+import { useVerificationCheck } from '@/features/settings/hooks/useVerificationCheck';
+
+interface VerificationGuardProps {
+ children: React.ReactNode;
+ operation: string;
+ fallback?: React.ReactNode;
+}
+
+/**
+ * A guard component that blocks operations if user is not fully verified
+ * Shows fallback content or nothing if verification is required
+ */
+export default function VerificationGuard({
+ children,
+ operation: _operation,
+ fallback,
+}: VerificationGuardProps) {
+ const { isFullyVerified, isLoading } = useVerificationCheck();
+
+ if (isLoading) {
+ return <>{fallback ?? Loading verification status...
}>;
+ }
+
+ if (!isFullyVerified) {
+ return <>{fallback ?? null}>;
+ }
+
+ return <>{children}>;
+}
+
+/**
+ * Hook to wrap functions that require verification
+ */
+export const useVerificationGuard = () => {
+ const { blockOperationWithAlert } = useVerificationCheck();
+
+ const guardFunction = unknown>(
+ fn: T,
+ operation: string,
+ ): T => {
+ return ((...args: Parameters) => {
+ if (!blockOperationWithAlert(operation)) {
+ return;
+ }
+ return fn(...args);
+ }) as T;
+ };
+
+ return { guardFunction };
+};
diff --git a/src/features/settings/hooks/useVerificationCheck.ts b/src/features/settings/hooks/useVerificationCheck.ts
new file mode 100644
index 0000000..707f128
--- /dev/null
+++ b/src/features/settings/hooks/useVerificationCheck.ts
@@ -0,0 +1,93 @@
+import { useRouter } from 'next/navigation';
+import { useCallback } from 'react';
+
+import { useGetVerificationQuery } from '@/features/settings/settingsApi';
+import { useAppSelector } from '@/redux/hooks';
+
+export const useVerificationCheck = () => {
+ const router = useRouter();
+ const user = useAppSelector(state => state.auth.user);
+
+ const { data: verificationData, isLoading } = useGetVerificationQuery(
+ user?._id ?? '',
+ {
+ skip: !user?._id,
+ },
+ );
+
+ const isFullyVerified =
+ verificationData?.emailVerified && verificationData?.mobileVerified;
+ const isEmailVerified = verificationData?.emailVerified ?? false;
+ const isPhoneVerified = verificationData?.mobileVerified ?? false;
+
+ const checkVerificationAndRedirect = useCallback(() => {
+ if (isLoading) return false;
+
+ if (!isFullyVerified) {
+ router.push('/admin/settings');
+ return false;
+ }
+
+ return true;
+ }, [isLoading, isFullyVerified, router]);
+
+ const showVerificationModal = useCallback(() => {
+ if (!isFullyVerified) {
+ router.push('/admin/settings');
+ return true;
+ }
+ return false;
+ }, [isFullyVerified, router]);
+
+ // New function to check verification with detailed error message
+ const checkVerificationWithMessage = useCallback(
+ (operation: string) => {
+ if (isLoading) {
+ return { allowed: false, message: 'Verification status is loading...' };
+ }
+
+ if (!isFullyVerified) {
+ const unverifiedItems = [];
+ if (!isEmailVerified) unverifiedItems.push('email');
+ if (!isPhoneVerified) unverifiedItems.push('phone');
+
+ return {
+ allowed: false,
+ message: `Cannot ${operation}. Please verify your ${unverifiedItems.join(' and ')} first.`,
+ unverifiedItems,
+ redirect: () => router.push('/admin/settings'),
+ };
+ }
+
+ return { allowed: true };
+ },
+ [isLoading, isFullyVerified, isEmailVerified, isPhoneVerified, router],
+ );
+
+ // Function to block operations with alert
+ const blockOperationWithAlert = useCallback(
+ (operation: string) => {
+ const result = checkVerificationWithMessage(operation);
+ if (!result.allowed) {
+ alert(
+ `๐จ Verification Required\n\n${result.message}\n\nYou will be redirected to Settings to complete verification.`,
+ );
+ result.redirect?.();
+ }
+ return result.allowed;
+ },
+ [checkVerificationWithMessage],
+ );
+
+ return {
+ isFullyVerified,
+ isEmailVerified,
+ isPhoneVerified,
+ isLoading,
+ verificationData,
+ checkVerificationAndRedirect,
+ showVerificationModal,
+ checkVerificationWithMessage,
+ blockOperationWithAlert,
+ };
+};
diff --git a/src/features/settings/settingsApi.ts b/src/features/settings/settingsApi.ts
index b858b21..9aede30 100644
--- a/src/features/settings/settingsApi.ts
+++ b/src/features/settings/settingsApi.ts
@@ -138,7 +138,7 @@ export const settingsApi = createApi({
}),
getVerification: builder.query({
query: userId => ({
- url: `/api/settings/user/${userId}/verification`,
+ url: `/verification/user/${userId}`,
method: 'GET',
}),
providesTags: ['Verification'],
@@ -148,7 +148,7 @@ export const settingsApi = createApi({
{ userId: string } & VerificationSettings
>({
query: ({ userId, ...verificationData }) => ({
- url: `/api/settings/user/${userId}/verification`,
+ url: `/verification/user/${userId}`,
method: 'PUT',
data: verificationData,
}),
@@ -159,23 +159,56 @@ export const settingsApi = createApi({
{ userId: string; mobile: string }
>({
query: ({ userId, mobile }) => ({
- url: `/api/settings/user/${userId}/verification/mobile`,
+ url: `/verification/user/${userId}/mobile`,
method: 'POST',
data: { mobile },
}),
invalidatesTags: ['Verification'],
}),
- verifyEmail: builder.mutation<
+ sendSmsVerification: builder.mutation<
+ { success: boolean; message?: string },
+ { userId: string; mobile: string }
+ >({
+ query: ({ userId, mobile }) => ({
+ url: `/verification/user/${userId}/mobile/send`,
+ method: 'POST',
+ data: { mobile },
+ }),
+ invalidatesTags: ['Verification'],
+ }),
+ verifySms: builder.mutation<
{ success: boolean; message: string },
+ { userId: string; mobile: string; code: string }
+ >({
+ query: ({ userId, mobile, code }) => ({
+ url: `/verification/user/${userId}/mobile/verify`,
+ method: 'POST',
+ data: { mobile, code },
+ }),
+ invalidatesTags: ['Verification'],
+ }),
+ sendEmailVerification: builder.mutation<
+ { success: boolean; message?: string },
{ userId: string; email: string }
>({
query: ({ userId, email }) => ({
- url: `/api/settings/user/${userId}/verification/email`,
+ url: `/verification/user/${userId}/email/send`,
method: 'POST',
data: { email },
}),
invalidatesTags: ['Verification'],
}),
+ verifyEmail: builder.mutation<
+ { success: boolean; message: string },
+ { userId: string; email: string; code: string }
+ >({
+ query: ({ userId, email, code }) => ({
+ url: `/verification/user/${userId}/email/verify`,
+ method: 'POST',
+ data: { email, code },
+ }),
+ invalidatesTags: ['Verification'],
+ }),
checkABNExists: builder.mutation<
{ exists: boolean },
{ abn: string; userId: string }
@@ -220,6 +253,9 @@ export const {
useGetVerificationQuery,
useUpdateVerificationMutation,
useVerifyMobileMutation,
+ useSendSmsVerificationMutation,
+ useVerifySmsMutation,
+ useSendEmailVerificationMutation,
useVerifyEmailMutation,
useGetAddressQuery,
useUpdateAddressMutation,