Skip to content

Commit

Permalink
Task R: Add authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
WeiViv committed Oct 26, 2024
1 parent c8bad1f commit 9b29ae1
Show file tree
Hide file tree
Showing 12 changed files with 159 additions and 44 deletions.
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
7 changes: 4 additions & 3 deletions database.rules.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
/* Visit https://firebase.google.com/docs/database/security to learn more about security rules. */
"rules": {
".read": true,
".write": true
"courses": {
".read": true,
".write": "auth != null"
}
}
}
2 changes: 1 addition & 1 deletion src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ const App = () => (
</QueryClientProvider>
);

export default App;
export default App;
28 changes: 28 additions & 0 deletions src/App.test-first-mock.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {describe, it, vi} from 'vitest';
import {render, screen} from '@testing-library/react';
import App from './App';
import {useAuthState, useDbData} from './utilities/firebase';

const mockSchedule = {
"title": "CS Courses for 1850-1851",
"courses": {
}
};

vi.mock('./utilities/firebase');

beforeEach(() => {
useDbData.mockReturnValue([mockSchedule, null]);
useAuthState.mockReturnValue([null]);
});

afterEach(() => {
vi.resetAllMocks();
});

describe('launching', () => {
it('should show the current year', () => {
render(<App />);
screen.getByText(/1850/);
});
});
47 changes: 34 additions & 13 deletions src/App.test.jsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,40 @@
import {describe, expect, test} from 'vitest';
import {fireEvent, render, screen} from '@testing-library/react';
// import {describe, it,} from 'vitest';
// import {render, screen} from '@testing-library/react';
// import App from './App';

// describe('launching', () => {
// it('should show the current year', async () => {
// render(<App />);
// await screen.findByText(/2018/);
// });
// });

import {describe, it, vi} from 'vitest';
import {render, screen} from '@testing-library/react';
import App from './App';
import {useAuthState, useDbData} from './utilities/firebase';

