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

Production deploy #3438

Merged
merged 8 commits into from
Jul 18, 2024
1 change: 1 addition & 0 deletions api.planx.uk/modules/gis/service/digitalLand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
doncaster: require("./local_authorities/metadata/doncaster"),
"epsom-and-ewell": require("./local_authorities/metadata/epsomAndEwell"),
gateshead: require("./local_authorities/metadata/gateshead"),
gloucester: require("./local_authorities/metadata/gloucester"),
lambeth: require("./local_authorities/metadata/lambeth"),
medway: require("./local_authorities/metadata/medway"),
newcastle: require("./local_authorities/metadata/newcastle"),
Expand Down Expand Up @@ -77,8 +78,8 @@
options,
)}${datasets}`;
const res = await fetch(url)
.then((response: { json: () => any }) => response.json())

Check warning on line 81 in api.planx.uk/modules/gis/service/digitalLand.ts

View workflow job for this annotation

GitHub Actions / Run API Tests

Unexpected any. Specify a different type
.catch((error: any) => console.log(error));

Check warning on line 82 in api.planx.uk/modules/gis/service/digitalLand.ts

View workflow job for this annotation

GitHub Actions / Run API Tests

Unexpected any. Specify a different type

// if analytics are "on", store an audit record of the raw response
if (extras?.analytics !== "false") {
Expand Down Expand Up @@ -112,7 +113,7 @@
// check for & add any 'positive' constraints to the formattedResult
let formattedResult: Record<string, Constraint> = {};
if (res && res.count > 0 && res.entities) {
res.entities.forEach((entity: { dataset: any }) => {

Check warning on line 116 in api.planx.uk/modules/gis/service/digitalLand.ts

View workflow job for this annotation

GitHub Actions / Run API Tests

Unexpected any. Specify a different type
// get the planx variable that corresponds to this entity's 'dataset', should never be null because our initial request is filtered on 'dataset'
const key = Object.keys(baseSchema).find((key) =>
baseSchema[key]["digital-land-datasets"]?.includes(entity.dataset),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
LAD20CD: E07000081
LAD20NM: Gloucester
LAD20NMW:
FID:

https://www.planning.data.gov.uk/entity/?dataset=article-4-direction&dataset=article-4-direction-area&geometry_curie=statistical-geography%3AE07000081&entry_date_day=&entry_date_month=&entry_date_year=
https://docs.google.com/spreadsheets/d/1ALSH4hiupdUlrA7Rq7jhfHnWr0-PcB-IzhsztPLl7FY/edit?gid=0#gid=0
*/

import { LocalAuthorityMetadata } from "../../digitalLand";

const planningConstraints: LocalAuthorityMetadata["planningConstraints"] = {
article4: {
// Planx granular values link to Digital Land entity.reference
records: {
"article4.gloucester.southgateStreetConservationArea":
"Southgate Street Conservation Area",
"article4.gloucester.stMichaelsSquare":
"St Michael’s Square Article 4 direction",
},
},
};

export { planningConstraints };
125 changes: 84 additions & 41 deletions editor.planx.uk/src/@planx/components/PlanningConstraints/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ import type {
Metadata,
} from "@opensystemslab/planx-core/types";
import groupBy from "lodash/groupBy";
import { useStore } from "pages/FlowEditor/lib/store";
import React, { ReactNode } from "react";
import ReactHtmlParser from "react-html-parser";
import { FONT_WEIGHT_SEMI_BOLD } from "theme";
import Caret from "ui/icons/Caret";
import ReactMarkdownOrHtml from "ui/shared/ReactMarkdownOrHtml";

import { SiteAddress } from "../FindProperty/model";
import { availableDatasets } from "./model";

const CATEGORY_COLORS: Record<string, string> = {
"General policy": "#99C1DE",
"Heritage and conservation": "#EDDCD2",
Expand Down Expand Up @@ -70,7 +74,7 @@ export default function ConstraintsList({
data,
metadata,
}: ConstraintsListProps) {
const groupedConstraints = groupBy(data, (constraint: Constraint) => {
const groupedConstraints = groupBy(data, (constraint) => {
return constraint.category;
});

Expand All @@ -84,7 +88,7 @@ export default function ConstraintsList({
disableGutters
disableSticky
color="primary"
key={category}
key={`${category}-ls`}
style={{
padding: 0,
backgroundColor: CATEGORY_COLORS[category],
Expand All @@ -105,10 +109,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) => (
<ConstraintListItem
key={con.text}
key={con.fn}
value={con.value}
content={con.text}
data={con.value ? con.data : null}
metadata={metadata?.[con.fn]}
Expand All @@ -126,19 +131,41 @@ export default function ConstraintsList({
}

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

function ConstraintListItem({ children, ...props }: ConstraintListItemProps) {
const { longitude, latitude, usrn } =
(useStore(
(state) => state.computePassport().data?._address,
) as SiteAddress) || {};
const item = props.metadata?.name.replaceAll(" ", "-");
const isSourcedFromPlanningData =
props.metadata?.plural !== "Classified roads";

// Some constraint categories search for entities amongst many PD datasets, but our `props.metadata.dataset` will only store reference to the last one
// Cross reference with `availableDatasets` in Editor to ensure map URL is filtered to include each possible dataset
const matchingDatasets = availableDatasets.find(
(d) =>
props.metadata?.dataset && d.datasets.includes(props.metadata.dataset),
)?.datasets || [props?.metadata?.dataset];
const encodedMatchingDatasets = matchingDatasets
?.map((d) => `dataset=${d}`)
.join("&");
const planningDataMapURL = `https://www.planning.data.gov.uk/map/?${encodedMatchingDatasets}#${latitude},${longitude},17.5z`;

