Merge branch 'master' into feature/new-categories-menu
This commit is contained in:
commit
ef09fa3b78
@ -3,7 +3,6 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
/* padding: 0.1em; */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@ -18,5 +17,6 @@
|
||||
text-align: center;
|
||||
font-size: 1em;
|
||||
margin: 0;
|
||||
padding: 0.25em;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
.section-header .h2 {
|
||||
display: inline-block;
|
||||
flex-basis: 150px;
|
||||
flex-basis: 200px;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 1;
|
||||
margin: 0.75rem 0 0.5em 0.1em;
|
||||
@ -33,7 +33,8 @@
|
||||
|
||||
.section-header .section-header-actions {
|
||||
display: inline-block;
|
||||
flex-basis: 400px;
|
||||
flex-basis: 220px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
align-items: center;
|
||||
|
@ -99,7 +99,7 @@ const PlanningDataOfficialDataEntry: React.FC<PlanningDataOfficialDataEntryProps
|
||||
<Fragment>
|
||||
<InfoBox type='success'>
|
||||
<Fragment>
|
||||
<div><b>Current planning application status for this site:</b> <StatusInfo
|
||||
<div><b>Planning application status for this site:</b> <StatusInfo
|
||||
statusBeforeAliasing={item["status_before_aliasing"]}
|
||||
status={item["status"]}
|
||||
/></div>
|
||||
|
@ -13,7 +13,7 @@ import { CategoryViewProps } from './category-view-props';
|
||||
*/
|
||||
const StreetscapeView: React.FunctionComponent<CategoryViewProps> = (props) => (
|
||||
<Fragment>
|
||||
<InfoBox msg="This is what we're planning to collect on the building's context" />
|
||||
<InfoBox type='warning' msg="This is what we're planning to collect on the building's context" />
|
||||
<ul className="data-list">
|
||||
<li>Gardens</li>
|
||||
<li>Trees</li>
|
||||
|
@ -120,7 +120,7 @@ export const buildingUserFields = {
|
||||
export const dataFields = { /* eslint-disable @typescript-eslint/camelcase */
|
||||
location_name: {
|
||||
category: Category.Location,
|
||||
title: "Building information (link)",
|
||||
title: "Building Name (Information link)",
|
||||
tooltip: "Link to a website with information on the building, not needed for most.",
|
||||
example: "https://en.wikipedia.org/wiki/Palace_of_Westminster",
|
||||
},
|
||||
|
@ -3,6 +3,9 @@ import React, { createContext, useCallback, useContext, useEffect, useState } fr
|
||||
import { LayerEnablementState, MapTheme } from './config/map-config';
|
||||
|
||||
interface DisplayPreferencesContextState {
|
||||
resetLayers: (e: React.FormEvent<HTMLFormElement>) => void;
|
||||
anyLayerModifiedState: () => boolean;
|
||||
|
||||
vista: LayerEnablementState;
|
||||
vistaSwitch: (e: React.FormEvent<HTMLFormElement>) => void;
|
||||
vistaSwitchOnClick: React.MouseEventHandler<HTMLButtonElement>;
|
||||
@ -45,6 +48,9 @@ const stub = (): never => {
|
||||
};
|
||||
|
||||
export const DisplayPreferencesContext = createContext<DisplayPreferencesContextState>({
|
||||
resetLayers: stub,
|
||||
anyLayerModifiedState: stub,
|
||||
|
||||
vista: undefined,
|
||||
vistaSwitch: stub,
|
||||
vistaSwitchOnClick: undefined,
|
||||
@ -85,16 +91,69 @@ export const DisplayPreferencesContext = createContext<DisplayPreferencesContext
|
||||
const noop = () => {};
|
||||
|
||||
export const DisplayPreferencesProvider: React.FC<{}> = ({children}) => {
|
||||
const [vista, setVista] = useState<LayerEnablementState>('disabled');
|
||||
const [flood, setFlood] = useState<LayerEnablementState>('disabled');
|
||||
const [creative, setCreative] = useState<LayerEnablementState>('disabled');
|
||||
const [housing, setHousing] = useState<LayerEnablementState>('disabled');
|
||||
const [borough, setBorough] = useState<LayerEnablementState>('enabled');
|
||||
const [parcel, setParcel] = useState<LayerEnablementState>('disabled');
|
||||
const [conservation, setConservation] = useState<LayerEnablementState>('disabled');
|
||||
const [historicData, setHistoricData] = useState<LayerEnablementState>('disabled');
|
||||
const defaultVista = 'disabled'
|
||||
const defaultFlood = 'disabled'
|
||||
const defaultCreative = 'disabled'
|
||||
const defaultHousing = 'disabled'
|
||||
const defaultBorough = 'enabled'
|
||||
const defaultParcel = 'disabled'
|
||||
const defaultConservation = 'disabled'
|
||||
const defaultHistoricData = 'disabled'
|
||||
const [vista, setVista] = useState<LayerEnablementState>(defaultVista);
|
||||
const [flood, setFlood] = useState<LayerEnablementState>(defaultFlood);
|
||||
const [creative, setCreative] = useState<LayerEnablementState>(defaultCreative);
|
||||
const [housing, setHousing] = useState<LayerEnablementState>(defaultHousing);
|
||||
const [borough, setBorough] = useState<LayerEnablementState>(defaultBorough);
|
||||
const [parcel, setParcel] = useState<LayerEnablementState>(defaultParcel);
|
||||
const [conservation, setConservation] = useState<LayerEnablementState>(defaultConservation);
|
||||
const [historicData, setHistoricData] = useState<LayerEnablementState>(defaultHistoricData);
|
||||
const [darkLightTheme, setDarkLightTheme] = useState<MapTheme>('night');
|
||||
|
||||
const resetLayers = useCallback(
|
||||
(e) => {
|
||||
e.preventDefault();
|
||||
setVista(defaultVista);
|
||||
setFlood(defaultFlood);
|
||||
setCreative(defaultCreative);
|
||||
setHousing(defaultHousing);
|
||||
setBorough(defaultBorough)
|
||||
setParcel(defaultParcel);
|
||||
setConservation(defaultConservation);
|
||||
setHistoricData(defaultHistoricData);
|
||||
//setDarkLightTheme('night'); // reset only layers
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
function anyLayerModifiedState() {
|
||||
if(vista != defaultVista) {
|
||||
return true;
|
||||
}
|
||||
if(flood != defaultFlood) {
|
||||
return true;
|
||||
}
|
||||
if(creative != defaultCreative) {
|
||||
return true;
|
||||
}
|
||||
if(housing != defaultHousing) {
|
||||
return true;
|
||||
}
|
||||
if(borough != defaultBorough) {
|
||||
return true;
|
||||
}
|
||||
if(parcel != defaultParcel) {
|
||||
return true;
|
||||
}
|
||||
if(conservation != defaultConservation) {
|
||||
return true;
|
||||
}
|
||||
if(historicData != defaultHistoricData) {
|
||||
return true;
|
||||
}
|
||||
//darkLightTheme not handled here
|
||||
return false;
|
||||
}
|
||||
|
||||
const vistaSwitch = useCallback(
|
||||
(e) => {
|
||||
e.preventDefault();
|
||||
@ -233,6 +292,9 @@ export const DisplayPreferencesProvider: React.FC<{}> = ({children}) => {
|
||||
|
||||
return (
|
||||
<DisplayPreferencesContext.Provider value={{
|
||||
resetLayers,
|
||||
anyLayerModifiedState,
|
||||
|
||||
vista,
|
||||
vistaSwitch,
|
||||
vistaSwitchOnClick,
|
||||
|
@ -15,23 +15,14 @@ export function BoroughBoundaryLayer({}) {
|
||||
}, []);
|
||||
|
||||
if(borough == "enabled") {
|
||||
return boundaryGeojson &&
|
||||
<><GeoJSON
|
||||
attribution='Borough boundary from <a href=https://data.london.gov.uk/dataset/london_boroughs>London Datastore</a> Ordnance Survey Open Data - Contains public sector information licensed under the <a href=https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/>Open Government Licence v3.0</a>'
|
||||
data={boundaryGeojson}
|
||||
style={{color: '#f00', fill: false, weight: 1}}
|
||||
/* minNativeZoom={17}*/
|
||||
/><BuildingBaseLayerAllZoom theme="boroughs" /></>;
|
||||
} else if (borough == "disabled") {
|
||||
return <div></div>
|
||||
// do not display anything
|
||||
return boundaryGeojson &&
|
||||
<GeoJSON
|
||||
data={boundaryGeojson}
|
||||
style={{color: '#0f0', fill: false, weight: 1}} />
|
||||
attribution='Borough boundary from <a href=https://data.london.gov.uk/dataset/london_boroughs>London Datastore</a> Ordnance Survey Open Data - Contains public sector information licensed under the <a href=https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/>Open Government Licence v3.0</a>'
|
||||
data={boundaryGeojson}
|
||||
style={{color: '#f00', fill: false, weight: 1}}
|
||||
/>;
|
||||
} else {
|
||||
return boundaryGeojson &&
|
||||
<GeoJSON data={boundaryGeojson} style={{color: '#0f0', fill: true}}/>;
|
||||
return <></>
|
||||
}
|
||||
}
|
||||
|
||||
|
17
app/src/frontend/map/layers/borough-label-layer.tsx
Normal file
17
app/src/frontend/map/layers/borough-label-layer.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { GeoJsonObject } from 'geojson';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { GeoJSON } from 'react-leaflet';
|
||||
import { useDisplayPreferences } from '../../displayPreferences-context';
|
||||
import { apiGet } from '../../apiHelpers';
|
||||
import { BuildingBaseLayerAllZoom } from './building-base-layer-all-zoom';
|
||||
|
||||
export function BoroughLabelLayer({}) {
|
||||
const { borough } = useDisplayPreferences();
|
||||
|
||||
if(borough == "enabled") {
|
||||
return <BuildingBaseLayerAllZoom theme="boroughs" />;
|
||||
} else {
|
||||
return <></>
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
.reset-switcher {
|
||||
top: 37px;
|
||||
margin-right: 60px;
|
||||
}
|
||||
.reset-switcher .btn {
|
||||
min-width: 280px;
|
||||
}
|
||||
|
||||
.theme-switcher {
|
||||
top: 77px;
|
||||
}
|
||||
|
@ -28,19 +28,6 @@
|
||||
.leaflet-grab {
|
||||
cursor: crosshair;
|
||||
}
|
||||
.map-notice {
|
||||
position: absolute;
|
||||
top: 3.5rem;
|
||||
left: 0.5rem;
|
||||
z-index: 1000;
|
||||
padding: 0.5rem 0.75rem;
|
||||
width: 250px;
|
||||
background: #fff;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0px 0px 1px 1px #222;
|
||||
display: none;
|
||||
}
|
||||
@media (min-width: 990px){
|
||||
/* Only show the "Click a building ..." notice for larger screens */
|
||||
.map-notice {
|
||||
|
@ -14,6 +14,7 @@ import { Building } from '../models/building';
|
||||
import { CityBaseMapLayer } from './layers/city-base-map-layer';
|
||||
import { CityBoundaryLayer } from './layers/city-boundary-layer';
|
||||
import { BoroughBoundaryLayer } from './layers/borough-boundary-layer';
|
||||
import { BoroughLabelLayer } from './layers/borough-label-layer';
|
||||
import { ParcelBoundaryLayer } from './layers/parcel-boundary-layer';
|
||||
import { HistoricDataLayer } from './layers/historic-data-layer';
|
||||
import { FloodBoundaryLayer } from './layers/flood-boundary-layer';
|
||||
@ -30,6 +31,7 @@ import { Legend } from './legend';
|
||||
import SearchBox from './search-box';
|
||||
import ThemeSwitcher from './theme-switcher';
|
||||
import DataLayerSwitcher from './data-switcher';
|
||||
import { ResetSwitcher } from './reset-switcher';
|
||||
import { BoroughSwitcher } from './borough-switcher';
|
||||
import { ParcelSwitcher } from './parcel-switcher';
|
||||
import { FloodSwitcher } from './flood-switcher';
|
||||
@ -158,6 +160,12 @@ export const ColouringMap : FC<ColouringMapProps> = ({
|
||||
/>
|
||||
}
|
||||
</Pane>
|
||||
<Pane
|
||||
name='cc-label-overlay-pane'
|
||||
style={{zIndex: 1000}}
|
||||
>
|
||||
<BoroughLabelLayer/>
|
||||
</Pane>
|
||||
|
||||
<ZoomControl position="topright" />
|
||||
<AttributionControl prefix=""/>
|
||||
@ -165,13 +173,8 @@ export const ColouringMap : FC<ColouringMapProps> = ({
|
||||
{
|
||||
mode !== 'basic' &&
|
||||
<>
|
||||
{
|
||||
!hasSelection &&
|
||||
<div className="map-notice">
|
||||
<HelpIcon /> {isEdit ? 'Click a building to edit' : 'Click a building for details'}
|
||||
</div>
|
||||
}
|
||||
<Legend mapColourScaleDefinitions={categoryMapDefinitions} mapColourScale={mapColourScale} onMapColourScale={onMapColourScale}/>
|
||||
<ResetSwitcher/>
|
||||
<ThemeSwitcher onSubmit={darkLightThemeSwitch} currentTheme={darkLightTheme} />
|
||||
<DataLayerSwitcher onSubmit={layerSwitch} currentDisplay={dataLayers} />
|
||||
{
|
||||
|
20
app/src/frontend/map/reset-switcher.tsx
Normal file
20
app/src/frontend/map/reset-switcher.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
|
||||
import './map-button.css';
|
||||
import { useDisplayPreferences } from '../displayPreferences-context';
|
||||
|
||||
export const ResetSwitcher: React.FC<{}> = () => {
|
||||
const { resetLayers, darkLightTheme, anyLayerModifiedState } = useDisplayPreferences();
|
||||
if(anyLayerModifiedState()) {
|
||||
return (
|
||||
<form className={`reset-switcher map-button ${darkLightTheme}`} onSubmit={resetLayers}>
|
||||
<button className="btn btn-outline btn-outline-dark"
|
||||
type="submit">
|
||||
{"Reset layers"}
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
} else {
|
||||
return <></>
|
||||
}
|
||||
}
|
@ -103,10 +103,7 @@ class SearchBox extends Component<SearchBoxProps, SearchBoxState> {
|
||||
apiGet(`/api/search?q=${this.state.q}`)
|
||||
.then((data) => {
|
||||
if (data && data.results){
|
||||
this.setState({
|
||||
results: data.results,
|
||||
fetching: false
|
||||
});
|
||||
this.props.onLocate(data.results[0].geometry.coordinates[1], data.results[0].geometry.coordinates[0], data.results[0].attributes.zoom)
|
||||
} else {
|
||||
console.error(data);
|
||||
|
||||
@ -157,31 +154,7 @@ class SearchBox extends Component<SearchBoxProps, SearchBoxState> {
|
||||
);
|
||||
}
|
||||
|
||||
const resultsList = this.state.results.length?
|
||||
<ul className="search-box-results">
|
||||
{
|
||||
this.state.results.map((result) => {
|
||||
const label = result.attributes.label;
|
||||
const lng = result.geometry.coordinates[0];
|
||||
const lat = result.geometry.coordinates[1];
|
||||
const zoom = result.attributes.zoom;
|
||||
const href = `?lng=${lng}&lat=${lat}&zoom=${zoom}`;
|
||||
return (
|
||||
<li key={result.attributes.label}>
|
||||
<a
|
||||
className="search-box-result"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
this.props.onLocate(lat, lng, zoom);
|
||||
}}
|
||||
href={href}
|
||||
>{`${label.substring(0, 4)} ${label.substring(4, 7)}`}</a>
|
||||
</li>
|
||||
);
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
: null;
|
||||
const resultsList = null;
|
||||
return (
|
||||
<div className="search-box" onKeyDown={this.handleKeyPress}>
|
||||
<div className="search-box-pane">
|
||||
|
@ -39,7 +39,7 @@ export const AuthRoute: React.FC<RouteProps> = ({ component: Component, children
|
||||
if(isAuthenticated) {
|
||||
let state = props.location.state as any;
|
||||
let from = '/my-account.html';
|
||||
if (typeof state == 'object' && 'from' in state){
|
||||
if (typeof state == 'object' && state !== null && 'from' in state) {
|
||||
from = state.from;
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ const tileCache = new TileCache(
|
||||
shouldCacheFn,
|
||||
|
||||
// don't clear on bounding box cache clear tilesets not affected by user-editable data
|
||||
(tileset: string) => tileset !== 'base_light' && tileset !== 'base_night' && tileset !== 'base_night_outlines' && tileset !== 'base_burough' && tileset !== "planning_applications_status_recent" && tileset !== "planning_applications_status_very_recent" && tileset !== "planning_applications_status_all"
|
||||
(tileset: string) => tileset !== 'base_light' && tileset !== 'base_night' && tileset !== 'base_night_outlines' && tileset !== 'base_borough' && tileset !== "planning_applications_status_recent" && tileset !== "planning_applications_status_very_recent" && tileset !== "planning_applications_status_all"
|
||||
);
|
||||
|
||||
const renderBuildingTile = (t: TileParams, d: any) => renderDataSourceTile(t, d, getDataConfig, getLayerVariables);
|
||||
|
@ -1,15 +1,15 @@
|
||||
-- Remove sustainability fields, update in paralell with adding new fields
|
||||
-- Remove sustainability fields, update in parallel with adding new fields
|
||||
-- BREEAM rating
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS sust_breeam_rating;
|
||||
-- BREEAM date
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS sust_breeam_date;
|
||||
|
||||
-- DEC (display energy certifcate, only applies to non domestic buildings)
|
||||
-- DEC (display energy certificate, only applies to non domestic buildings)
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS sust_dec;
|
||||
-- DEC date
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS sust_dec_date;
|
||||
|
||||
--DEC certifcate lmk key, this would be lmkkey, no online lookup but can scrape through API. Numeric (25)
|
||||
--DEC certificate lmk key, this would be lmkkey, no online lookup but can scrape through API. Numeric (25)
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS sust_dec_lmkey;
|
||||
|
||||
-- Aggregate EPC rating (Estimated) for a building, derived from inidividual certificates
|
||||
|
@ -1,4 +1,4 @@
|
||||
-- Remove sustainability fields, update in paralell with adding new fields
|
||||
-- Remove sustainability fields, update in parallel with adding new fields
|
||||
-- Last significant retrofit date YYYY
|
||||
-- Need to add a constraint to sust_retrofit_date
|
||||
-- Renewal technologies
|
||||
@ -6,7 +6,7 @@
|
||||
-- Values:
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS sust_renewables_tech;
|
||||
|
||||
--Has a building had a major renovation without extenstion (captured in form)
|
||||
--Has a building had a major renovation without extension (captured in form)
|
||||
--Boolean yes/no - links to the the DATE
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS sust_retrofitted;
|
||||
|
||||
|
@ -20,7 +20,7 @@ ALTER TABLE buildings
|
||||
ALTER TABLE buildings
|
||||
ADD COLUMN IF NOT EXISTS sust_breeam_date smallint;
|
||||
|
||||
-- DEC (display energy certifcate, only applies to non domestic buildings)
|
||||
-- DEC (display energy certificate, only applies to non domestic buildings)
|
||||
-- A - G
|
||||
CREATE TYPE sust_dec
|
||||
AS ENUM ('A',
|
||||
|
@ -1,10 +1,10 @@
|
||||
-- Remove sustainability fields, update in paralell with adding new fields
|
||||
-- Remove sustainability fields, update in parallel with adding new fields
|
||||
-- Last significant retrofit date YYYY
|
||||
-- Need to add a constraint to sust_retrofit_date
|
||||
ALTER TABLE buildings
|
||||
ADD CONSTRAINT sust_retrofit_date_end CHECK (sust_retrofit_date <= DATE_PART('year', CURRENT_DATE));
|
||||
|
||||
--Has a building had a major renovation without extenstion (captured in form)
|
||||
--Has a building had a major renovation without extension (captured in form)
|
||||
--Boolean yes/no - links to the the DATE
|
||||
ALTER TABLE buildings
|
||||
ADD COLUMN IF NOT EXISTS sust_retrofitted boolean DEFAULT 'n';
|
||||
|
@ -1,4 +1,4 @@
|
||||
--Landuse is hierachical. Highest level is Order (ie. Residential) then Group (ie Residential-Dwelling) then Class (ie Residential-Dwelling-Detached house)
|
||||
--Landuse is hierarchical. Highest level is Order (ie. Residential) then Group (ie Residential-Dwelling) then Class (ie Residential-Dwelling-Detached house)
|
||||
--Interface will collected most detailed (class) but visualise highest level (order)
|
||||
--Landuse is a table as #358
|
||||
--Land use class, group and order will be stored in a new table
|
||||
|
@ -1,5 +1,5 @@
|
||||
-- Create land use and fields
|
||||
--Landuse is hierachical. Highest level is Order (Residential) then Group (Residential-Dwelling) then Class (Residential-Dwelling-Detached house)
|
||||
--Landuse is hierarchical. Highest level is Order (Residential) then Group (Residential-Dwelling) then Class (Residential-Dwelling-Detached house)
|
||||
--Some ETL work required to get this together refer to analysis repo
|
||||
--Prerequesite is to have first run bulk_data_sources migrations
|
||||
--Then create table landuse_order for the app, this is used as foreign key for current and original landuse_order
|
||||
@ -36,11 +36,11 @@ FROM reference_tables.landuse_classifications a
|
||||
WHERE a.level = 'class'
|
||||
AND a.is_used;
|
||||
|
||||
--Landuse is hierachical. Highest level is Order (Residential) then Group (Residential-Dwelling) then Class (Residential-Dwelling-Detached house)
|
||||
--Landuse is hierarchical. Highest level is Order (Residential) then Group (Residential-Dwelling) then Class (Residential-Dwelling-Detached house)
|
||||
--Interface will collected most detailed (class) but visualise highest level (order)
|
||||
--Landuse is a table as #358
|
||||
--Prerequisite run bulk_sources migration first
|
||||
-- Land use is table with 3 levels of hierachy (highest to lowest). order > group > class
|
||||
-- Land use is table with 3 levels of hierarchy (highest to lowest). order > group > class
|
||||
|
||||
|
||||
-- Land use order, singular. Client and db constrained with foreign key
|
||||
|
@ -3,7 +3,7 @@
|
||||
-- -- Ownership type, enumerate type from:
|
||||
-- ALTER TABLE buildings DROP COLUMN IF EXISTS ownership_type;
|
||||
|
||||
-- -- Ownerhsip perception, would you describe this as a community asset?
|
||||
-- -- Ownership perception, would you describe this as a community asset?
|
||||
-- -- Boolean yes / no
|
||||
-- ALTER TABLE buildings DROP COLUMN IF EXISTS ownership_perception;
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
-- Remove team fields, update in paralell with adding new fields
|
||||
-- Remove team fields, update in parallel with adding new fields
|
||||
-- Award or awards (may be multiple) stored as json b object
|
||||
ALTER TABLE buildings DROP COLUMN IF EXISTS team_awards;
|
||||
|
@ -3,6 +3,6 @@
|
||||
ALTER TABLE buildings
|
||||
ADD COLUMN IF NOT EXISTS team_awards jsonb;
|
||||
|
||||
--To validate this input, the following confirms it's an valid object but not that the items in the object are validated agains those we will acccept
|
||||
--To validate this input, the following confirms it's an valid object but not that the items in the object are validated against those we will accept
|
||||
ALTER TABLE buildings
|
||||
ADD CONSTRAINT data_is_valid CHECK (is_jsonb_valid ('{"type": "object"}', team_awards));
|
||||
|
Loading…
Reference in New Issue
Block a user