diff --git a/src/PolicyEngine.jsx b/src/PolicyEngine.jsx index 8ed2c72f2..c22fa7e29 100644 --- a/src/PolicyEngine.jsx +++ b/src/PolicyEngine.jsx @@ -44,6 +44,8 @@ import CountryIdLayout from "./routing/CountryIdLayout"; import RedirectBlogPost from "./routing/RedirectBlogPost"; import { StatusPage } from "./pages/StatusPage"; import ManifestosComparison from "./applets/ManifestosComparison"; +import DeveloperLayout from "./pages/DeveloperLayout"; +import DeveloperHome from "./pages/DeveloperHome"; import CTCComparison from "./applets/CTCComparison"; import { wrappedResponseJson } from "./data/wrappedJson"; @@ -299,6 +301,11 @@ export default function PolicyEngine() { } /> } /> } /> + }> + } /> + } /> + } /> + }> } /> } /> @@ -350,7 +357,6 @@ export default function PolicyEngine() { } /> } /> - } /> } diff --git a/src/data/developerToolsList.js b/src/data/developerToolsList.js new file mode 100644 index 000000000..9d1ff1121 --- /dev/null +++ b/src/data/developerToolsList.js @@ -0,0 +1,18 @@ +// A JSON data structure that describes PolicyEngine Developer tools, +// for use on the Developer Hub page +import apiImage from "../images/devTools/apistatus.png"; +import simImage from "../images/devTools/simulation.png"; +export const devTools = [ + { + title: "API Status", + desc: "Monitor the health and performance of the PolicyEngine API. View real-time status updates, scheduled maintenance notifications, and historical incident data to stay informed about the API's reliability and availability.", + path: "api_status", + image: apiImage, + }, + { + title: "Policy Simulations", + desc: "View simulations that are currently running or have run in the past for the current version of the API. The table below updates every 15 seconds, providing you with real-time insights into your policy testing.", + path: "simulations", + image: simImage, + }, +]; diff --git a/src/images/devTools/apistatus.png b/src/images/devTools/apistatus.png new file mode 100644 index 000000000..12cd8527c Binary files /dev/null and b/src/images/devTools/apistatus.png differ diff --git a/src/images/devTools/simulation.png b/src/images/devTools/simulation.png new file mode 100644 index 000000000..360ca6d96 Binary files /dev/null and b/src/images/devTools/simulation.png differ diff --git a/src/layout/Footer.jsx b/src/layout/Footer.jsx index 22b5ddc6a..6821cf93e 100644 --- a/src/layout/Footer.jsx +++ b/src/layout/Footer.jsx @@ -62,6 +62,11 @@ function LinkSection() { label: "Terms and Conditions", isInternal: true, }, + { + link: `/${countryId}/developer-tools`, + label: "Developer Tools", + isInternal: true, + }, ]; const links = linkData.map((link, index) => { diff --git a/src/pages/DeveloperHome.jsx b/src/pages/DeveloperHome.jsx new file mode 100644 index 000000000..4dae1aabb --- /dev/null +++ b/src/pages/DeveloperHome.jsx @@ -0,0 +1,192 @@ +import React, { useEffect } from "react"; +import Section from "../layout/Section"; +import { devTools } from "../data/developerToolsList"; +import style from "../style/index.js"; +import LinkButton from "../controls/LinkButton.jsx"; +import useDisplayCategory from "../hooks/useDisplayCategory.js"; +import { useLocation } from "react-router-dom"; + +const DeveloperHome = () => { + const displayCategory = useDisplayCategory(); + const { pathname } = useLocation(); + + useEffect(() => { + window.scrollTo(0, 0); + }, [pathname]); + return ( + <> +
+
+ {devTools.map((tool, index) => ( + + ))} +
+
+ + ); +}; + +export default DeveloperHome; + +function ToolsCard({ tool }) { + const displayCategory = useDisplayCategory(); + const mobile = displayCategory === "mobile"; + const tablet = displayCategory === "tablet"; + + return ( + + {" "} + {/* Set width to 100% */} + + + } + bottomRight={ +
+ +
+ } + style={{ + backgroundColor: style.colors.LIGHT_GRAY, + // maxWidth: "1000px", // Set a max width for ToolBox + height: "100%", + position: "relative", + }} + > +
+

+ {tool.title} +

+ {displayCategory !== "mobile" && ( +

+ {tool.desc} +

+ )} +
+
+ ); +} + +function ToolsLayout({ + children, + top, + tablet, + + bottomRight, + noBorder, + style, + mobile, +}) { + return ( +
+
+
+ {top} +
+
+ {children} +
+ {/*
{bottomLeft}
*/} +
{bottomRight}
+
+
+
+
+ ); +} diff --git a/src/pages/DeveloperLayout.jsx b/src/pages/DeveloperLayout.jsx new file mode 100644 index 000000000..b8dc45435 --- /dev/null +++ b/src/pages/DeveloperLayout.jsx @@ -0,0 +1,43 @@ +import Header from "../layout/Header.jsx"; +import Footer from "../layout/Footer.jsx"; +import style from "../style/index.js"; +import PageHeader from "../layout/PageHeader.jsx"; +import { Outlet, useLocation } from "react-router-dom"; +import { Helmet } from "react-helmet"; + +export default function DeveloperLayout() { + const { pathname } = useLocation(); + + if (pathname.length > 20) { + return ( +
+ +
+ ); + } + + return ( +
+ + Developer Tools | PolicyEngine + +
+
+ + +

+ Welcome to the Developer Tools page for PolicyEngine. This hub is + designed to enhance your experience with our open-source projects by + providing quick access to essential resources. +

+
+ + +
+
+ ); +} diff --git a/src/pages/Simulations.jsx b/src/pages/Simulations.jsx index a1e022865..b1e274ea6 100644 --- a/src/pages/Simulations.jsx +++ b/src/pages/Simulations.jsx @@ -1,4 +1,3 @@ -import Header from "../layout/Header"; import Footer from "../layout/Footer"; import { apiCall } from "../api/call"; import { Table } from "antd"; @@ -13,6 +12,7 @@ import CodeBlock from "../layout/CodeBlock"; import PageHeader from "../layout/PageHeader"; import style from "../style"; import { Link } from "react-router-dom"; +import Header from "../layout/Header"; export default function SimulationsPage() { // call /simulations endpoint, which returns diff --git a/src/pages/StatusPage.jsx b/src/pages/StatusPage.jsx index 9073c8d34..3f27b87ae 100644 --- a/src/pages/StatusPage.jsx +++ b/src/pages/StatusPage.jsx @@ -1,7 +1,6 @@ import { useEffect, useState } from "react"; import useMobile from "../layout/Responsive"; -import Header from "../layout/Header"; import Footer from "../layout/Footer"; import { countryApiCall, apiCall } from "../api/call"; import { @@ -13,6 +12,7 @@ import { COUNTRY_NAMES, } from "../data/countries"; import { Helmet } from "react-helmet"; +import Header from "../layout/Header"; import { wrappedResponseJson } from "../data/wrappedJson"; function ApiStatus({ apiStatus, apiCategory, countryNames }) {