describe('counter tests', () => {

test("Counter should be 0 at the start", () => {
render(<App />);
expect(screen.getByText('count is: 0')).toBeDefined();
});
const mockSchedule = {
"title": "CS Courses for 1850-1851",
"courses": {
}
};

vi.mock('./utilities/firebase');

beforeEach(() => {
useDbData.mockReturnValue([mockSchedule, null]);
useAuthState.mockReturnValue([null]);
});

afterEach(() => {
vi.resetAllMocks();
});

test("Counter should increment by one when clicked", async () => {
describe('launching', () => {
it('should show the current year', () => {
render(<App />);
const counter = screen.getByRole('button');
fireEvent.click(counter);
expect(await screen.getByText('count is: 1')).toBeDefined();
screen.getByText(/1850/);
});
});

});
27 changes: 25 additions & 2 deletions src/components/Banner.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
const Banner = ({title}) => (
<center><h1>{title}</h1></center>
import { signInWithGoogle, signOut, useAuthState } from "../utilities/firebase";

const SignInButton = () => (
<button className="ms-auto btn btn-outline-success" onClick={signInWithGoogle}>Sign in</button>
);

const SignOutButton = () => (
<button className="ms-auto btn btn-outline-danger" onClick={signOut}>Sign out</button>
);

const AuthButton = () => {
const [user] = useAuthState();
return user ? <SignOutButton /> : <SignInButton />;
};
const Banner = ({title}) => {
return(
<div className="tittle">
<br/>
<center><h1>{title}</h1></center>
<div className="log-in">
<AuthButton />
</div>
</div>
);
};


export default Banner;
17 changes: 11 additions & 6 deletions src/components/CourseForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,17 @@ const CourseForm = ({ course , courseID }) => {
}else if(initialTittle === formValues.title && initialMeets === formValues.meets){
setNoChangesWarning(true);
} else {
// Trigger the update
updateData(formValues);

// If update is successful, navigate back
console.log("Form submitted successfully:", formValues);
navigate(-1);// Redirect back to previous page
updateData({ title, meets });
console.log(result?.status);

if (result?.status === 'success') {
// If update is successful, navigate back
console.log("Form submitted successfully:", formValues);
navigate(-1);// Redirect back to previous page
} else if (result?.status === 'error') {
console.error("Error updating course:", result.error);
alert("Failed to update course.");
}
}
};

Expand Down
14 changes: 14 additions & 0 deletions src/components/CourseFormWrapper.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import CourseForm from './CourseForm';
const CourseFormWrapper = ({ courses, user }) => {
const { courseId } = useParams(); // Get the courseId from the URL
const course = courses[courseId];
const navigate = useNavigate();
if (!user || !course) {
navigate('/');
return null;
}
return <CourseForm course={course} courseId={courseId} />;
};
export default CourseFormWrapper;
16 changes: 9 additions & 7 deletions src/components/CourseList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import { Link } from 'react-router-dom';
import './CourseList.css';

const CourseList = ({ courses, selectedTerm, selectedCourse, conflictingCourses, toggleSelectedCourse }) => {
const CourseList = ({ courses, selectedTerm, selectedCourse, conflictingCourses, toggleSelectedCourse, user }) => {
const filteredCourses = Object.entries(courses).filter(([key, course]) => course.term === selectedTerm);

return (
Expand All @@ -22,12 +22,14 @@ const CourseList = ({ courses, selectedTerm, selectedCourse, conflictingCourses,
>
<div className="card-body">
<h5 className="card-title">{course.term} CS {course.number}</h5>
<Link to={`/edit/${key}`} className="btn edit-button">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil-square" viewBox="0 0 16 16">
<path d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/>
<path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5z"/>
</svg>
</Link>
{user && (
<Link to={`/edit/${key}`} className="btn edit-button">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil-square" viewBox="0 0 16 16">
<path d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/>
<path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5z"/>
</svg>
</Link>
)}
<p className="card-text">{course.title}</p>
<div className="course-info-divider">
<hr className="divider-line" />
Expand Down
19 changes: 8 additions & 11 deletions src/components/Main.jsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
import Banner from './Banner';
import TermPage from './TermPage';
import CourseForm from './CourseForm';
import { BrowserRouter, Routes, Route, useParams} from "react-router-dom";
import CourseFormWrapper from './CourseFormWrapper';

import { useDbData } from "../utilities/firebase";
import { BrowserRouter, Routes, Route} from "react-router-dom";

const CourseFormWrapper = ({ courses }) => {
const { courseID } = useParams(); // Get the courseId from the URL
const course = courses[courseID];
return <CourseForm course={course} courseID={courseID}/>;
};
import { useAuthState, useDbData } from "../utilities/firebase";

const Main = () => {
const [data, error] = useDbData('/');
const [user] = useAuthState();
console.log("Authenticated user:", user);

if (error) return <h1>Error loading data: {error.toString()}</h1>;
if (data === undefined) return <h1>Loading data...</h1>;
if (data === undefined || user === undefined) return <h1>Loading data...</h1>;
if (!data) return <h1>No data found</h1>;

return (
Expand All @@ -24,8 +21,8 @@ const Main = () => {
<Banner title={data.title}></Banner>
<br/>
<Routes>
<Route path="/" element={<TermPage courses={data.courses}/>} />
<Route path="/edit/:courseID" element={<CourseFormWrapper courses={data.courses} />} />
<Route path="/" element={<TermPage courses={data.courses} user={user} />} />
<Route path="/edit/:courseId" element={<CourseFormWrapper courses={data.courses} user={user} />} />
</Routes>
</BrowserRouter>

Expand Down
3 changes: 2 additions & 1 deletion src/components/TermPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import CourseList from './CourseList';
import Modal from './Modal';
import { hasConflictWithSelected } from '../utilities/timeUtils';

const TermPage = ({ courses }) => {
const TermPage = ({ courses, user }) => {
const [selectedTerm, setSelectedTerm] = useState("Fall");
const [selectedCourse, setSelectedCourse] = useState([]);
const [conflictingCourses, setConflictingCourses] = useState([]); // New state for conflicts
Expand Down Expand Up @@ -46,6 +46,7 @@ const TermPage = ({ courses }) => {
selectedCourse={selectedCourse}
conflictingCourses={conflictingCourses} // Pass conflicts to CourseList
toggleSelectedCourse={toggleSelectedCourse}
user={user}
/>
</div>
);
Expand Down
21 changes: 21 additions & 0 deletions src/utilities/firebase.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { initializeApp } from "firebase/app";
import { getDatabase, onValue, ref, update } from 'firebase/database';
import { getAnalytics } from "firebase/analytics";

import { getAuth, GoogleAuthProvider, onAuthStateChanged, signInWithPopup, signOut } from 'firebase/auth';


const firebaseConfig = {
apiKey: "AIzaSyC0JgjKavj6fky5dye1z3CkzlELTEc_PEg",
authDomain: "wei-sun-react-app-9d712.firebaseapp.com",
Expand Down Expand Up @@ -43,4 +46,22 @@ export const useDbUpdate = (path) => {
.catch((error) => setResult(makeResult(error)))
}, [database, path]);
return [updateData, result];
};

export const signInWithGoogle = () => {
signInWithPopup(getAuth(firebase), new GoogleAuthProvider());
};

const firebaseSignOut = () => signOut(getAuth(firebase));

export { firebaseSignOut as signOut };

export const useAuthState = () => {
const [user, setUser] = useState();

useEffect(() => (
onAuthStateChanged(getAuth(firebase), setUser)
), []);

return [user];
};

0 comments on commit 9b29ae1

Please sign in to comment.