Skip to content

Commit

Permalink
Setup dash route
Browse files Browse the repository at this point in the history
* add frame for the initial widgets
* refactor routes to constant to not repeat paths
  • Loading branch information
igorschoester committed Nov 11, 2024
1 parent 938cc50 commit 488a5dd
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 10 deletions.
52 changes: 52 additions & 0 deletions packages/js/src/dash/components/dashboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import PropTypes from "prop-types";
import { PageTitle } from "./page-title";
import { SeoScores } from "./seo-scores";

/**
* @typedef {Object} Taxonomy A taxonomy.
* @property {string} name The unique identifier.
* @property {string} label The user-facing label.
* @property {Object} links The links.
* @property {string} [links.search] The search link, might not exist.
*/

/**
* @typedef {Object} ContentType A content type.
* @property {string} name The unique identifier.
* @property {string} label The user-facing label.
* @property {Taxonomy|null} taxonomy The (main) taxonomy or null.
*/

/**
* @param {ContentType[]} contentTypes The content types.
* @param {string} userName The user name.
* @returns {JSX.Element} The element.
*/
export const Dashboard = ( { contentTypes, userName } ) => {
return (
<div className="yst-@container">
<PageTitle userName={ userName } />
<div className="yst-flex yst-flex-col @7xl:yst-flex-row yst-gap-6 yst-mt-6">
<SeoScores contentTypes={ contentTypes } />
<SeoScores contentTypes={ contentTypes } />
</div>
</div>
);
};

Dashboard.propTypes = {
contentTypes: PropTypes.arrayOf(
PropTypes.shape( {
name: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
taxonomy: PropTypes.shape( {
name: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
links: PropTypes.shape( {
search: PropTypes.string,
} ).isRequired,
} ),
} )
).isRequired,
userName: PropTypes.string.isRequired,
};
27 changes: 27 additions & 0 deletions packages/js/src/dash/components/page-title.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { __, sprintf } from "@wordpress/i18n";
import { Paper, Title } from "@yoast/ui-library";
import PropTypes from "prop-types";

/**
* @param {string} userName The user name.
* @returns {JSX.Element} The element.
*/
export const PageTitle = ( { userName } ) => (
<Paper>
<Paper.Content>
<Title as="h1">
{ sprintf(
__( "Hi %s!", "wordpress-seo" ),
userName
) }
</Title>
<p className="yst-mt-3 yst-text-tiny">
{ __( "Welcome to your SEO dashboard! Don't forget to check it regularly to see how your site is performing and if there are any important tasks waiting for you.", "wordpress-seo" ) }
</p>
</Paper.Content>
</Paper>
);

PageTitle.propTypes = {
userName: PropTypes.string.isRequired,
};
39 changes: 39 additions & 0 deletions packages/js/src/dash/components/seo-scores.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { __ } from "@wordpress/i18n";
import { AutocompleteField, Paper, Title } from "@yoast/ui-library";
import PropTypes from "prop-types";

/**
* @returns {JSX.Element} The element.
*/
export const SeoScores = ( { contentTypes } ) => { // eslint-disable-line no-unused-vars
return (
<Paper className="yst-@container yst-grow yst-max-w-screen-sm yst-p-8">
<Title as="h2">{ __( "SEO scores", "wordpress-seo" ) }</Title>
<div className="yst-grid yst-grid-cols-1 @md:yst-grid-cols-2 yst-gap-6 yst-mt-4">
<AutocompleteField />
<AutocompleteField />
</div>
<p className="yst-my-6">{ __( "description", "wordpress-seo" ) }</p>
<div className="yst-grid yst-grid-cols-1 @md:yst-grid-cols-3 yst-gap-6">
<div className="yst-col-span-2">Scores</div>
<div>chart</div>
</div>
</Paper>
);
};

SeoScores.propTypes = {
contentTypes: PropTypes.arrayOf(
PropTypes.shape( {
name: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
taxonomy: PropTypes.shape( {
name: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
links: PropTypes.shape( {
search: PropTypes.string,
} ).isRequired,
} ),
} )
).isRequired,
};
1 change: 1 addition & 0 deletions packages/js/src/dash/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Dashboard } from "./components/dashboard";
18 changes: 14 additions & 4 deletions packages/js/src/general/app.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable complexity */

import { Transition } from "@headlessui/react";
import { AdjustmentsIcon, BellIcon } from "@heroicons/react/outline";
import { AdjustmentsIcon, BellIcon, ChartPieIcon } from "@heroicons/react/outline";
import { useDispatch, useSelect } from "@wordpress/data";
import { useCallback, useEffect, useMemo } from "@wordpress/element";
import { __ } from "@wordpress/i18n";
Expand All @@ -16,6 +16,7 @@ import { MenuItemLink, YoastLogo } from "../shared-admin/components";
import { Notice } from "./components";
import { STORE_NAME } from "./constants";
import { useNotificationCountSync, useSelectGeneralPage } from "./hooks";
import { ROUTES } from "./routes";

