Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

demo: Old editor for show & tell #3362

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 60 additions & 85 deletions editor.planx.uk/src/@planx/components/PlanningConstraints/Public.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,27 @@ import type {
GISResponse,
} from "@opensystemslab/planx-core/types";
import Card from "@planx/components/shared/Preview/Card";
import QuestionHeader from "@planx/components/shared/Preview/QuestionHeader";
import CardHeader from "@planx/components/shared/Preview/CardHeader";
import type { PublicProps } from "@planx/components/ui";
import DelayedLoadingIndicator from "components/DelayedLoadingIndicator";
import { useFormik } from "formik";
import { submitFeedback } from "lib/feedback";
import capitalize from "lodash/capitalize";
import { useStore } from "pages/FlowEditor/lib/store";
import { handleSubmit } from "pages/Preview/Node";
import React from "react";
import useSWR, { Fetcher } from "swr";
import { FONT_WEIGHT_SEMI_BOLD } from "theme";
import ReactMarkdownOrHtml from "ui/shared/ReactMarkdownOrHtml";
import { stringify } from "wkt";

import { SiteAddress } from "../FindProperty/model";
import { ErrorSummaryContainer } from "../shared/Preview/ErrorSummaryContainer";
import SimpleExpand from "../shared/Preview/SimpleExpand";
import { WarningContainer } from "../shared/Preview/WarningContainer";
import ConstraintsList from "./List";
import type { IntersectingConstraints, PlanningConstraints } from "./model";
import {
DEFAULT_PLANNING_CONDITIONS_DISCLAIMER,
type IntersectingConstraints,
type PlanningConstraints,
} from "./model";

type Props = PublicProps<PlanningConstraints>;

