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

feat: allows users to override inaccurate planning constraints #3216

Closed
wants to merge 5 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
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ const baseSchema: PlanningConstraintsBaseSchema = {
active: true,
neg: "is not, or is not within, a Listed Building",
pos: "is, or is within, a Listed Building",
"digital-land-datasets": ["listed-building", "listed-building-outline"], // HE publishes points, LPAs publish polygons
"digital-land-datasets": ["listed-building", "listed-building-outline"], // HE published points, LPAs publish polygons
category: "Heritage and conservation",
},
locallyListed: {
Expand Down
3 changes: 1 addition & 2 deletions editor.planx.uk/src/@planx/components/List/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,20 @@ import InputRowLabel from "ui/shared/InputRowLabel";
import { EditorProps, ICONS, InternalNotes, MoreInformation } from "../ui";
import { List, parseContent } from "./model";
import { ProposedAdvertisements } from "./schemas/Adverts";
import { MaterialDetails } from "./schemas/Materials";
import { NonResidentialFloorspace } from "./schemas/Floorspace";
import { BuildingDetailsGLA } from "./schemas/GLA/BuildingDetails";
import { CommunalSpaceGLA } from "./schemas/GLA/CommunalSpace";
import { ExistingAndProposedUsesGLA } from "./schemas/GLA/ExistingAndProposedUses";
import { OpenSpaceGLA } from "./schemas/GLA/OpenSpace";
import { ProtectedSpaceGLA } from "./schemas/GLA/ProtectedSpace";
import { MaterialDetails } from "./schemas/Materials";
import { ResidentialUnitsExisting } from "./schemas/ResidentialUnits/Existing";
import { ResidentialUnitsGLANew } from "./schemas/ResidentialUnits/GLA/New";
import { ResidentialUnitsGLARebuilt } from "./schemas/ResidentialUnits/GLA/Rebuilt";
import { ResidentialUnitsGLARemoved } from "./schemas/ResidentialUnits/GLA/Removed";
import { ResidentialUnitsGLARetained } from "./schemas/ResidentialUnits/GLA/Retained";
import { ResidentialUnitsProposed } from "./schemas/ResidentialUnits/Proposed";


type Props = EditorProps<TYPES.List, List>;

export const SCHEMAS = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ export const MaterialDetails: Schema = {
},
{
id: "surface",
data: { text: "External ground materials for access and parking", val: "surface" },
data: {
text: "External ground materials for access and parking",
val: "surface",
},
},
{
id: "lighting",
Expand Down
172 changes: 118 additions & 54 deletions editor.planx.uk/src/@planx/components/PlanningConstraints/List.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import HelpIcon from "@mui/icons-material/Help";
import Accordion from "@mui/material/Accordion";
import AccordionDetails from "@mui/material/AccordionDetails";
import AccordionSummary from "@mui/material/AccordionSummary";
Expand All @@ -14,12 +15,21 @@ import type {
Metadata,
} from "@opensystemslab/planx-core/types";
import groupBy from "lodash/groupBy";
import React, { ReactNode } from "react";
import { useAnalyticsTracking } from "pages/FlowEditor/lib/analytics/provider";
import { HelpClickMetadata } from "pages/FlowEditor/lib/analytics/types";
import { useStore } from "pages/FlowEditor/lib/store";
import React, { ReactNode, useState } from "react";
import ReactHtmlParser from "react-html-parser";
import { FONT_WEIGHT_SEMI_BOLD } from "theme";
import { emptyContent } from "ui/editor/RichTextInput";
import Caret from "ui/icons/Caret";
import ReactMarkdownOrHtml from "ui/shared/ReactMarkdownOrHtml";

import { SiteAddress } from "../FindProperty/model";
import { HelpButton } from "../shared/Preview/CardHeader";
import MoreInfo from "../shared/Preview/MoreInfo";
import MoreInfoSection from "../shared/Preview/MoreInfoSection";

const CATEGORY_COLORS: Record<string, string> = {
"General policy": "#99C1DE",
"Heritage and conservation": "#EDDCD2",
Expand Down Expand Up @@ -75,7 +85,7 @@ export default function ConstraintsList({
});

return (
<Box mb={3}>
<Box key={Object.keys(groupedConstraints).join("-")} mb={3}>
{Object.keys(groupedConstraints).map(
(category: string, index: number) => (
<React.Fragment key={`${category}-wrapper`}>
Expand Down Expand Up @@ -105,11 +115,11 @@ export default function ConstraintsList({
{category}
</Typography>
</ListSubheader>
<List dense disablePadding>
{groupedConstraints[category].map((con: any) => (
<List key={`${category}-${index}`} dense disablePadding>
{groupedConstraints[category].map((con: Constraint) => (
<ConstraintListItem
key={con.text}
content={con.text}
key={con.fn}
value={con.value}
data={con.value ? con.data : null}
metadata={metadata?.[con.fn]}
category={category}
Expand All @@ -126,19 +136,32 @@ export default function ConstraintsList({
}

interface ConstraintListItemProps {
key: string;
content: string;
key: Constraint["fn"];
value: Constraint["value"];
data: Constraint["data"] | null;
metadata?: Metadata;
category: string;
children: ReactNode;
}

function ConstraintListItem({ children, ...props }: ConstraintListItemProps) {
const [open, setOpen] = useState(false);
const { trackEvent } = useAnalyticsTracking();

const { longitude, latitude } =
(useStore(
(state) => state.computePassport().data?._address,
) as SiteAddress) || {};

const handleHelpClick = (metadata: HelpClickMetadata) => {
setOpen(true);
trackEvent({ event: "helpClick", metadata }); // This returns a promise but we don't need to await for it
};

const item = props.metadata?.name.replaceAll(" ", "-");

return (
<ListItem disablePadding sx={{ backgroundColor: "white" }}>
<ListItem key={props.key} disablePadding sx={{ backgroundColor: "white" }}>
<StyledAccordion {...props} disableGutters>
<AccordionSummary
id={`${item}-header`}
Expand All @@ -159,55 +182,96 @@ function ConstraintListItem({ children, ...props }: ConstraintListItemProps) {
}}
>
<>
<Typography variant="h4" gutterBottom>
{`This property ${props?.content}`}
<Typography variant="h4">
{props.value
? `${
props.metadata?.plural || "Entities"
} that intersect with your property:`
: `We did not find any ${
props.metadata?.plural?.toLowerCase() || "entities"
} that apply to your property.`}
</Typography>
{Boolean(props.data?.length) && (
<List
dense
disablePadding
sx={{ listStyleType: "disc", pl: 4, pt: 1 }}
>
{props.data &&
props.data.map(
(record: any) =>
record.name && (
<ListItem
key={record.entity}
dense
disableGutters
sx={{ display: "list-item" }}
>
<Typography variant="body2">
{record.name}{" "}
{record.name && record["documentation-url"] && (
<span>
(
<Link
href={record["documentation-url"]}
target="_blank"
>
source
</Link>
)
</span>
)}
</Typography>
</ListItem>
),
)}
{props.value && (
<List>
{props.data?.map((record: any) => (
<ListItem
key={record.entity}
disableGutters
divider
sx={{ display: "flex", justifyContent: "space-between" }}
>
<Typography variant="body2">
{record.name ||
(record["flood-risk-level"] &&
`${props.metadata?.name} - Level ${record["flood-risk-level"]}`) ||
`Entity #${record.entity}`}
</Typography>
<Typography variant="body2">
<Link>Report an inaccuracy</Link>
</Typography>
</ListItem>
))}
</List>
)}
{props.metadata?.plural !== "Classified roads" && (
<Typography variant="body2" my={2}>
{`See ${props.metadata?.plural?.toLowerCase()} ${
props.value ? `at this property` : ``
} on the `}
<Link
href={`https://www.planning.data.gov.uk/map/?dataset=${props.metadata?.dataset}#${latitude},${longitude},17.5z`}
target="_blank"
>
Planning Data map
</Link>
{` (opens in a new tab).`}
</Typography>
)}
{props.metadata?.text && props.metadata.text !== emptyContent && (
<Typography variant="body2" component="div">
<HelpButton
variant="help"
title={`More information`}
aria-label={`See more information about "${props.metadata?.name}"`}
onClick={() =>
handleHelpClick({
[props.key]: props.metadata?.name || "Constraint",
})
}
aria-haspopup="dialog"
data-testid="more-info-button"
>
<HelpIcon /> More information
</HelpButton>
</Typography>
)}
<MoreInfo open={open} handleClose={() => setOpen(false)}>
<MoreInfoSection title="Source">
<Typography variant="body2">
{props.metadata?.plural === "Classified roads" ? (
`Ordnance Survey MasterMap Highways`
) : (
<Link
href={`https://www.planning.data.gov.uk/`}
target="_blank"
>
Planning Data
</Link>
)}
</Typography>
</MoreInfoSection>
<MoreInfoSection title="How is it defined?">
<ReactMarkdownOrHtml
source={props.metadata?.text?.replaceAll(
"(/",
"(https://www.planning.data.gov.uk/",
)}
openLinksOnNewTab
id="howMeasured"
/>
</MoreInfoSection>
</MoreInfo>
</>
<Typography component="div" variant="body2">
<ReactMarkdownOrHtml
source={props.metadata?.text?.replaceAll(
"(/",
"(https://www.planning.data.gov.uk/",
)}
openLinksOnNewTab
/>
</Typography>
</AccordionDetails>
</StyledAccordion>
</ListItem>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Close from "@mui/icons-material/Close";
import Done from "@mui/icons-material/Done";
import NotInterested from "@mui/icons-material/NotInterested";
import Warning from "@mui/icons-material/Warning";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Collapse from "@mui/material/Collapse";
Expand All @@ -18,7 +19,6 @@ import { useAsync } from "react-use";
import Caret from "ui/icons/Caret";

import { useStore } from "../../lib/store";
import Warning from "@mui/icons-material/Warning";

export interface AlteredNode {
id: string;
Expand Down
2 changes: 1 addition & 1 deletion editor.planx.uk/src/pages/FlowEditor/lib/store/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ComponentType as TYPES } from "@opensystemslab/planx-core/types";
import { isPreviewOnlyDomain } from "routes/utils";
import { create, StoreApi,UseBoundStore } from "zustand";
import { create, StoreApi, UseBoundStore } from "zustand";

import type { EditorStore, EditorUIStore } from "./editor";
import type { NavigationStore } from "./navigation";
Expand Down
6 changes: 4 additions & 2 deletions editor.planx.uk/src/routes/views/flowEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ const getFlowMetadata = async (
*/
export const flowEditorView = async (req: NaviRequest) => {
const [flow] = req.params.flow.split(",");
const { flowAnalyticsLink, isFlowPublished } =
await getFlowMetadata(flow, req.params.team);
const { flowAnalyticsLink, isFlowPublished } = await getFlowMetadata(
flow,
req.params.team,
);
useStore.setState({ flowAnalyticsLink, isFlowPublished });

return (
Expand Down
Loading