return (
<ListItem disablePadding sx={{ backgroundColor: "white" }}>
<ListItem
key={`${props.key}-li`}
disablePadding
sx={{ backgroundColor: "white" }}
>
<StyledAccordion {...props} disableGutters>
<AccordionSummary
id={`${item}-header`}
Expand All @@ -158,7 +185,7 @@ function ConstraintListItem({ children, ...props }: ConstraintListItemProps) {
background: (theme) => theme.palette.background.default,
}}
>
<>
<React.Fragment>
<Typography variant="h4" gutterBottom>
{`This property ${props?.content}`}
</Typography>
Expand All @@ -169,42 +196,58 @@ function ConstraintListItem({ children, ...props }: ConstraintListItemProps) {
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.data.map((record: any) => (
<ListItem
key={`entity-${record.entity}-li`}
dense
disableGutters
sx={{ display: "list-item" }}
>
{isSourcedFromPlanningData ? (
<Typography variant="body2">
<Link
href={`https://www.planning.data.gov.uk/entity/${record.entity}`}
target="_blank"
>
{record.name ||
(record["flood-risk-level"] &&
`${props.metadata?.name} - Level ${record["flood-risk-level"]}`) ||
`Planning Data entity #${record.entity}`}
</Link>
</Typography>
) : (
<Typography variant="body2">{record.name}</Typography>
)}
</ListItem>
))}
</List>
)}
</>
</React.Fragment>
{isSourcedFromPlanningData ? (
<Typography component="div" variant="body2" my={2}>
{`View on the `}
<Link href={planningDataMapURL} target="_blank">
Planning Data map
</Link>
{` (opens in a new tab).`}
</Typography>
) : (
<Typography component="div" variant="body2" my={2}>
{`We searched Ordnance Survey MasterMap Highways using the Unique Street Reference Number of your property`}
{usrn && ` (${usrn})`}
</Typography>
)}
<Typography variant="h5">{`How is it defined`}</Typography>
<Typography component="div" variant="body2">
<ReactMarkdownOrHtml
source={props.metadata?.text?.replaceAll(
"(/",
"(https://www.planning.data.gov.uk/",
)}
source={
isSourcedFromPlanningData
? props.metadata?.text?.replaceAll(
"(/",
"(https://www.planning.data.gov.uk/",
)
: props.metadata?.text
}
openLinksOnNewTab
/>
</Typography>
Expand Down
24 changes: 12 additions & 12 deletions editor.planx.uk/src/components/EditorNavMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,18 +116,18 @@ function EditorNavMenu() {
route: "/",
accessibleBy: ["platformAdmin", "teamEditor", "teamViewer"],
},
{
title: "Admin panel",
Icon: AdminPanelSettingsIcon,
route: "admin-panel",
accessibleBy: ["platformAdmin"],
},
{
title: "Global settings",
Icon: TuneIcon,
route: "global-settings",
accessibleBy: ["platformAdmin"],
},
{
title: "Admin panel",
Icon: AdminPanelSettingsIcon,
route: "admin-panel",
accessibleBy: ["platformAdmin"],
},
];