/**
* @param {string} [idSuffix] Extra id suffix. Can prevent double IDs on the page.
Expand All @@ -39,7 +40,16 @@ const Menu = ( { idSuffix = "" } ) => {
<div className="yst-px-0.5 yst-space-y-6">
<ul className="yst-mt-1 yst-space-y-1">
<MenuItemLink
to="/"
to={ ROUTES.dashboard }
label={ <>
<ChartPieIcon className="yst-sidebar-navigation__icon yst-w-6 yst-h-6" />
{ __( "Dashboard", "wordpress-seo" ) }
</> }
idSuffix={ idSuffix }
className="yst-gap-3"
/>
<MenuItemLink
to={ ROUTES.alertCenter }
label={ <>
<BellIcon className="yst-sidebar-navigation__icon yst-w-6 yst-h-6" />
{ __( "Alert center", "wordpress-seo" ) }
Expand All @@ -48,7 +58,7 @@ const Menu = ( { idSuffix = "" } ) => {
className="yst-gap-3"
/>
<MenuItemLink
to="/first-time-configuration"
to={ ROUTES.firstTimeConfiguration }
label={ <>
<AdjustmentsIcon className="yst-sidebar-navigation__icon yst-w-6 yst-h-6" />
{ __( "First-time configuration", "wordpress-seo" ) }
Expand Down Expand Up @@ -118,7 +128,7 @@ const App = () => {
enterFrom="yst-opacity-0"
enterTo="yst-opacity-100"
>
{ pathname !== "/first-time-configuration" && <div>
{ pathname !== ROUTES.firstTimeConfiguration && <div>
{ shouldShowWebinarPromotionNotificationInDashboard( STORE_NAME ) &&
<WebinarPromoNotification store={ STORE_NAME } url={ webinarIntroSettingsUrl } image={ null } />
}
Expand Down
37 changes: 31 additions & 6 deletions packages/js/src/general/initialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ import { render } from "@wordpress/element";
import { Root } from "@yoast/ui-library";
import { get } from "lodash";
import { createHashRouter, createRoutesFromElements, Navigate, Route, RouterProvider } from "react-router-dom";
import { Dashboard } from "../dash";
import { LINK_PARAMS_NAME } from "../shared-admin/store";
import { FTC_NAME } from "./store/first-time-configuration";
import App from "./app";
import { RouteErrorFallback } from "./components";
import { STORE_NAME } from "./constants";
import { AlertCenter, FirstTimeConfiguration } from "./routes";
import { AlertCenter, FirstTimeConfiguration, ROUTES } from "./routes";
import registerStore from "./store";
import { ALERT_CENTER_NAME } from "./store/alert-center";
import { FTC_NAME } from "./store/first-time-configuration";

domReady( () => {
const root = document.getElementById( "yoast-seo-general" );
Expand All @@ -31,19 +32,43 @@ domReady( () => {
} );
const isRtl = select( STORE_NAME ).selectPreference( "isRtl", false );

const contentTypes = get( window, "wpseoScriptData.dash.contentTypes", [
{
name: "post",
label: "Posts",
taxonomy: {
name: "category",
label: "Categories",
links: {
search: "http://basic.wordpress.test/wp-json/wp/v2/categories",
},
},
},
{
name: "page",
label: "Pages",
taxonomy: null,
},
] );
const userName = get( window, "wpseoScriptData.dash.userName", "User" );

const router = createHashRouter(
createRoutesFromElements(
<Route path="/" element={ <App /> } errorElement={ <RouteErrorFallback className="yst-m-8" /> }>
<Route path="/" element={ <AlertCenter /> } errorElement={ <RouteErrorFallback /> } />
<Route path="/first-time-configuration" element={ <FirstTimeConfiguration /> } errorElement={ <RouteErrorFallback /> } />
<Route
path={ ROUTES.dashboard }
element={ <Dashboard contentTypes={ contentTypes } userName={ userName } /> } errorElement={ <RouteErrorFallback /> }
/>
<Route path={ ROUTES.alertCenter } element={ <AlertCenter /> } errorElement={ <RouteErrorFallback /> } />
<Route path={ ROUTES.firstTimeConfiguration } element={ <FirstTimeConfiguration /> } errorElement={ <RouteErrorFallback /> } />
{
/**
* Fallback route: redirect to the root (alert center).
* Fallback route: redirect to the dashboard.
* A redirect is used to support the activePath in the menu. E.g. `pathname` matches exactly.
* It replaces the current path to not introduce invalid history in the browser (that would just redirect again).
*/
}
<Route path="*" element={ <Navigate to="/" replace={ true } /> } />
<Route path="*" element={ <Navigate to={ ROUTES.dashboard } replace={ true } /> } />
</Route>
)
);
Expand Down
6 changes: 6 additions & 0 deletions packages/js/src/general/routes/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
export { FirstTimeConfiguration } from "./first-time-configuration";
export { AlertCenter } from "./alert-center";

export const ROUTES = {
dashboard: "/",
alertCenter: "/alert-center",
firstTimeConfiguration: "/first-time-configuration",
};

0 comments on commit 488a5dd

Please sign in to comment.