Skip to content

Commit

Permalink
Merge branch 'staging' into admin-panel/manage-members
Browse files Browse the repository at this point in the history
  • Loading branch information
petark7 committed Dec 1, 2024
2 parents e35265b + 6cb1bad commit 68c9ce5
Show file tree
Hide file tree
Showing 49 changed files with 1,715 additions and 506 deletions.
1 change: 1 addition & 0 deletions .env.examples
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ NEXT_PUBLIC_API_BASE_URL=
API_KEY=
NEXT_PUBLIC_TURNSTILE=0x4AAAAAAAWq36_j09RgOKQR


# Test variables
BASE_URL = "http://localhost:3000"
DEBUG_LEVEL = "info" # info or debug
Expand Down
8 changes: 8 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@
},
"plugins": ["react", "prettier", "@typescript-eslint"],
"rules": {
"jsx-a11y/label-has-associated-control": [
"error",
{
"controlComponents": ["Field"],
"assert": "htmlFor",
"depth": 3
}
],
"no-param-reassign": "off",
"@typescript-eslint/no-unused-vars": [
"error",
Expand Down
36 changes: 36 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
## Summary of Changes

<!--
Provide a brief summary of the changes introduced in this pull request.
For example:
- Implemented user authentication using JWT.
- Refactored the data processing module for better performance.
- Updated the dashboard UI to enhance user experience.
-->

## Splash Damage

<!--
Describe any potential side effects or impacts these changes might have on other parts of the project.
For example:
- The new authentication system may require updating API endpoints.
- Refactoring may affect existing unit tests; ensure all tests pass.
- UI changes might require adjustments in mobile responsiveness.
-->

## Manual Test Steps

<!--
Outline the steps required to manually test and verify the changes made in this pull request.
For example:
1. **Setup:**
- Clone the repository and check out the feature branch.
- Run `npm install` to install dependencies.
2. **UI Verification:**
- Navigate to `http://localhost:3000/dashboard` and review the updated UI elements.
- Test responsiveness on different screen sizes.
3. **New Feature Access:**
- Go to `http://localhost:3000/new-feature` to access the newly implemented feature.
- Verify that all functionalities of the new feature work as expected.
- Check for any UI inconsistencies or errors.
-->
7 changes: 5 additions & 2 deletions .gitleaks.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
title = "Custom Gitleaks Config"

[allowlist]
files = ["package-lock.json"]
files = [
"package-lock.json",
"node_modules/"
]
regexes = ["https://registry\\.npmjs\\.org"]
paths = ['''package-lock\.json''']
paths=["package-lock.json"]

# Rule to detect API keys
[[rules]]
Expand Down
105 changes: 105 additions & 0 deletions Types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/* eslint-disable no-unused-vars */

import { MutationStatus, QueryStatus } from '@tanstack/react-query';
import { HTMLProps } from 'react';

export enum Role {
admin = 'admin',
content_manager = 'content_manager',
content = 'content',
member = 'member',
}

export type UserType = {
id: number | string;
is_verified: boolean;
email: string;
role: Role;
};

export interface LoginParams {
email: string;
password: string;
cfTurnstileResponse: string;
role?: string;
remember: boolean;
}

export interface AuthContextType {
user: UserType | null;
login: (params: LoginParams & { userType: string; redirectUrl: string }) => void;
logout: () => void;
userQuery: {
status: QueryStatus;
error: Error | null;
isLoading: boolean;
};
loginMutation: {
isLoading: boolean;
status: MutationStatus;
error: Error | null;
};
logoutMutation: {
isLoading: boolean;
status: MutationStatus;
error: Error | null;
};
loginStatus: MutationStatus;
}

export interface LoginResponse {
data: {
user: UserType;
access_token: string;
};
}

export interface Session {
token: string;
}

export interface LoginFormProps {
onSubmit: (values: LoginParams) => Promise<void>;
isLoading?: boolean;
turnstileToken?: string | null;
setTurnstileToken?: (token: string) => void;
}

export interface FormField {
name: string;
type: string;
label: string;
placeholder: string;
}

export interface SocialLink {
id: string;
icon: string;
url: string;
alt: string;
}

export interface ReusableFormProps {
title: string;
fields: FormField[];
initialValues: any;
validationSchema: any;
onSubmit: (values: any) => Promise<void>;
submitButtonText: string;
socialLinks?: SocialLink[];
alternativeActionText?: string;
alternativeActionLink?: string;
alternativeActionLinkText?: string;
}

export interface InputProps extends HTMLProps<HTMLInputElement> {
placeholder: string;
label: string;
name: string;
type: string;
field: string;
formik: any;
isRequired?: boolean;
isFooter?: boolean;
inputClass?: string[];
}
40 changes: 40 additions & 0 deletions Types/next-auth.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* eslint-disable */
/* tslint:disable */
/* eslint-disable @typescript-eslint/no-unused-vars */

// eslint-disable-next-line @typescript-eslint/no-unused-vars
import NextAuth from 'next-auth';
import { JWT } from 'next-auth/jwt';

declare module 'next-auth' {
interface Session {
accessToken: string | unknown;
expires: string;
user: {
id: string | unknown;
name?: string | unknown;
email?: string | unknown;
image?: string | unknown;
};
}
}

declare module 'next-auth' {
interface JWT {
accessToken?: string;
id: string;
name?: string;
email?: string;
exp?: number;
}
}
declare module 'next-auth' {
interface User {
id: string;
name?: string;
email?: string;
image?: string;
accessToken?: string;
remember?: boolean;
}
}
123 changes: 123 additions & 0 deletions api/authApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { LoginParams, LoginResponse, Role, UserType } from '../Types';
import {
getFromLocalStorage,
removeFromLocalStorage,
saveToLocalStorage,
} from './utils/localStorageUtils';

const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL || '';

export const getUser = async (): Promise<UserType | null> => {
const session = getFromLocalStorage('session');
if (!session?.token) {
console.log('No session token found, user is not authenticated');
return null;
}

try {
const response = await fetch(`${baseUrl}/content/user`, {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${session.token}`,
},
});

if (!response.ok) {
if (response.status === 401) {
console.log('Unauthorized access, clearing session');
removeFromLocalStorage('session');
removeFromLocalStorage('user');
}
throw new Error(response.statusText || 'An error occurred while fetching the user');
}

const data = await response.json();
console.log('User data fetched successfully:', data.data);
return data.data;
} catch (error) {
console.error('Error fetching user:', error);
return null;
}
};

export const login = async ({
email,
password,
cfTurnstileResponse,
}: LoginParams): Promise<LoginResponse> => {
console.log('Login attempt for email:', email);

try {
const response = await fetch(`${baseUrl}/content/login`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, password, cfTurnstileResponse }),
});

const data = await response.json();

if (!response.ok) {
console.error('Login error:', data);
if (response.status === 422) {
const errorMessage = data.message || 'Validation failed';
const errors = data.errors || {};
throw new Error(JSON.stringify({ message: errorMessage, errors }));
}
throw new Error(data.message || 'An error occurred while logging in');
}

console.log('Login successful');

if (data.data && data.data.access_token && data.data.user) {
console.log('Setting session and user data');
saveToLocalStorage('session', {
token: data.data.access_token,
role: Role.content,
});
saveToLocalStorage('user', data.data.user);
} else {
console.error('Login response is missing expected data');
throw new Error('Invalid login response data');
}

return data;
} catch (error) {
console.error('Login process failed:', error);
throw error;
}
};

export const logout = async (): Promise<void> => {
const session = getFromLocalStorage('session');
if (!session?.token) {
console.log('No session found, user is already logged out');
return;
}

try {
const response = await fetch(`${baseUrl}/content/logout`, {
method: 'POST',
headers: {
Accept: 'application/json',
Authorization: `Bearer ${session.token}`,
},
});

if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || 'An error occurred while logging out');
}

console.log('Logout successful');
} catch (error) {
console.error('Logout process failed:', error);
throw error;
} finally {
console.log('Clearing session');
removeFromLocalStorage('session');
removeFromLocalStorage('user');
}
};
38 changes: 38 additions & 0 deletions api/utils/actions/session.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Session, UserType } from '../../../Types';

export const setSession = async (sessionData: Session | null): Promise<void> => {
console.log('setSession called with:', sessionData);
if (sessionData === null) {
localStorage.removeItem('session');
console.log('Session removed from localStorage');
} else {
localStorage.setItem('session', JSON.stringify(sessionData));
console.log('Session saved to localStorage', localStorage.getItem('session'));
}
};

export const getSession = async (): Promise<Session | null> => {
const sessionData = localStorage.getItem('session');
return sessionData ? JSON.parse(sessionData) : null;
};

export const clearSession = async (): Promise<void> => {
localStorage.removeItem('session');
localStorage.removeItem('user');
};

export const setUser = async (user: UserType | null): Promise<void> => {
console.log('setUser called with:', user);
if (user === null) {
localStorage.removeItem('user');
console.log('User removed from localStorage');
} else {
localStorage.setItem('user', JSON.stringify(user));
console.log('User saved to localStorage');
}
};

export const getUserFromStorage = async (): Promise<UserType | null> => {
const userData = localStorage.getItem('user');
return userData ? JSON.parse(userData) : null;
};
Loading

0 comments on commit 68c9ce5

Please sign in to comment.