diff --git a/.gitignore b/.gitignore index 11ae0e2..124c14b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ src/google.py src/chromedriver +src/chromedriver.exe src/violations.txt # --- Frontend diff --git a/src/frontend/README.md b/src/frontend/README.md index 805300b..a620606 100644 --- a/src/frontend/README.md +++ b/src/frontend/README.md @@ -18,10 +18,34 @@ The `ViolatingSitesTable.tsx` component requires an active API Key to retrieve a 8. Copy the newly created API key 9. Create a **_.env_** at **_./src/frontend/_** in your project - The file should have the following contents: - ```bash - REACT_APP_AD_EXPERIENCE_API_KEY="[API_KEY]" + ```bash + REACT_APP_AD_EXPERIENCE_API_KEY=[API_KEY] + ``` + > Replace `[API_KEY]` with the key you copied in the previous step + +## Controlling Site Filtering +By default, when you run the frontend, the sites that populate the main table of violating sites are the first 9 sites in the response received from Google's Ad Experience Report API.
+ +However, there is the option to filter out sites that do not exist. To do so, change the `FILTER_SITES` constant in **_.\src\frontend\src\components\ViolatingSitesTable.tsx_** from `false` to `true`. +> **NOTE**: Filtering out sites can take a long time (anywhere from a few minutes to tens of minutes, depending on network speed). This will impact the time it takes to populate the table of violating sites, both on initial page load, and every time a new list is requested. + +## Controlling Port for the Backend +By default, port 8080 is used to run the Flask server. However, some situations may arise where you have to use a different port. The purpose of changing the port is to ensure the Flask server does not conflict with other applications/services and to ensure the frontend is using the correct port to communicate with the server. To change the port, follow these steps: +1. Change the port argument in the main function of **_./src/server.py_** + ```python + # BEFORE + if __name__ == '__main__': + app.run(port=8080, debug=True) + ``` + ```python + # AFTER + if __name__ == '__main__': + app.run(port=5000, debug=True) ``` - > Replace `[API_KEY]` with the key you copied at step 8 +2. Add a new environment variable to the **_./src/frontend/.env_** file. The variable will have your desired port number, like so: + ```bash + REACT_APP_BACKEND_PORT=5000 + ``` ## Documentation Reference diff --git a/src/frontend/package.json b/src/frontend/package.json index 7585884..fa3bcd9 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -1,7 +1,6 @@ { "name": "frontend", "version": "0.1.0", - "proxy": "http://localhost:8080", "private": true, "dependencies": { "@types/node": "^16.18.93", @@ -41,4 +40,4 @@ "devDependencies": { "tailwindcss": "^3.4.3" } -} +} \ No newline at end of file diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx index 4da42a3..959c98a 100644 --- a/src/frontend/src/App.tsx +++ b/src/frontend/src/App.tsx @@ -6,13 +6,13 @@ function App(): JSX.Element { return (
diff --git a/src/frontend/src/components/CustomURLTableRow.tsx b/src/frontend/src/components/CustomURLTableRow.tsx new file mode 100644 index 0000000..76d09e4 --- /dev/null +++ b/src/frontend/src/components/CustomURLTableRow.tsx @@ -0,0 +1,84 @@ +import { useState } from "react"; +import FixSuggestions from "./FixSuggestion"; + +export default function ViolatingSiteTableRow() { + const BACKEND_PORT = process.env.REACT_APP_BACKEND_PORT || 8080; // Port where the backend server is running + const [loading, setLoading] = useState(false); // State to show/hide loading spinner + const [showViolations, setShowViolations] = useState(false); // State to show/hide violations + const [url, setUrl] = useState(""); // State to store the URL entered by the user + + // Run AdHere.py against the url in this table row + const findViolations = async (site: string) => { + try { + setLoading(true); + + // Pass url to endpoint that runs AdHere + const response = await fetch( + // If 'options object' issues are encountered then delete the following line from package.json + // "proxy": "http://localhost:8080", + `http://localhost:${BACKEND_PORT}/find-violations?url=${encodeURIComponent(site)}`, + { + method: "GET", + } + ); + + // AdHere.py execution complete + if (!response.ok) { + throw new Error("Failed to fetch data"); + } else { + setShowViolations(true); // Trigger component to show violations + } + } catch (error) { + console.error("Error running AdHere on custom url ", site, " : ", error); + } finally { + setLoading(false); + } + }; + + return ( +
+ {/* Text box */} + setUrl(e.target.value.trim())}> + + + {/* Row Button */} + {/* Default: Run AdHere || Waiting on AdHere execution: circle_spin || Showing violations: Close */} + {!showViolations ? ( + loading ? ( +
+ loading spinner +
+ ) : ( + + ) + ) : ( + + )} + + {/* Render component to show violations only when AdHere completes execution */} + {showViolations && } +
+ ); +} diff --git a/src/frontend/src/components/FixSuggestion.tsx b/src/frontend/src/components/FixSuggestion.tsx index 9059f8d..a0b31a8 100644 --- a/src/frontend/src/components/FixSuggestion.tsx +++ b/src/frontend/src/components/FixSuggestion.tsx @@ -3,11 +3,13 @@ import CodeEditor from "@uiw/react-textarea-code-editor"; export default function FixSuggestions() { const [fixText, setFixText] = useState(""); + const BACKEND_PORT = process.env.REACT_APP_BACKEND_PORT || 8080; // Port where the backend server is running useEffect(() => { + // Fetch the fix suggestions from the Flask server const fetchData = async () => { try { - const response = await fetch("/get-violations"); + const response = await fetch(`http://localhost:${BACKEND_PORT}/get-violations`); const data = await response.text(); setFixText(data); } catch (error) { @@ -26,7 +28,8 @@ export default function FixSuggestions() { language="js" data-color-mode={localStorage.theme} style={{ - overflow: "scroll", + overflowY: "scroll", + overflowX: "hidden", height: "20rem", fontSize: "1rem", }} diff --git a/src/frontend/src/components/Header.tsx b/src/frontend/src/components/Header.tsx index f6b56de..64fd3a8 100644 --- a/src/frontend/src/components/Header.tsx +++ b/src/frontend/src/components/Header.tsx @@ -6,7 +6,7 @@ export default function Header() { const [pageTheme, setPageTheme] = useState("dark"); // Element where 'dark' class will be applied - const app = document.documentElement.getElementsByTagName("div"); + const divs = document.documentElement.getElementsByTagName("div"); const iconStyling = "text-zinc-700 dark:text-white hover:text-slate-400 cursor-pointer"; @@ -26,10 +26,10 @@ export default function Header() { // toggle between dark and light theme useEffect(() => { if (pageTheme === "dark") { - app[0].classList.add("dark"); + divs[0].classList.add("dark"); localStorage.theme = "dark"; } else { - app[0].classList.remove("dark"); + divs[0].classList.remove("dark"); localStorage.theme = "light"; } // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/src/frontend/src/components/ViolatingSiteTableRow.tsx b/src/frontend/src/components/ViolatingSiteTableRow.tsx index 984875f..2ca508e 100644 --- a/src/frontend/src/components/ViolatingSiteTableRow.tsx +++ b/src/frontend/src/components/ViolatingSiteTableRow.tsx @@ -7,11 +7,12 @@ interface RowProps { } export default function ViolatingSiteTableRow(props: RowProps) { - const [loading, setLoading] = useState(false); - const [showViolations, setShowViolations] = useState(false); + const BACKEND_PORT = process.env.REACT_APP_BACKEND_PORT || 8080; // Port where the backend server is running + const [loading, setLoading] = useState(false); // State to show/hide loading spinner + const [showViolations, setShowViolations] = useState(false); // State to show/hide violations // Run AdHere.py against the url in this table row - const findViolations = async (site: string, index: number) => { + const findViolations = async (site: string) => { try { setLoading(true); @@ -19,7 +20,7 @@ export default function ViolatingSiteTableRow(props: RowProps) { const response = await fetch( // If 'options object' issues are encountered then delete the following line from package.json // "proxy": "http://localhost:8080", - `http://localhost:8080/find-violations?url=${encodeURIComponent(site)}`, + `http://localhost:${BACKEND_PORT}/find-violations?url=${encodeURIComponent(site)}`, { method: "GET", } @@ -29,7 +30,7 @@ export default function ViolatingSiteTableRow(props: RowProps) { if (!response.ok) { throw new Error("Failed to fetch data"); } else { - setShowViolations(true); // Trigger component @1 + setShowViolations(true); // Trigger component to show violations } } catch (error) { console.error("Error running AdHere on ", site, ": ", error); @@ -41,17 +42,15 @@ export default function ViolatingSiteTableRow(props: RowProps) { return (
{/* Site url */}

{props.url}

@@ -70,7 +69,7 @@ export default function ViolatingSiteTableRow(props: RowProps) {
) : ( )} - {/* @1 Render component to show violations only when AdHere completes execution */} + {/* Render component to show violations only when AdHere completes execution */} {showViolations && }
); diff --git a/src/frontend/src/components/ViolatingSitesTable.tsx b/src/frontend/src/components/ViolatingSitesTable.tsx index 5cae20f..c5e4854 100644 --- a/src/frontend/src/components/ViolatingSitesTable.tsx +++ b/src/frontend/src/components/ViolatingSitesTable.tsx @@ -1,11 +1,14 @@ import { useEffect, useState } from "react"; import ViolatingSiteTableRow from "./ViolatingSiteTableRow"; +import CustomURLTableRow from "./CustomURLTableRow"; export default function ViolatingSitesTable() { + const FILTER_SITES = false; + const BACKEND_PORT = process.env.REACT_APP_BACKEND_PORT || 8080; // Port where the backend server is running const [loading, setLoading] = useState(false); const [violatingSites, setViolatingSites] = useState([]); - const numSitesToShow = 10; + const numSitesToShow = 9; // Get list of violating sites const getNewSites = async () => { @@ -32,22 +35,29 @@ export default function ViolatingSitesTable() { violatingSitesIndex < data.violatingSites.length ) { const site = data.violatingSites[violatingSitesIndex++].reviewedSite; - // Calls Flask server endpoint which returns true or false depending on whether or not the site exists - await fetch( - `http://localhost:8080/url-check?url=${encodeURIComponent(site)}` - ) - .then((response) => response.json()) - .then((data) => { - if ( - data.does_url_exist && - filteredSites.length !== numSitesToShow - ) { - filteredSites.push(site); - } - }) - .catch((error) => { - console.error("Error:", error); - }); + + if (FILTER_SITES) { + // Calls Flask server endpoint which returns true or false depending on whether or not the site exists + await fetch( + `http://localhost:${BACKEND_PORT}/url-check?url=${encodeURIComponent(site)}` + ) + .then((response) => response.json()) + .then((data) => { + if ( + data.does_url_exist && + filteredSites.length !== numSitesToShow + ) { + filteredSites.push(site); + } + }) + .catch((error) => { + console.error("Error:", error); + }); + } + else { + // If we're not filtering sites, just add them to the list + filteredSites.push(site); + } } // Update state with the new sites @@ -65,7 +75,7 @@ export default function ViolatingSitesTable() { }, []); return ( -
+
{/* Row plus button for getting new sites */}
) : ( - // Map over sites and display them - violatingSites.map((site, index) => ( - - )) +
+ + {// Map over sites and display them + violatingSites.map((site, index) => ( + + ))} + +
)}
);