Expand Down Expand Up @@ -55,20 +57,11 @@ function Component(props: Props) {
const wktPolygon: string | undefined =
siteBoundary && stringify(siteBoundary);

// Configure which planx teams should query Digital Land (or continue to use custom GIS) and set URL params accordingly
// In future, Digital Land will theoretically support any UK address and this list won't be necessary, but data collection still limited to select councils!
const digitalLandOrganisations: string[] = [
"opensystemslab",
"buckinghamshire",
"canterbury",
"camden",
"doncaster",
"gloucester",
"lambeth",
"medway",
"newcastle",
"southwark",
];
// Check if this team should query Planning Data (or continue to use custom GIS) and set URL params accordingly
// In future, Planning Data will theoretically support any UK address and this db setting won't be necessary, but data collection still limited to select councils!
const hasPlanningData = useStore(
(state) => state.teamIntegrations?.hasPlanningData,
);

const digitalLandParams: Record<string, string> = {
geom: wktPolygon || wktPoint,
Expand All @@ -86,9 +79,7 @@ function Component(props: Props) {
const teamGisEndpoint: string =
root +
new URLSearchParams(
digitalLandOrganisations.includes(teamSlug)
? digitalLandParams
: customGisParams,
hasPlanningData ? digitalLandParams : customGisParams,
).toString();

const fetcher: Fetcher<GISResponse | GISResponse["constraints"]> = (
Expand Down Expand Up @@ -116,10 +107,7 @@ function Component(props: Props) {
error: roadsError,
isValidating: isValidatingRoads,
} = useSWR(
() =>
usrn && digitalLandOrganisations.includes(teamSlug)
? classifiedRoadsEndpoint
: null,
() => (usrn && hasPlanningData ? classifiedRoadsEndpoint : null),
fetcher,
{ revalidateOnFocus: false },
);
Expand All @@ -144,14 +132,14 @@ function Component(props: Props) {
title={props.title}
description={props.description || ""}
fn={props.fn}
disclaimer={props.disclaimer}
constraints={constraints}
metadata={metadata}
previousFeedback={props.previouslySubmittedData?.feedback}
handleSubmit={(values: { feedback?: string }) => {
handleSubmit={() => {
const _constraints: Array<
EnhancedGISResponse | GISResponse["constraints"]
> = [];
if (digitalLandOrganisations.includes(teamSlug)) {
if (hasPlanningData) {
if (data && !dataError)
_constraints.push({
...data,
Expand Down Expand Up @@ -185,15 +173,14 @@ function Component(props: Props) {
};

props.handleSubmit?.({
...values,
data: passportData,
});
}}
refreshConstraints={() => mutate()}
/>
) : (
<Card handleSubmit={props.handleSubmit} isValid>
<QuestionHeader
<CardHeader
title={props.title}
description={props.description || ""}
/>
Expand All @@ -208,11 +195,11 @@ export type PlanningConstraintsContentProps = {
title: string;
description: string;
fn: string;
disclaimer: string;
constraints: GISResponse["constraints"];
metadata: GISResponse["metadata"];
handleSubmit: (values: { feedback: string }) => void;
handleSubmit: () => void;
refreshConstraints: () => void;
previousFeedback?: string;
};

export function PlanningConstraintsContent(
Expand All @@ -223,25 +210,9 @@ export function PlanningConstraintsContent(
description,
constraints,
metadata,
handleSubmit,
refreshConstraints,
previousFeedback,
disclaimer,
} = props;
const formik = useFormik({
initialValues: {
feedback: previousFeedback || "",
},
onSubmit: (values) => {
if (values.feedback) {
submitFeedback(
values.feedback,
"Inaccurate planning constraints",
constraints,
);
}
handleSubmit?.(values);
},
});
const error = constraints.error || undefined;
const showError = error || !Object.values(constraints)?.length;

Expand All @@ -254,17 +225,17 @@ export function PlanningConstraintsContent(
);

return (
<Card handleSubmit={formik.handleSubmit} isValid>
<QuestionHeader title={title} description={description} />
<Card handleSubmit={props.handleSubmit}>
<CardHeader title={title} description={description} />
{showError && (
<ConstraintsFetchError
error={error}
refreshConstraints={refreshConstraints}
/>
)}
{positiveConstraints.length > 0 && (
{!showError && positiveConstraints.length > 0 && (
<>
<Typography variant="h3" component="h2" gutterBottom>
<Typography variant="h3" component="h2" mt={3}>
These are the planning constraints we think apply to this property
</Typography>
<ConstraintsList data={positiveConstraints} metadata={metadata} />
Expand All @@ -279,44 +250,48 @@ export function PlanningConstraintsContent(
<ConstraintsList data={negativeConstraints} metadata={metadata} />
</SimpleExpand>
)}
<PlanningConditionsInfo />
</>
)}
{positiveConstraints.length === 0 && negativeConstraints.length > 0 && (
<>
<Typography variant="h3" component="h2">
It looks like there are no constraints on this property
</Typography>
<Typography variant="body2">
Based on the information you've given it looks like there are no
planning constraints on your property that might limit what you can
do.
</Typography>
<Typography variant="body2">
Continue with your application to tell us more about your project.
</Typography>
<SimpleExpand
id="negative-constraints-list"
buttonText={{
open: "Show the things we checked",
closed: "Hide constraints that don't apply",
}}
>
<ConstraintsList data={negativeConstraints} metadata={metadata} />
</SimpleExpand>
<PlanningConditionsInfo />
<Disclaimer text={disclaimer} />
</>
)}
{!showError &&
positiveConstraints.length === 0 &&
negativeConstraints.length > 0 && (
<>
<Typography variant="h3" component="h2" gutterBottom mt={3}>
It looks like there are no constraints on this property
</Typography>
<Typography variant="body1" gutterBottom>
Based on the information you've given it looks like there are no
planning constraints on your property that might limit what you
can do.
</Typography>
<Typography variant="body1" gutterBottom>
Continue with your application to tell us more about your project.
</Typography>
<SimpleExpand
id="negative-constraints-list"
buttonText={{
open: "Show the things we checked",
closed: "Hide constraints that don't apply",
}}
>
<ConstraintsList data={negativeConstraints} metadata={metadata} />
</SimpleExpand>
<Disclaimer text={disclaimer} />
</>
)}
</Card>
);
}

const PlanningConditionsInfo = () => (
const Disclaimer = (props: { text: string }) => (
<WarningContainer>
<ErrorOutline />
<Typography variant="body1" ml={2} fontWeight={FONT_WEIGHT_SEMI_BOLD}>
This page does not include information about historic planning conditions
that may apply to this property.
<Typography variant="body1" component="div" ml={2} mb={1}>
<ReactMarkdownOrHtml
source={props.text || DEFAULT_PLANNING_CONDITIONS_DISCLAIMER}
openLinksOnNewTab
/>
</Typography>
</WarningContainer>
);
Expand Down Expand Up @@ -357,7 +332,7 @@ interface ConstraintsGraphErrorProps {

const ConstraintsGraphError = (props: ConstraintsGraphErrorProps) => (
<Card handleSubmit={props.handleSubmit} isValid>
<QuestionHeader title={props.title} description={props.description || ""} />
<CardHeader title={props.title} description={props.description || ""} />
<ErrorSummaryContainer
role="status"
data-testid="error-summary-invalid-graph"
Expand Down
76 changes: 76 additions & 0 deletions editor.planx.uk/src/components/DelayedLoadingSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import Box from "@mui/material/Box";
import { styled } from "@mui/material/styles";
import Typography from "@mui/material/Typography";
import React, { useEffect, useState } from "react";

const Root = styled(Box)(() => ({
padding: "0",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
}));

const SkeletonBox = styled(Box)(({ theme }) => ({
width: "100%",
maxWidth: theme.breakpoints.values.formWrap,
height: "70px",
background: "#F2F2F2",
marginTop: "20px",
overflow: "hidden",
position: "relative",
"&::after": {
position: "absolute",
top: 0,
right: 0,
bottom: 0,
left: 0,
width: "100%",
height: "100%",
transform: "translateX(-100%)",
background: `linear-gradient(
90deg,
rgba(255, 255, 255, 0) 0,
rgba(255, 255, 255, 0.2) 20%,
rgba(255, 255, 255, 0.5) 60%,
rgba(255, 255, 255, 0)
)`,
animation: "shimmer 1.5s infinite",
content: '""',

"@keyframes shimmer": {
"100%": {
transform: "translateX(100%)",
},
},
},
}));

const DelayedLoadingSkeleton: React.FC<{
msDelayBeforeVisible?: number;
text?: string;
}> = ({ msDelayBeforeVisible = 0, text }) => {
const [visible, setVisible] = useState(false);

useEffect(() => {
const timeout = setTimeout(() => setVisible(true), msDelayBeforeVisible);
return () => {
clearTimeout(timeout);
};
}, [msDelayBeforeVisible]);

return visible ? (
<Root
role="alert"
aria-busy="true"
aria-live="assertive"
data-testid="delayed-loading-skeleton"
>
<Typography variant="body1">{text ?? "Loading…"}</Typography>
<SkeletonBox></SkeletonBox>
<SkeletonBox></SkeletonBox>
</Root>
) : null;
};

export default DelayedLoadingSkeleton;
Loading