-
Notifications
You must be signed in to change notification settings - Fork 31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
enhance: reporting tool enhancements and fixes #2176
Changes from 3 commits
c1ece84
4923f66
f455f90
fd41cc0
b953eb1
0edc2da
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
{ | ||
"name": "reports", | ||
"version": "0.5.1", | ||
"version": "0.5.2", | ||
"private": true, | ||
"scripts": { | ||
"dev": "next dev", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import React from 'react'; | ||
|
||
interface NoInternetSVGProps { | ||
className?: string; | ||
} | ||
|
||
const NoInternetSVG: React.FC<NoInternetSVGProps> = ({ className }) => ( | ||
<svg | ||
className={className} | ||
xmlns="http://www.w3.org/2000/svg" | ||
viewBox="0 0 512 512" | ||
fill="currentColor" | ||
> | ||
<path d="M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm0 384c-97.2 0-176-78.8-176-176S158.8 80 256 80s176 78.8 176 176-78.8 176-176 176zm95.8-272H160.2c-9.6 0-18.4 5.8-22.2 14.8s-1.8 19.6 5.4 26.6l95.8 95.8c7 7 16.2 10.8 25.8 10.8s18.8-3.8 25.8-10.8l95.8-95.8c7.2-7 9-17.6 5.4-26.6s-12.6-14.8-22.2-14.8zm-95.8 160c-13.3 0-24-10.7-24-24s10.7-24 24-24 24 10.7 24 24-10.7 24-24 24z" /> | ||
</svg> | ||
); | ||
|
||
export default NoInternetSVG; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
'use client'; | ||
import React, { useState, useEffect } from 'react'; | ||
|
||
import { useNetworkChecker } from '@/hooks/useNetworkChecker'; | ||
import NoInternetSVG from '@/public/svgs/NoInternetSVG'; | ||
|
||
const NetworkStatus: React.FC<React.PropsWithChildren> = ({ children }) => { | ||
const [retrying, setRetrying] = useState(false); | ||
const [retryTimer, setRetryTimer] = useState<number | null>(null); | ||
const isOnline = useNetworkChecker(); | ||
|
||
const handleRetry = () => { | ||
setRetrying(true); | ||
setRetryTimer(setTimeout(() => window.location.reload(), 5000) as unknown as number); | ||
}; | ||
|
||
const cancelRetry = () => { | ||
if (retryTimer) { | ||
clearTimeout(retryTimer); | ||
setRetryTimer(null); | ||
setRetrying(false); | ||
} | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Enhance retry logic and improve type safety. The retry logic is well-implemented, but there are a couple of areas for improvement:
These changes will make the code more maintainable and type-safe. |
||
|
||
useEffect(() => { | ||
return () => { | ||
if (retryTimer) clearTimeout(retryTimer); | ||
}; | ||
}, [retryTimer]); | ||
|
||
if (!isOnline) { | ||
return ( | ||
<div className="flex justify-center items-center h-screen bg-gray-50 px-4"> | ||
<div className="text-center max-w-lg"> | ||
<NoInternetSVG className="w-64 h-64 mx-auto mb-6" /> | ||
<h1 className="text-3xl md:text-4xl text-blue-800 font-bold mb-4"> | ||
Oops! No Internet Connection | ||
</h1> | ||
<p className="text-lg mb-6 text-gray-700"> | ||
It seems you are offline. Please check your connection and try again. | ||
</p> | ||
<div className="space-x-4"> | ||
<button | ||
onClick={handleRetry} | ||
className="px-6 py-3 bg-blue-700 text-white rounded-md transition hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-700 focus:ring-opacity-50 disabled:opacity-50" | ||
aria-label="Retry connection" | ||
disabled={retrying} | ||
> | ||
{retrying ? 'Retrying in 5s...' : 'Retry Now'} | ||
</button> | ||
{retrying && ( | ||
<button | ||
onClick={cancelRetry} | ||
className="px-6 py-3 bg-gray-300 text-gray-800 rounded-md transition hover:bg-gray-400 focus:outline-none focus:ring-4 focus:ring-gray-400 focus:ring-opacity-50" | ||
aria-label="Cancel retry" | ||
> | ||
Cancel | ||
</button> | ||
)} | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
return <>{children}</>; | ||
}; | ||
|
||
export default NetworkStatus; |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,18 +1,31 @@ | ||||||||||||||||||||||||||||||||||||||||||
import axios from 'axios'; | ||||||||||||||||||||||||||||||||||||||||||
import { NextAuthOptions, User as NextAuthUser } from 'next-auth'; | ||||||||||||||||||||||||||||||||||||||||||
import CredentialsProvider from 'next-auth/providers/credentials'; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
export const options = { | ||||||||||||||||||||||||||||||||||||||||||
// Define types for credentials, user, and token | ||||||||||||||||||||||||||||||||||||||||||
interface Credentials { | ||||||||||||||||||||||||||||||||||||||||||
email?: string; | ||||||||||||||||||||||||||||||||||||||||||
password?: string; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Refine the 'Credentials' interface by requiring fields Defining the Consider updating the interface to: interface Credentials {
- email?: string;
- password?: string;
+ email: string;
+ password: string;
} This change enforces at the type level that both fields must be provided. 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
interface CustomUser extends NextAuthUser { | ||||||||||||||||||||||||||||||||||||||||||
_id: string; | ||||||||||||||||||||||||||||||||||||||||||
userName: string; | ||||||||||||||||||||||||||||||||||||||||||
token: string; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
export const options: NextAuthOptions = { | ||||||||||||||||||||||||||||||||||||||||||
providers: [ | ||||||||||||||||||||||||||||||||||||||||||
CredentialsProvider({ | ||||||||||||||||||||||||||||||||||||||||||
id: 'credentials', | ||||||||||||||||||||||||||||||||||||||||||
name: 'Credentials', | ||||||||||||||||||||||||||||||||||||||||||
credentials: {}, | ||||||||||||||||||||||||||||||||||||||||||
async authorize(credentials: { email?: string; password?: string } | undefined) { | ||||||||||||||||||||||||||||||||||||||||||
if (!credentials) { | ||||||||||||||||||||||||||||||||||||||||||
throw new Error('No credentials provided'); | ||||||||||||||||||||||||||||||||||||||||||
async authorize(credentials: Credentials | undefined) { | ||||||||||||||||||||||||||||||||||||||||||
if (!credentials || !credentials.email || !credentials.password) { | ||||||||||||||||||||||||||||||||||||||||||
throw new Error('Please provide both email and password.'); | ||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid throwing errors directly in authentication flow Throwing an error with Consider modifying the code: if (!credentials || !credentials.email || !credentials.password) {
- throw new Error('Please provide both email and password.');
+ return null;
} Ensure the client handles the 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
const { email: userName = '', password = '' } = credentials; | ||||||||||||||||||||||||||||||||||||||||||
const { email: userName, password } = credentials; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||
const url = `${process.env.NEXT_PUBLIC_API_URL}/users/loginUser`; | ||||||||||||||||||||||||||||||||||||||||||
|
@@ -22,12 +35,19 @@ export const options = { | |||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
if (response) { | ||||||||||||||||||||||||||||||||||||||||||
return response; | ||||||||||||||||||||||||||||||||||||||||||
return { | ||||||||||||||||||||||||||||||||||||||||||
_id: response._id, | ||||||||||||||||||||||||||||||||||||||||||
userName: response.userName, | ||||||||||||||||||||||||||||||||||||||||||
email: response.email, | ||||||||||||||||||||||||||||||||||||||||||
token: response.token, | ||||||||||||||||||||||||||||||||||||||||||
} as CustomUser; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
throw new Error('User not found'); | ||||||||||||||||||||||||||||||||||||||||||
return null; | ||||||||||||||||||||||||||||||||||||||||||
} catch (error: any) { | ||||||||||||||||||||||||||||||||||||||||||
throw new Error(error.response.data.message || error.message); | ||||||||||||||||||||||||||||||||||||||||||
throw new Error( | ||||||||||||||||||||||||||||||||||||||||||
error?.response?.data?.message || 'An error occurred during login. Please try again.', | ||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||
}), | ||||||||||||||||||||||||||||||||||||||||||
|
@@ -39,22 +59,26 @@ export const options = { | |||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||
secret: process.env.NEXTAUTH_SECRET, | ||||||||||||||||||||||||||||||||||||||||||
callbacks: { | ||||||||||||||||||||||||||||||||||||||||||
jwt: async ({ token, user }: any) => { | ||||||||||||||||||||||||||||||||||||||||||
jwt: async ({ token, user }) => { | ||||||||||||||||||||||||||||||||||||||||||
if (user) { | ||||||||||||||||||||||||||||||||||||||||||
token.id = user._id; | ||||||||||||||||||||||||||||||||||||||||||
token.userName = user.userName; | ||||||||||||||||||||||||||||||||||||||||||
token.email = user.email; | ||||||||||||||||||||||||||||||||||||||||||
token.accessToken = user.token; | ||||||||||||||||||||||||||||||||||||||||||
const customUser = user as CustomUser; | ||||||||||||||||||||||||||||||||||||||||||
token.id = customUser._id; | ||||||||||||||||||||||||||||||||||||||||||
token.userName = customUser.userName; | ||||||||||||||||||||||||||||||||||||||||||
token.email = customUser.email; | ||||||||||||||||||||||||||||||||||||||||||
token.accessToken = customUser.token; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
return token; | ||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||
session: async ({ session, token }: { session: any; token: any }) => { | ||||||||||||||||||||||||||||||||||||||||||
session.user.id = token._id; | ||||||||||||||||||||||||||||||||||||||||||
session.user.userName = token.userName; | ||||||||||||||||||||||||||||||||||||||||||
session.user.email = token.email; | ||||||||||||||||||||||||||||||||||||||||||
session.accessToken = token.accessToken; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
return session; | ||||||||||||||||||||||||||||||||||||||||||
session: async ({ session, token }) => { | ||||||||||||||||||||||||||||||||||||||||||
return { | ||||||||||||||||||||||||||||||||||||||||||
...session, | ||||||||||||||||||||||||||||||||||||||||||
user: { | ||||||||||||||||||||||||||||||||||||||||||
id: token.id as string, | ||||||||||||||||||||||||||||||||||||||||||
userName: token.userName as string, | ||||||||||||||||||||||||||||||||||||||||||
email: token.email as string, | ||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||
accessToken: token.accessToken as string, | ||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sensitive data in session object Including Consider removing return {
...session,
user: {
id: token.id as string,
userName: token.userName as string,
email: token.email as string,
},
- accessToken: token.accessToken as string,
}; If the token is needed on the client, evaluate using HTTP-only cookies or other secure storage mechanisms to prevent unauthorized access. 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
import NextAuth from 'next-auth'; | ||
|
||
import { options } from './options'; | ||
|
||
const handler = NextAuth(options); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import Files from '@/components/pages/home/Files'; | ||
|
||
const Page = () => { | ||
return <Files />; | ||
}; | ||
export default Page; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import HelpPage from '@/components/pages/help'; | ||
import MainLayout from '@/layout/MainLayout'; | ||
|
||
const Help = () => { | ||
return ( | ||
<MainLayout> | ||
<HelpPage /> | ||
</MainLayout> | ||
); | ||
}; | ||
|
||
export default Help; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove duplicate entry in remotePatterns array
Hey there! I noticed we've got a bit of a doppelganger situation in our
remotePatterns
array. It looks like we accidentally added an identical entry for the Cloudinary hostname. While I appreciate the enthusiasm for Cloudinary (it's pretty great, right?), we only need one entry for each unique pattern.Let's clean this up by removing the duplicate. Here's a quick fix:
This should keep our config nice and tidy without changing any functionality. What do you think?
📝 Committable suggestion