const teamLayoutRoutes: Route[] = [
Expand All @@ -138,9 +138,9 @@ function EditorNavMenu() {
accessibleBy: ["platformAdmin", "teamEditor", "teamViewer"],
},
{
title: "Team members",
Icon: GroupIcon,
route: `/${teamSlug}/members`,
title: "Settings",
Icon: TuneIcon,
route: `/${teamSlug}/general-settings`,
accessibleBy: ["platformAdmin", "teamEditor"],
},
{
Expand All @@ -150,9 +150,9 @@ function EditorNavMenu() {
accessibleBy: ["platformAdmin", "teamEditor"],
},
{
title: "Settings",
Icon: TuneIcon,
route: `/${teamSlug}/general-settings`,
title: "Team members",
Icon: GroupIcon,
route: `/${teamSlug}/members`,
accessibleBy: ["platformAdmin", "teamEditor"],
},
];
Expand Down
2 changes: 1 addition & 1 deletion editor.planx.uk/src/lib/featureFlags.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// add/edit/remove feature flags in array below
const AVAILABLE_FEATURE_FLAGS = [] as const;
const AVAILABLE_FEATURE_FLAGS = ["SEARCH"] as const;

type FeatureFlag = (typeof AVAILABLE_FEATURE_FLAGS)[number];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export default function ContactForm({ formikConfig, onSuccess }: FormProps) {
onChange={(event) => {
onChangeFn("homepage", event);
}}
value={formik.values.homepage}
value={formik.values.homepage ?? ""}
errorMessage={formik.errors.homepage}
id="homepage"
/>
Expand Down Expand Up @@ -91,7 +91,6 @@ export default function ContactForm({ formikConfig, onSuccess }: FormProps) {
</InputLabel>
<InputLabel label="Opening hours" htmlFor="helpOpeningHours">
<Input
multiline
name="helpOpeningHours"
value={formik.values.helpOpeningHours}
errorMessage={formik.errors.helpOpeningHours}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import Link from "@mui/material/Link";
import Typography from "@mui/material/Typography";
import { useFormik } from "formik";
import { useStore } from "pages/FlowEditor/lib/store";
import React, { ChangeEvent } from "react";
import InputLabel from "ui/editor/InputLabel";
import Input from "ui/shared/Input";
import * as Yup from "yup";

import { SettingsForm } from "../shared/SettingsForm";
import { FormProps } from ".";

export default function ReferenceCodeForm({
formikConfig,
onSuccess,
}: FormProps) {
const formSchema = Yup.object().shape({
referenceCode: Yup.string()
.min(3, "Code must be 3 characters long")
.max(3, "Code must be 3 characters long")
.required("Enter a reference code"),
});

const formik = useFormik({
...formikConfig,
validationSchema: formSchema,
onSubmit: async (values, { resetForm }) => {
const isSuccess = await useStore.getState().updateTeamSettings({
referenceCode: values.referenceCode,
});
if (isSuccess) {
onSuccess();
resetForm({ values });
}
},
});

const onChangeFn = (type: string, event: ChangeEvent<HTMLInputElement>) =>
formik.setFieldValue(type, event.target.value.toUpperCase());

return (
<SettingsForm
legend="Local authority reference code"
formik={formik}
description={
<>
<Typography variant="body2">
Your local authority reference code is required for submissions.
This is a unique three-letter code per local authority.
</Typography>
<Typography variant="body2">
The reference code can be found from Planning Data at:{" "}
<Link
href="https://www.planning.data.gov.uk/entity/?dataset=local-authority"
target="_blank"
rel="noopener noreferrer"
>
https://www.planning.data.gov.uk/entity/?dataset=local-authority
</Link>
</Typography>
</>
}
input={
<>
<InputLabel label="Reference code" htmlFor="referenceCode">
<Input
name="referenceCode"
onChange={(event) => {
onChangeFn("referenceCode", event);
}}
value={formik.values.referenceCode ?? ""}
errorMessage={formik.errors.referenceCode}
id="homepage"
/>
</InputLabel>
</>
}
/>
);
}
Loading
Loading