Merge pull request #497 from mz8i/feature/sort-imports
Lint imports, semicolons
This commit is contained in:
commit
49fd95d980
@ -1,12 +1,11 @@
|
|||||||
import express from 'express';
|
|
||||||
import bodyParser from 'body-parser';
|
import bodyParser from 'body-parser';
|
||||||
|
import express from 'express';
|
||||||
import { authUser, getNewUserAPIKey, logout } from './services/user';
|
|
||||||
import { queryLocation } from './services/search';
|
|
||||||
|
|
||||||
import buildingsRouter from './routes/buildingsRouter';
|
import buildingsRouter from './routes/buildingsRouter';
|
||||||
import usersRouter from './routes/usersRouter';
|
|
||||||
import extractsRouter from './routes/extractsRouter';
|
import extractsRouter from './routes/extractsRouter';
|
||||||
|
import usersRouter from './routes/usersRouter';
|
||||||
|
import { queryLocation } from './services/search';
|
||||||
|
import { authUser, getNewUserAPIKey, logout } from './services/user';
|
||||||
|
|
||||||
|
|
||||||
const server = express.Router();
|
const server = express.Router();
|
||||||
@ -29,7 +28,7 @@ server.post('/login', function (req, res) {
|
|||||||
res.send(user);
|
res.send(user);
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
res.send(error);
|
res.send(error);
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// POST user logout
|
// POST user logout
|
||||||
@ -46,7 +45,7 @@ server.post('/logout', function (req, res) {
|
|||||||
server.post('/api/key', function (req, res) {
|
server.post('/api/key', function (req, res) {
|
||||||
if (!req.session.user_id) {
|
if (!req.session.user_id) {
|
||||||
res.send({ error: 'Must be logged in' });
|
res.send({ error: 'Must be logged in' });
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNewUserAPIKey(req.session.user_id).then(function (apiKey) {
|
getNewUserAPIKey(req.session.user_id).then(function (apiKey) {
|
||||||
@ -54,7 +53,7 @@ server.post('/api/key', function (req, res) {
|
|||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
res.send(error);
|
res.send(error);
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
// GET search
|
// GET search
|
||||||
server.get('/search', function (req, res) {
|
server.get('/search', function (req, res) {
|
||||||
@ -62,20 +61,20 @@ server.get('/search', function (req, res) {
|
|||||||
if (!searchTerm) {
|
if (!searchTerm) {
|
||||||
res.send({
|
res.send({
|
||||||
error: 'Please provide a search term'
|
error: 'Please provide a search term'
|
||||||
})
|
});
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
queryLocation(searchTerm).then((results) => {
|
queryLocation(searchTerm).then((results) => {
|
||||||
if (typeof (results) === 'undefined') {
|
if (typeof (results) === 'undefined') {
|
||||||
res.send({
|
res.send({
|
||||||
error: 'Database error'
|
error: 'Database error'
|
||||||
})
|
});
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
res.send({
|
res.send({
|
||||||
results: results.map(item => {
|
results: results.map(item => {
|
||||||
// map from DB results to GeoJSON Feature objects
|
// map from DB results to GeoJSON Feature objects
|
||||||
const geom = JSON.parse(item.st_asgeojson)
|
const geom = JSON.parse(item.st_asgeojson);
|
||||||
return {
|
return {
|
||||||
type: 'Feature',
|
type: 'Feature',
|
||||||
attributes: {
|
attributes: {
|
||||||
@ -83,13 +82,13 @@ server.get('/search', function (req, res) {
|
|||||||
zoom: item.zoom || 9
|
zoom: item.zoom || 9
|
||||||
},
|
},
|
||||||
geometry: geom
|
geometry: geom
|
||||||
}
|
};
|
||||||
})
|
})
|
||||||
})
|
});
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
res.send(error);
|
res.send(error);
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
server.use((err, req, res, next) => {
|
server.use((err, req, res, next) => {
|
||||||
if (res.headersSent) {
|
if (res.headersSent) {
|
||||||
@ -104,7 +103,7 @@ server.use((err, req, res, next) => {
|
|||||||
|
|
||||||
server.use((req, res) => {
|
server.use((req, res) => {
|
||||||
res.status(404).json({ error: 'Resource not found'});
|
res.status(404).json({ error: 'Resource not found'});
|
||||||
})
|
});
|
||||||
|
|
||||||
|
|
||||||
export default server;
|
export default server;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
|
|
||||||
|
import asyncController from '../routes/asyncController';
|
||||||
import * as buildingService from '../services/building';
|
import * as buildingService from '../services/building';
|
||||||
import * as userService from '../services/user';
|
import * as userService from '../services/user';
|
||||||
import asyncController from '../routes/asyncController';
|
|
||||||
|
|
||||||
|
|
||||||
// GET buildings
|
// GET buildings
|
||||||
@ -106,7 +107,7 @@ const getBuildingLikeById = asyncController(async (req: express.Request, res: ex
|
|||||||
// any value returned means like
|
// any value returned means like
|
||||||
res.send({ like: like });
|
res.send({ like: like });
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
res.send({ error: 'Database error' })
|
res.send({ error: 'Database error' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import * as dataExtractService from '../services/dataExtract';
|
|
||||||
import asyncController from '../routes/asyncController';
|
import asyncController from '../routes/asyncController';
|
||||||
|
import * as dataExtractService from '../services/dataExtract';
|
||||||
|
|
||||||
|
|
||||||
const getAllDataExtracts = asyncController(async function(req: express.Request, res: express.Response) {
|
const getAllDataExtracts = asyncController(async function(req: express.Request, res: express.Response) {
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import { URL } from 'url';
|
|
||||||
|
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
|
|
||||||
import * as userService from '../services/user';
|
import asyncController from '../routes/asyncController';
|
||||||
import * as passwordResetService from '../services/passwordReset';
|
import * as passwordResetService from '../services/passwordReset';
|
||||||
import { TokenVerificationError } from '../services/passwordReset';
|
import { TokenVerificationError } from '../services/passwordReset';
|
||||||
import asyncController from '../routes/asyncController';
|
import * as userService from '../services/user';
|
||||||
import { ValidationError } from '../validation';
|
import { ValidationError } from '../validation';
|
||||||
|
|
||||||
const createUser = asyncController(async (req: express.Request, res: express.Response) => {
|
const createUser = asyncController(async (req: express.Request, res: express.Response) => {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Request, Response, NextFunction } from 'express';
|
import { NextFunction, Request, Response } from 'express';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A wrapper for controller functions that return a Promise, enabling them to be used with Express
|
* A wrapper for controller functions that return a Promise, enabling them to be used with Express
|
||||||
@ -14,4 +14,4 @@ function asyncController(fn: (req: Request, res: Response, next: NextFunction) =
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default asyncController;
|
export default asyncController;
|
||||||
|
@ -12,4 +12,4 @@ router.route('/me')
|
|||||||
|
|
||||||
router.put('/password', userController.resetPassword);
|
router.put('/password', userController.resetPassword);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -2,10 +2,11 @@
|
|||||||
* Building data access
|
* Building data access
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
import { ITask } from 'pg-promise';
|
||||||
|
|
||||||
import db from '../../db';
|
import db from '../../db';
|
||||||
import { tileCache } from '../../tiles/rendererDefinition';
|
import { tileCache } from '../../tiles/rendererDefinition';
|
||||||
import { BoundingBox } from '../../tiles/types';
|
import { BoundingBox } from '../../tiles/types';
|
||||||
import { ITask } from 'pg-promise';
|
|
||||||
|
|
||||||
// data type note: PostgreSQL bigint (64-bit) is handled as string in JavaScript, because of
|
// data type note: PostgreSQL bigint (64-bit) is handled as string in JavaScript, because of
|
||||||
// JavaScript numerics are 64-bit double, giving only partial coverage.
|
// JavaScript numerics are 64-bit double, giving only partial coverage.
|
||||||
@ -276,7 +277,7 @@ async function updateBuildingData(
|
|||||||
|
|
||||||
console.log(update);
|
console.log(update);
|
||||||
const patches = compare(oldBuilding, update);
|
const patches = compare(oldBuilding, update);
|
||||||
console.log('Patching', buildingId, patches)
|
console.log('Patching', buildingId, patches);
|
||||||
const [forward, reverse] = patches;
|
const [forward, reverse] = patches;
|
||||||
if (Object.keys(forward).length === 0) {
|
if (Object.keys(forward).length === 0) {
|
||||||
throw 'No change provided';
|
throw 'No change provided';
|
||||||
@ -336,7 +337,7 @@ function privateQueryBuildingBBOX(buildingId: number){
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function expireBuildingTileCache(buildingId: number) {
|
async function expireBuildingTileCache(buildingId: number) {
|
||||||
const bbox = await privateQueryBuildingBBOX(buildingId)
|
const bbox = await privateQueryBuildingBBOX(buildingId);
|
||||||
const buildingBbox: BoundingBox = [bbox.xmax, bbox.ymax, bbox.xmin, bbox.ymin];
|
const buildingBbox: BoundingBox = [bbox.xmax, bbox.ymax, bbox.xmin, bbox.ymin];
|
||||||
tileCache.removeAllAtBbox(buildingBbox);
|
tileCache.removeAllAtBbox(buildingBbox);
|
||||||
}
|
}
|
||||||
|
@ -50,12 +50,12 @@ function getDataExtractFromRow(er: DataExtractRow): DataExtract {
|
|||||||
extract_id: er.extract_id,
|
extract_id: er.extract_id,
|
||||||
extracted_on: er.extracted_on,
|
extracted_on: er.extracted_on,
|
||||||
download_path: getDownloadLinkForExtract(er)
|
download_path: getDownloadLinkForExtract(er)
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDownloadLinkForExtract(extract: DataExtractRow): string {
|
function getDownloadLinkForExtract(extract: DataExtractRow): string {
|
||||||
const file_name = path.basename(extract.extract_path);
|
const file_name = path.basename(extract.extract_path);
|
||||||
return `/downloads/${file_name}` // TODO: potentially move base path to env var
|
return `/downloads/${file_name}`; // TODO: potentially move base path to env var
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import url, { URL } from 'url';
|
|
||||||
import { errors } from 'pg-promise';
|
|
||||||
import nodemailer from 'nodemailer';
|
import nodemailer from 'nodemailer';
|
||||||
|
import { errors } from 'pg-promise';
|
||||||
|
import url, { URL } from 'url';
|
||||||
|
|
||||||
import db from '../../db';
|
import db from '../../db';
|
||||||
import * as userService from './user';
|
|
||||||
import { transporter } from './email';
|
|
||||||
import { validatePassword } from '../validation';
|
import { validatePassword } from '../validation';
|
||||||
|
|
||||||
|
import { transporter } from './email';
|
||||||
|
import * as userService from './user';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a password reset token for the specified account and send the password reset link by email
|
* Generate a password reset token for the specified account and send the password reset link by email
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import { errors } from 'pg-promise';
|
import { errors } from 'pg-promise';
|
||||||
|
import { promisify } from 'util';
|
||||||
|
|
||||||
import db from '../../db';
|
import db from '../../db';
|
||||||
import { validateUsername, ValidationError, validatePassword } from '../validation';
|
import { validatePassword, validateUsername, ValidationError } from '../validation';
|
||||||
import { promisify } from 'util';
|
|
||||||
|
|
||||||
|
|
||||||
async function createUser(user) {
|
async function createUser(user) {
|
||||||
@ -71,9 +71,9 @@ async function authUser(username: string, password: string) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (user && user.auth_ok) {
|
if (user && user.auth_ok) {
|
||||||
return { user_id: user.user_id }
|
return { user_id: user.user_id };
|
||||||
} else {
|
} else {
|
||||||
return { error: 'Username or password not recognised' }
|
return { error: 'Username or password not recognised' };
|
||||||
}
|
}
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
if (err instanceof errors.QueryResultError) {
|
if (err instanceof errors.QueryResultError) {
|
||||||
@ -99,7 +99,7 @@ async function getUserById(id: string) {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
console.error('Error:', error)
|
console.error('Error:', error);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,7 +137,7 @@ async function getNewUserAPIKey(id: string) {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
console.error('Error:', error)
|
console.error('Error:', error);
|
||||||
return { error: 'Failed to generate new API key.' };
|
return { error: 'Failed to generate new API key.' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,7 +156,7 @@ async function authAPIUser(key: string) {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
console.error('Error:', error)
|
console.error('Error:', error);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,4 +22,4 @@ export {
|
|||||||
ValidationError,
|
ValidationError,
|
||||||
validateUsername,
|
validateUsername,
|
||||||
validatePassword
|
validatePassword
|
||||||
};
|
};
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
* Client-side entry point to shared frontend React App
|
* Client-side entry point to shared frontend React App
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { hydrate } from 'react-dom';
|
import { hydrate } from 'react-dom';
|
||||||
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
|
|
||||||
import App from './frontend/app';
|
import App from './frontend/app';
|
||||||
|
|
||||||
|
@ -1,29 +1,27 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import { Route, Switch, Link } from 'react-router-dom';
|
import { Link, Route, Switch } from 'react-router-dom';
|
||||||
|
|
||||||
|
import Header from './header';
|
||||||
|
import MapApp from './map-app';
|
||||||
|
import { Building } from './models/building';
|
||||||
|
import { User } from './models/user';
|
||||||
|
import AboutPage from './pages/about';
|
||||||
|
import ContactPage from './pages/contact';
|
||||||
|
import ContributorAgreementPage from './pages/contributor-agreement';
|
||||||
|
import DataAccuracyPage from './pages/data-accuracy';
|
||||||
|
import DataExtracts from './pages/data-extracts';
|
||||||
|
import OrdnanceSurveyLicencePage from './pages/ordnance-survey-licence';
|
||||||
|
import OrdnanceSurveyUprnPage from './pages/ordnance-survey-uprn';
|
||||||
|
import PrivacyPolicyPage from './pages/privacy-policy';
|
||||||
|
import ForgottenPassword from './user/forgotten-password';
|
||||||
|
import Login from './user/login';
|
||||||
|
import MyAccountPage from './user/my-account';
|
||||||
|
import PasswordReset from './user/password-reset';
|
||||||
|
import SignUp from './user/signup';
|
||||||
|
|
||||||
import '../../node_modules/bootstrap/dist/css/bootstrap.min.css';
|
import '../../node_modules/bootstrap/dist/css/bootstrap.min.css';
|
||||||
import './app.css';
|
import './app.css';
|
||||||
|
|
||||||
import Header from './header';
|
|
||||||
import AboutPage from './pages/about';
|
|
||||||
import ContributorAgreementPage from './pages/contributor-agreement';
|
|
||||||
import PrivacyPolicyPage from './pages/privacy-policy';
|
|
||||||
import DataExtracts from './pages/data-extracts';
|
|
||||||
|
|
||||||
import Login from './user/login';
|
|
||||||
import MyAccountPage from './user/my-account';
|
|
||||||
import SignUp from './user/signup';
|
|
||||||
import ForgottenPassword from './user/forgotten-password';
|
|
||||||
import PasswordReset from './user/password-reset';
|
|
||||||
|
|
||||||
import MapApp from './map-app';
|
|
||||||
import ContactPage from './pages/contact';
|
|
||||||
import DataAccuracyPage from './pages/data-accuracy';
|
|
||||||
import OrdnanceSurveyLicencePage from './pages/ordnance-survey-licence';
|
|
||||||
import OrdnanceSurveyUprnPage from './pages/ordnance-survey-uprn';
|
|
||||||
import { Building } from './models/building';
|
|
||||||
import { User } from './models/user';
|
|
||||||
|
|
||||||
|
|
||||||
interface AppProps {
|
interface AppProps {
|
||||||
user?: User;
|
user?: User;
|
||||||
|
@ -5,7 +5,7 @@ import InfoBox from '../components/info-box';
|
|||||||
|
|
||||||
|
|
||||||
interface BuildingNotFoundProps {
|
interface BuildingNotFoundProps {
|
||||||
mode: string
|
mode: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BuildingNotFound: React.FunctionComponent<BuildingNotFoundProps> = (props) => (
|
const BuildingNotFound: React.FunctionComponent<BuildingNotFoundProps> = (props) => (
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import BuildingNotFound from './building-not-found';
|
|
||||||
|
|
||||||
import LocationContainer from './data-containers/location';
|
|
||||||
import UseContainer from './data-containers/use';
|
|
||||||
import TypeContainer from './data-containers/type';
|
|
||||||
import AgeContainer from './data-containers/age';
|
|
||||||
import SizeContainer from './data-containers/size';
|
|
||||||
import ConstructionContainer from './data-containers/construction';
|
|
||||||
import TeamContainer from './data-containers/team';
|
|
||||||
import SustainabilityContainer from './data-containers/sustainability';
|
|
||||||
import StreetscapeContainer from './data-containers/streetscape';
|
|
||||||
import CommunityContainer from './data-containers/community';
|
|
||||||
import PlanningContainer from './data-containers/planning';
|
|
||||||
import LikeContainer from './data-containers/like';
|
|
||||||
import { Building } from '../models/building';
|
import { Building } from '../models/building';
|
||||||
|
|
||||||
|
import BuildingNotFound from './building-not-found';
|
||||||
|
import AgeContainer from './data-containers/age';
|
||||||
|
import CommunityContainer from './data-containers/community';
|
||||||
|
import ConstructionContainer from './data-containers/construction';
|
||||||
|
import LikeContainer from './data-containers/like';
|
||||||
|
import LocationContainer from './data-containers/location';
|
||||||
|
import PlanningContainer from './data-containers/planning';
|
||||||
|
import SizeContainer from './data-containers/size';
|
||||||
|
import StreetscapeContainer from './data-containers/streetscape';
|
||||||
|
import SustainabilityContainer from './data-containers/sustainability';
|
||||||
|
import TeamContainer from './data-containers/team';
|
||||||
|
import TypeContainer from './data-containers/type';
|
||||||
|
import UseContainer from './data-containers/use';
|
||||||
|
|
||||||
|
|
||||||
interface BuildingViewProps {
|
interface BuildingViewProps {
|
||||||
cat: string;
|
cat: string;
|
||||||
@ -23,7 +23,7 @@ interface BuildingViewProps {
|
|||||||
building?: Building;
|
building?: Building;
|
||||||
building_like?: boolean;
|
building_like?: boolean;
|
||||||
user?: any;
|
user?: any;
|
||||||
selectBuilding: (building: Building) => void
|
selectBuilding: (building: Building) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,7 +39,7 @@ const BuildingView: React.FunctionComponent<BuildingViewProps> = (props) => {
|
|||||||
title="Location"
|
title="Location"
|
||||||
help="https://pages.colouring.london/location"
|
help="https://pages.colouring.london/location"
|
||||||
intro="Where are the buildings? Address, location and cross-references."
|
intro="Where are the buildings? Address, location and cross-references."
|
||||||
/>
|
/>;
|
||||||
case 'use':
|
case 'use':
|
||||||
return <UseContainer
|
return <UseContainer
|
||||||
{...props}
|
{...props}
|
||||||
@ -47,7 +47,7 @@ const BuildingView: React.FunctionComponent<BuildingViewProps> = (props) => {
|
|||||||
title="Land Use"
|
title="Land Use"
|
||||||
intro="How are buildings used, and how does use change over time? Coming soon…"
|
intro="How are buildings used, and how does use change over time? Coming soon…"
|
||||||
help="https://pages.colouring.london/use"
|
help="https://pages.colouring.london/use"
|
||||||
/>
|
/>;
|
||||||
case 'type':
|
case 'type':
|
||||||
return <TypeContainer
|
return <TypeContainer
|
||||||
{...props}
|
{...props}
|
||||||
@ -55,21 +55,21 @@ const BuildingView: React.FunctionComponent<BuildingViewProps> = (props) => {
|
|||||||
title="Type"
|
title="Type"
|
||||||
intro="How were buildings previously used?"
|
intro="How were buildings previously used?"
|
||||||
help="https://www.pages.colouring.london/buildingtypology"
|
help="https://www.pages.colouring.london/buildingtypology"
|
||||||
/>
|
/>;
|
||||||
case 'age':
|
case 'age':
|
||||||
return <AgeContainer
|
return <AgeContainer
|
||||||
{...props}
|
{...props}
|
||||||
title="Age"
|
title="Age"
|
||||||
help="https://pages.colouring.london/age"
|
help="https://pages.colouring.london/age"
|
||||||
intro="Building age data can support energy analysis and help predict long-term change."
|
intro="Building age data can support energy analysis and help predict long-term change."
|
||||||
/>
|
/>;
|
||||||
case 'size':
|
case 'size':
|
||||||
return <SizeContainer
|
return <SizeContainer
|
||||||
{...props}
|
{...props}
|
||||||
title="Size & Shape"
|
title="Size & Shape"
|
||||||
intro="How big are buildings?"
|
intro="How big are buildings?"
|
||||||
help="https://pages.colouring.london/shapeandsize"
|
help="https://pages.colouring.london/shapeandsize"
|
||||||
/>
|
/>;
|
||||||
case 'construction':
|
case 'construction':
|
||||||
return <ConstructionContainer
|
return <ConstructionContainer
|
||||||
{...props}
|
{...props}
|
||||||
@ -77,7 +77,7 @@ const BuildingView: React.FunctionComponent<BuildingViewProps> = (props) => {
|
|||||||
intro="How are buildings built? Coming soon…"
|
intro="How are buildings built? Coming soon…"
|
||||||
help="https://pages.colouring.london/construction"
|
help="https://pages.colouring.london/construction"
|
||||||
inactive={true}
|
inactive={true}
|
||||||
/>
|
/>;
|
||||||
case 'team':
|
case 'team':
|
||||||
return <TeamContainer
|
return <TeamContainer
|
||||||
{...props}
|
{...props}
|
||||||
@ -85,7 +85,7 @@ const BuildingView: React.FunctionComponent<BuildingViewProps> = (props) => {
|
|||||||
intro="Who built the buildings? Coming soon…"
|
intro="Who built the buildings? Coming soon…"
|
||||||
help="https://pages.colouring.london/team"
|
help="https://pages.colouring.london/team"
|
||||||
inactive={true}
|
inactive={true}
|
||||||
/>
|
/>;
|
||||||
case 'sustainability':
|
case 'sustainability':
|
||||||
return <SustainabilityContainer
|
return <SustainabilityContainer
|
||||||
{...props}
|
{...props}
|
||||||
@ -93,7 +93,7 @@ const BuildingView: React.FunctionComponent<BuildingViewProps> = (props) => {
|
|||||||
intro="Are buildings energy efficient?"
|
intro="Are buildings energy efficient?"
|
||||||
help="https://pages.colouring.london/sustainability"
|
help="https://pages.colouring.london/sustainability"
|
||||||
inactive={false}
|
inactive={false}
|
||||||
/>
|
/>;
|
||||||
case 'streetscape':
|
case 'streetscape':
|
||||||
return <StreetscapeContainer
|
return <StreetscapeContainer
|
||||||
{...props}
|
{...props}
|
||||||
@ -101,7 +101,7 @@ const BuildingView: React.FunctionComponent<BuildingViewProps> = (props) => {
|
|||||||
intro="What's the building's context? Coming soon…"
|
intro="What's the building's context? Coming soon…"
|
||||||
help="https://pages.colouring.london/streetscape"
|
help="https://pages.colouring.london/streetscape"
|
||||||
inactive={true}
|
inactive={true}
|
||||||
/>
|
/>;
|
||||||
case 'community':
|
case 'community':
|
||||||
return <CommunityContainer
|
return <CommunityContainer
|
||||||
{...props}
|
{...props}
|
||||||
@ -109,24 +109,24 @@ const BuildingView: React.FunctionComponent<BuildingViewProps> = (props) => {
|
|||||||
intro="How does this building work for the local community?"
|
intro="How does this building work for the local community?"
|
||||||
help="https://pages.colouring.london/community"
|
help="https://pages.colouring.london/community"
|
||||||
inactive={true}
|
inactive={true}
|
||||||
/>
|
/>;
|
||||||
case 'planning':
|
case 'planning':
|
||||||
return <PlanningContainer
|
return <PlanningContainer
|
||||||
{...props}
|
{...props}
|
||||||
title="Planning"
|
title="Planning"
|
||||||
intro="Planning controls relating to protection and reuse."
|
intro="Planning controls relating to protection and reuse."
|
||||||
help="https://pages.colouring.london/planning"
|
help="https://pages.colouring.london/planning"
|
||||||
/>
|
/>;
|
||||||
case 'like':
|
case 'like':
|
||||||
return <LikeContainer
|
return <LikeContainer
|
||||||
{...props}
|
{...props}
|
||||||
title="Like Me!"
|
title="Like Me!"
|
||||||
intro="Do you like the building and think it contributes to the city?"
|
intro="Do you like the building and think it contributes to the city?"
|
||||||
help="https://pages.colouring.london/likeme"
|
help="https://pages.colouring.london/likeme"
|
||||||
/>
|
/>;
|
||||||
default:
|
default:
|
||||||
return <BuildingNotFound mode="view" />
|
return <BuildingNotFound mode="view" />;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export default BuildingView;
|
export default BuildingView;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
|
|
||||||
import './categories.css'
|
import './categories.css';
|
||||||
|
|
||||||
interface CategoriesProps {
|
interface CategoriesProps {
|
||||||
mode: 'view' | 'edit' | 'multi-edit';
|
mode: 'view' | 'edit' | 'multi-edit';
|
||||||
@ -119,7 +119,7 @@ const Categories: React.FC<CategoriesProps> = (props) => (
|
|||||||
building_id={props.building_id}
|
building_id={props.building_id}
|
||||||
/>
|
/>
|
||||||
</ol>
|
</ol>
|
||||||
)
|
);
|
||||||
|
|
||||||
interface CategoryProps {
|
interface CategoryProps {
|
||||||
mode: 'view' | 'edit' | 'multi-edit';
|
mode: 'view' | 'edit' | 'multi-edit';
|
||||||
@ -152,6 +152,6 @@ const Category: React.FC<CategoryProps> = (props) => {
|
|||||||
</NavLink>
|
</NavLink>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Categories;
|
export default Categories;
|
||||||
|
@ -19,6 +19,6 @@ const ContainerHeader: React.FunctionComponent<ContainerHeaderProps> = (props) =
|
|||||||
{props.children}
|
{props.children}
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
)
|
);
|
||||||
|
|
||||||
export default ContainerHeader;
|
export default ContainerHeader;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
|
||||||
import { DataTitleCopyable } from './data-title';
|
|
||||||
import { BaseDataEntryProps } from './data-entry';
|
import { BaseDataEntryProps } from './data-entry';
|
||||||
|
import { DataTitleCopyable } from './data-title';
|
||||||
|
|
||||||
interface CheckboxDataEntryProps extends BaseDataEntryProps {
|
interface CheckboxDataEntryProps extends BaseDataEntryProps {
|
||||||
value: boolean;
|
value: boolean;
|
||||||
@ -33,6 +33,6 @@ const CheckboxDataEntry: React.FunctionComponent<CheckboxDataEntryProps> = (prop
|
|||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default CheckboxDataEntry;
|
export default CheckboxDataEntry;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import React, { Fragment, useState } from "react";
|
import React, { Fragment, useState } from "react";
|
||||||
|
|
||||||
|
import { DownIcon, RightIcon } from "../../components/icons";
|
||||||
|
|
||||||
import './data-entry-group.css';
|
import './data-entry-group.css';
|
||||||
import { RightIcon, DownIcon } from "../../components/icons";
|
|
||||||
|
|
||||||
interface DataEntryGroupProps {
|
interface DataEntryGroupProps {
|
||||||
/** Name of the group */
|
/** Name of the group */
|
||||||
@ -29,7 +30,7 @@ const DataEntryGroup: React.FunctionComponent<DataEntryGroupProps> = (props) =>
|
|||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const CollapseIcon: React.FunctionComponent<{collapsed: boolean}> = (props) => (
|
const CollapseIcon: React.FunctionComponent<{collapsed: boolean}> = (props) => (
|
||||||
<span className="collapse-icon">
|
<span className="collapse-icon">
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
|
||||||
import { DataTitleCopyable } from './data-title';
|
|
||||||
import { CopyProps } from '../data-containers/category-view-props';
|
import { CopyProps } from '../data-containers/category-view-props';
|
||||||
|
|
||||||
|
import { DataTitleCopyable } from './data-title';
|
||||||
|
|
||||||
interface BaseDataEntryProps {
|
interface BaseDataEntryProps {
|
||||||
slug: string;
|
slug: string;
|
||||||
title: string;
|
title: string;
|
||||||
@ -17,7 +18,7 @@ interface DataEntryProps extends BaseDataEntryProps {
|
|||||||
value?: string;
|
value?: string;
|
||||||
maxLength?: number;
|
maxLength?: number;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
valueTransform?: (string) => string
|
valueTransform?: (string) => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DataEntry: React.FunctionComponent<DataEntryProps> = (props) => {
|
const DataEntry: React.FunctionComponent<DataEntryProps> = (props) => {
|
||||||
@ -47,7 +48,7 @@ const DataEntry: React.FunctionComponent<DataEntryProps> = (props) => {
|
|||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default DataEntry;
|
export default DataEntry;
|
||||||
export {
|
export {
|
||||||
|
@ -15,8 +15,8 @@ const DataTitle: React.FunctionComponent<DataTitleProps> = (props) => {
|
|||||||
{ props.title }
|
{ props.title }
|
||||||
{ props.tooltip? <Tooltip text={ props.tooltip } /> : null }
|
{ props.tooltip? <Tooltip text={ props.tooltip } /> : null }
|
||||||
</dt>
|
</dt>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
interface DataTitleCopyableProps {
|
interface DataTitleCopyableProps {
|
||||||
@ -48,7 +48,7 @@ const DataTitleCopyable: React.FunctionComponent<DataTitleCopyableProps> = (prop
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default DataTitle;
|
export default DataTitle;
|
||||||
export { DataTitleCopyable }
|
export { DataTitleCopyable };
|
||||||
|
@ -46,6 +46,6 @@ const LikeDataEntry: React.FunctionComponent<LikeDataEntryProps> = (props) => {
|
|||||||
</label>
|
</label>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default LikeDataEntry;
|
export default LikeDataEntry;
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import React, { Component, Fragment } from 'react';
|
import React, { Component, Fragment } from 'react';
|
||||||
|
|
||||||
import { sanitiseURL } from '../../helpers';
|
import { sanitiseURL } from '../../helpers';
|
||||||
import { DataTitleCopyable } from './data-title';
|
|
||||||
import { BaseDataEntryProps } from './data-entry';
|
import { BaseDataEntryProps } from './data-entry';
|
||||||
|
import { DataTitleCopyable } from './data-title';
|
||||||
|
|
||||||
|
|
||||||
interface MultiDataEntryProps extends BaseDataEntryProps {
|
interface MultiDataEntryProps extends BaseDataEntryProps {
|
||||||
@ -68,7 +69,7 @@ class MultiDataEntry extends Component<MultiDataEntryProps> {
|
|||||||
key={index}
|
key={index}
|
||||||
className="form-control">
|
className="form-control">
|
||||||
<a href={sanitiseURL(item)}>{item}</a>
|
<a href={sanitiseURL(item)}>{item}</a>
|
||||||
</li>
|
</li>;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
@ -97,7 +98,7 @@ class MultiDataEntry extends Component<MultiDataEntryProps> {
|
|||||||
onClick={this.add}
|
onClick={this.add}
|
||||||
disabled={props.mode === 'view'}
|
disabled={props.mode === 'view'}
|
||||||
className="btn btn-outline-dark">+</button>
|
className="btn btn-outline-dark">+</button>
|
||||||
</Fragment>
|
</Fragment>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
|
||||||
import { DataTitleCopyable } from './data-title';
|
|
||||||
import { BaseDataEntryProps } from './data-entry';
|
import { BaseDataEntryProps } from './data-entry';
|
||||||
|
import { DataTitleCopyable } from './data-title';
|
||||||
|
|
||||||
|
|
||||||
interface NumericDataEntryProps extends BaseDataEntryProps {
|
interface NumericDataEntryProps extends BaseDataEntryProps {
|
||||||
@ -42,6 +42,6 @@ const NumericDataEntry: React.FunctionComponent<NumericDataEntryProps> = (props)
|
|||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default NumericDataEntry;
|
export default NumericDataEntry;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
|
||||||
import { DataTitleCopyable } from './data-title';
|
|
||||||
import { BaseDataEntryProps } from './data-entry';
|
import { BaseDataEntryProps } from './data-entry';
|
||||||
|
import { DataTitleCopyable } from './data-title';
|
||||||
|
|
||||||
interface SelectDataEntryProps extends BaseDataEntryProps {
|
interface SelectDataEntryProps extends BaseDataEntryProps {
|
||||||
value: string;
|
value: string;
|
||||||
@ -41,6 +41,6 @@ const SelectDataEntry: React.FunctionComponent<SelectDataEntryProps> = (props) =
|
|||||||
</select>
|
</select>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default SelectDataEntry;
|
export default SelectDataEntry;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
|
||||||
import { DataTitleCopyable } from './data-title';
|
|
||||||
import { BaseDataEntryProps } from './data-entry';
|
import { BaseDataEntryProps } from './data-entry';
|
||||||
|
import { DataTitleCopyable } from './data-title';
|
||||||
|
|
||||||
interface TextboxDataEntryProps extends BaseDataEntryProps {
|
interface TextboxDataEntryProps extends BaseDataEntryProps {
|
||||||
value: string;
|
value: string;
|
||||||
@ -39,6 +39,6 @@ const TextboxDataEntry: React.FunctionComponent<TextboxDataEntryProps> = (props)
|
|||||||
></textarea>
|
></textarea>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default TextboxDataEntry;
|
export default TextboxDataEntry;
|
||||||
|
@ -53,7 +53,7 @@ const UPRNsDataEntry: React.FC<UPRNsDataEntryProps> = (props) => {
|
|||||||
}
|
}
|
||||||
</dd>
|
</dd>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default UPRNsDataEntry;
|
export default UPRNsDataEntry;
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React, { Component, Fragment } from 'react';
|
import React, { Component, Fragment } from 'react';
|
||||||
|
|
||||||
import NumericDataEntry from './numeric-data-entry';
|
|
||||||
import { dataFields } from '../../data_fields';
|
import { dataFields } from '../../data_fields';
|
||||||
import { CopyProps } from '../data-containers/category-view-props';
|
import { CopyProps } from '../data-containers/category-view-props';
|
||||||
|
|
||||||
|
import NumericDataEntry from './numeric-data-entry';
|
||||||
|
|
||||||
interface YearDataEntryProps {
|
interface YearDataEntryProps {
|
||||||
year: number;
|
year: number;
|
||||||
upper: number;
|
upper: number;
|
||||||
@ -22,7 +23,7 @@ class YearDataEntry extends Component<YearDataEntryProps, any> {
|
|||||||
lower: props.lower,
|
lower: props.lower,
|
||||||
decade: Math.floor(props.year / 10) * 10,
|
decade: Math.floor(props.year / 10) * 10,
|
||||||
century: Math.floor(props.year / 100) * 100
|
century: Math.floor(props.year / 100) * 100
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
// TODO add dropdown for decade, century
|
// TODO add dropdown for decade, century
|
||||||
// TODO roll in first/last year estimate
|
// TODO roll in first/last year estimate
|
||||||
@ -61,7 +62,7 @@ class YearDataEntry extends Component<YearDataEntryProps, any> {
|
|||||||
tooltip={dataFields.date_lower.tooltip}
|
tooltip={dataFields.date_lower.tooltip}
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import { Redirect, NavLink } from 'react-router-dom';
|
import { NavLink, Redirect } from 'react-router-dom';
|
||||||
|
|
||||||
import ContainerHeader from './container-header';
|
|
||||||
import ErrorBox from '../components/error-box';
|
import ErrorBox from '../components/error-box';
|
||||||
import InfoBox from '../components/info-box';
|
import InfoBox from '../components/info-box';
|
||||||
import { CopyControl } from './header-buttons/copy-control';
|
import { compareObjects } from '../helpers';
|
||||||
import { ViewEditControl } from './header-buttons/view-edit-control';
|
|
||||||
import { Building } from '../models/building';
|
import { Building } from '../models/building';
|
||||||
import { User } from '../models/user';
|
import { User } from '../models/user';
|
||||||
import { compareObjects } from '../helpers';
|
|
||||||
|
import ContainerHeader from './container-header';
|
||||||
import { CategoryViewProps, CopyProps } from './data-containers/category-view-props';
|
import { CategoryViewProps, CopyProps } from './data-containers/category-view-props';
|
||||||
|
import { CopyControl } from './header-buttons/copy-control';
|
||||||
|
import { ViewEditControl } from './header-buttons/view-edit-control';
|
||||||
|
|
||||||
interface DataContainerProps {
|
interface DataContainerProps {
|
||||||
title: string;
|
title: string;
|
||||||
@ -22,7 +23,7 @@ interface DataContainerProps {
|
|||||||
mode: 'view' | 'edit';
|
mode: 'view' | 'edit';
|
||||||
building?: Building;
|
building?: Building;
|
||||||
building_like?: boolean;
|
building_like?: boolean;
|
||||||
selectBuilding: (building: Building) => void
|
selectBuilding: (building: Building) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DataContainerState {
|
interface DataContainerState {
|
||||||
@ -88,7 +89,7 @@ const withCopyEdit = (WrappedComponent: React.ComponentType<CategoryViewProps>)
|
|||||||
toggleCopying() {
|
toggleCopying() {
|
||||||
this.setState({
|
this.setState({
|
||||||
copying: !this.state.copying
|
copying: !this.state.copying
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -105,7 +106,7 @@ const withCopyEdit = (WrappedComponent: React.ComponentType<CategoryViewProps>)
|
|||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
keys_to_copy: keys
|
keys_to_copy: keys
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isEdited() {
|
isEdited() {
|
||||||
@ -172,7 +173,7 @@ const withCopyEdit = (WrappedComponent: React.ComponentType<CategoryViewProps>)
|
|||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
this.setState({error: data.error})
|
this.setState({error: data.error});
|
||||||
} else {
|
} else {
|
||||||
this.props.selectBuilding(data);
|
this.props.selectBuilding(data);
|
||||||
this.updateBuildingState('likes_total', data.likes_total);
|
this.updateBuildingState('likes_total', data.likes_total);
|
||||||
@ -198,7 +199,7 @@ const withCopyEdit = (WrappedComponent: React.ComponentType<CategoryViewProps>)
|
|||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
this.setState({error: data.error})
|
this.setState({error: data.error});
|
||||||
} else {
|
} else {
|
||||||
this.props.selectBuilding(data);
|
this.props.selectBuilding(data);
|
||||||
}
|
}
|
||||||
@ -209,14 +210,14 @@ const withCopyEdit = (WrappedComponent: React.ComponentType<CategoryViewProps>)
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.props.mode === 'edit' && !this.props.user){
|
if (this.props.mode === 'edit' && !this.props.user){
|
||||||
return <Redirect to="/sign-up.html" />
|
return <Redirect to="/sign-up.html" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentBuilding = this.getEditedBuilding();
|
const currentBuilding = this.getEditedBuilding();
|
||||||
|
|
||||||
const values_to_copy = {}
|
const values_to_copy = {};
|
||||||
for (const key of Object.keys(this.state.keys_to_copy)) {
|
for (const key of Object.keys(this.state.keys_to_copy)) {
|
||||||
values_to_copy[key] = currentBuilding[key]
|
values_to_copy[key] = currentBuilding[key];
|
||||||
}
|
}
|
||||||
const data_string = JSON.stringify(values_to_copy);
|
const data_string = JSON.stringify(values_to_copy);
|
||||||
const copy: CopyProps = {
|
const copy: CopyProps = {
|
||||||
@ -224,7 +225,7 @@ const withCopyEdit = (WrappedComponent: React.ComponentType<CategoryViewProps>)
|
|||||||
toggleCopying: this.toggleCopying,
|
toggleCopying: this.toggleCopying,
|
||||||
toggleCopyAttribute: this.toggleCopyAttribute,
|
toggleCopyAttribute: this.toggleCopyAttribute,
|
||||||
copyingKey: (key: string) => this.state.keys_to_copy[key]
|
copyingKey: (key: string) => this.state.keys_to_copy[key]
|
||||||
}
|
};
|
||||||
|
|
||||||
const headerBackLink = `/${this.props.mode}/categories${this.props.building != undefined ? `/${this.props.building.building_id}` : ''}`;
|
const headerBackLink = `/${this.props.mode}/categories${this.props.building != undefined ? `/${this.props.building.building_id}` : ''}`;
|
||||||
const edited = this.isEdited();
|
const edited = this.isEdited();
|
||||||
@ -346,7 +347,7 @@ const withCopyEdit = (WrappedComponent: React.ComponentType<CategoryViewProps>)
|
|||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
export default withCopyEdit;
|
export default withCopyEdit;
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
|
||||||
import withCopyEdit from '../data-container';
|
import { dataFields } from '../../data_fields';
|
||||||
import MultiDataEntry from '../data-components/multi-data-entry';
|
import MultiDataEntry from '../data-components/multi-data-entry';
|
||||||
import NumericDataEntry from '../data-components/numeric-data-entry';
|
import NumericDataEntry from '../data-components/numeric-data-entry';
|
||||||
import SelectDataEntry from '../data-components/select-data-entry';
|
import SelectDataEntry from '../data-components/select-data-entry';
|
||||||
import TextboxDataEntry from '../data-components/textbox-data-entry';
|
import TextboxDataEntry from '../data-components/textbox-data-entry';
|
||||||
import YearDataEntry from '../data-components/year-data-entry';
|
import YearDataEntry from '../data-components/year-data-entry';
|
||||||
import { dataFields } from '../../data_fields';
|
import withCopyEdit from '../data-container';
|
||||||
|
|
||||||
import { CategoryViewProps } from './category-view-props';
|
import { CategoryViewProps } from './category-view-props';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,7 +75,7 @@ const AgeView: React.FunctionComponent<CategoryViewProps> = (props) => (
|
|||||||
placeholder="https://..."
|
placeholder="https://..."
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
);
|
||||||
const AgeContainer = withCopyEdit(AgeView);
|
const AgeContainer = withCopyEdit(AgeView);
|
||||||
|
|
||||||
export default AgeContainer;
|
export default AgeContainer;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
|
||||||
import withCopyEdit from '../data-container';
|
import withCopyEdit from '../data-container';
|
||||||
|
|
||||||
import { CategoryViewProps } from './category-view-props';
|
import { CategoryViewProps } from './category-view-props';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,7 +28,7 @@ const CommunityView: React.FunctionComponent<CategoryViewProps> = (props) => (
|
|||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
);
|
||||||
const CommunityContainer = withCopyEdit(CommunityView);
|
const CommunityContainer = withCopyEdit(CommunityView);
|
||||||
|
|
||||||
export default CommunityContainer;
|
export default CommunityContainer;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
|
||||||
import withCopyEdit from '../data-container';
|
import withCopyEdit from '../data-container';
|
||||||
import DataEntry from '../data-components/data-entry';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construction view/edit section
|
* Construction view/edit section
|
||||||
@ -50,7 +49,7 @@ const ConstructionView = (props) => (
|
|||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
);
|
||||||
const ConstructionContainer = withCopyEdit(ConstructionView);
|
const ConstructionContainer = withCopyEdit(ConstructionView);
|
||||||
|
|
||||||
export default ConstructionContainer;
|
export default ConstructionContainer;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
|
||||||
import withCopyEdit from '../data-container';
|
|
||||||
import LikeDataEntry from '../data-components/like-data-entry';
|
import LikeDataEntry from '../data-components/like-data-entry';
|
||||||
|
import withCopyEdit from '../data-container';
|
||||||
|
|
||||||
import { CategoryViewProps } from './category-view-props';
|
import { CategoryViewProps } from './category-view-props';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -16,7 +17,7 @@ const LikeView: React.FunctionComponent<CategoryViewProps> = (props) => (
|
|||||||
onLike={props.onLike}
|
onLike={props.onLike}
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
);
|
||||||
const LikeContainer = withCopyEdit(LikeView);
|
const LikeContainer = withCopyEdit(LikeView);
|
||||||
|
|
||||||
export default LikeContainer;
|
export default LikeContainer;
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
|
||||||
import withCopyEdit from '../data-container';
|
import InfoBox from '../../components/info-box';
|
||||||
|
import { dataFields } from '../../data_fields';
|
||||||
import DataEntry from '../data-components/data-entry';
|
import DataEntry from '../data-components/data-entry';
|
||||||
import NumericDataEntry from '../data-components/numeric-data-entry';
|
import NumericDataEntry from '../data-components/numeric-data-entry';
|
||||||
import UPRNsDataEntry from '../data-components/uprns-data-entry';
|
import UPRNsDataEntry from '../data-components/uprns-data-entry';
|
||||||
import InfoBox from '../../components/info-box';
|
import withCopyEdit from '../data-container';
|
||||||
import { dataFields } from '../../data_fields';
|
|
||||||
import { CategoryViewProps } from './category-view-props';
|
import { CategoryViewProps } from './category-view-props';
|
||||||
|
|
||||||
const LocationView: React.FunctionComponent<CategoryViewProps> = (props) => (
|
const LocationView: React.FunctionComponent<CategoryViewProps> = (props) => (
|
||||||
@ -113,7 +114,7 @@ const LocationView: React.FunctionComponent<CategoryViewProps> = (props) => (
|
|||||||
onChange={props.onChange}
|
onChange={props.onChange}
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
);
|
||||||
const LocationContainer = withCopyEdit(LocationView);
|
const LocationContainer = withCopyEdit(LocationView);
|
||||||
|
|
||||||
export default LocationContainer;
|
export default LocationContainer;
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
|
||||||
import withCopyEdit from '../data-container';
|
|
||||||
import DataEntry from '../data-components/data-entry';
|
|
||||||
import CheckboxDataEntry from '../data-components/checkbox-data-entry';
|
|
||||||
import SelectDataEntry from '../data-components/select-data-entry';
|
|
||||||
import { DataEntryGroup } from '../data-components/data-entry-group';
|
|
||||||
import { dataFields } from '../../data_fields';
|
import { dataFields } from '../../data_fields';
|
||||||
|
import CheckboxDataEntry from '../data-components/checkbox-data-entry';
|
||||||
|
import DataEntry from '../data-components/data-entry';
|
||||||
|
import { DataEntryGroup } from '../data-components/data-entry-group';
|
||||||
|
import SelectDataEntry from '../data-components/select-data-entry';
|
||||||
|
import withCopyEdit from '../data-container';
|
||||||
|
|
||||||
import { CategoryViewProps } from './category-view-props';
|
import { CategoryViewProps } from './category-view-props';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -202,7 +203,7 @@ const PlanningView: React.FunctionComponent<CategoryViewProps> = (props) => (
|
|||||||
/>
|
/>
|
||||||
</DataEntryGroup>
|
</DataEntryGroup>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
);
|
||||||
const PlanningContainer = withCopyEdit(PlanningView);
|
const PlanningContainer = withCopyEdit(PlanningView);
|
||||||
|
|
||||||
export default PlanningContainer
|
export default PlanningContainer;
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
|
||||||
import withCopyEdit from '../data-container';
|
import { dataFields } from '../../data_fields';
|
||||||
|
import { DataEntryGroup } from '../data-components/data-entry-group';
|
||||||
import NumericDataEntry from '../data-components/numeric-data-entry';
|
import NumericDataEntry from '../data-components/numeric-data-entry';
|
||||||
import SelectDataEntry from '../data-components/select-data-entry';
|
import SelectDataEntry from '../data-components/select-data-entry';
|
||||||
import { DataEntryGroup } from '../data-components/data-entry-group';
|
import withCopyEdit from '../data-container';
|
||||||
import { dataFields } from '../../data_fields';
|
|
||||||
import { CategoryViewProps } from './category-view-props';
|
import { CategoryViewProps } from './category-view-props';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -146,7 +147,7 @@ const SizeView: React.FunctionComponent<CategoryViewProps> = (props) => (
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
);
|
||||||
const SizeContainer = withCopyEdit(SizeView);
|
const SizeContainer = withCopyEdit(SizeView);
|
||||||
|
|
||||||
export default SizeContainer;
|
export default SizeContainer;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
|
||||||
import withCopyEdit from '../data-container';
|
import withCopyEdit from '../data-container';
|
||||||
|
|
||||||
import { CategoryViewProps } from './category-view-props';
|
import { CategoryViewProps } from './category-view-props';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -18,7 +19,7 @@ const StreetscapeView: React.FunctionComponent<CategoryViewProps> = (props) => (
|
|||||||
<li>Building shading</li>
|
<li>Building shading</li>
|
||||||
</ul>
|
</ul>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
);
|
||||||
const StreetscapeContainer = withCopyEdit(StreetscapeView);
|
const StreetscapeContainer = withCopyEdit(StreetscapeView);
|
||||||
|
|
||||||
export default StreetscapeContainer;
|
export default StreetscapeContainer;
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
|
||||||
import withCopyEdit from '../data-container';
|
|
||||||
import DataEntry from '../data-components/data-entry';
|
|
||||||
import SelectDataEntry from '../data-components/select-data-entry';
|
|
||||||
import NumericDataEntry from '../data-components/numeric-data-entry';
|
|
||||||
import { dataFields } from '../../data_fields';
|
import { dataFields } from '../../data_fields';
|
||||||
|
import NumericDataEntry from '../data-components/numeric-data-entry';
|
||||||
|
import SelectDataEntry from '../data-components/select-data-entry';
|
||||||
|
import withCopyEdit from '../data-container';
|
||||||
|
|
||||||
import { CategoryViewProps } from './category-view-props';
|
import { CategoryViewProps } from './category-view-props';
|
||||||
|
|
||||||
const EnergyCategoryOptions = ["A", "B", "C", "D", "E", "F", "G"];
|
const EnergyCategoryOptions = ["A", "B", "C", "D", "E", "F", "G"];
|
||||||
@ -78,7 +78,7 @@ const SustainabilityView: React.FunctionComponent<CategoryViewProps> = (props) =
|
|||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
const SustainabilityContainer = withCopyEdit(SustainabilityView);
|
const SustainabilityContainer = withCopyEdit(SustainabilityView);
|
||||||
|
|
||||||
export default SustainabilityContainer;
|
export default SustainabilityContainer;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
|
||||||
import withCopyEdit from '../data-container';
|
import withCopyEdit from '../data-container';
|
||||||
import DataEntry from '../data-components/data-entry';
|
|
||||||
import { CategoryViewProps } from './category-view-props';
|
import { CategoryViewProps } from './category-view-props';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,7 +25,7 @@ const TeamView: React.FunctionComponent<CategoryViewProps> = (props) => (
|
|||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
);
|
||||||
const TeamContainer = withCopyEdit(TeamView);
|
const TeamContainer = withCopyEdit(TeamView);
|
||||||
|
|
||||||
export default TeamContainer;
|
export default TeamContainer;
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
|
||||||
import withCopyEdit from '../data-container';
|
|
||||||
import SelectDataEntry from '../data-components/select-data-entry';
|
|
||||||
import NumericDataEntry from '../data-components/numeric-data-entry';
|
|
||||||
import DataEntry from '../data-components/data-entry';
|
|
||||||
import { dataFields } from '../../data_fields';
|
import { dataFields } from '../../data_fields';
|
||||||
|
import DataEntry from '../data-components/data-entry';
|
||||||
|
import NumericDataEntry from '../data-components/numeric-data-entry';
|
||||||
|
import SelectDataEntry from '../data-components/select-data-entry';
|
||||||
|
import withCopyEdit from '../data-container';
|
||||||
|
|
||||||
import { CategoryViewProps } from './category-view-props';
|
import { CategoryViewProps } from './category-view-props';
|
||||||
|
|
||||||
const AttachmentFormOptions = [
|
const AttachmentFormOptions = [
|
||||||
@ -54,7 +55,7 @@ const TypeView: React.FunctionComponent<CategoryViewProps> = (props) => {
|
|||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
const TypeContainer = withCopyEdit(TypeView);
|
const TypeContainer = withCopyEdit(TypeView);
|
||||||
|
|
||||||
export default TypeContainer;
|
export default TypeContainer;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
|
||||||
import withCopyEdit from '../data-container';
|
import withCopyEdit from '../data-container';
|
||||||
|
|
||||||
import { CategoryViewProps } from './category-view-props';
|
import { CategoryViewProps } from './category-view-props';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -31,7 +32,7 @@ const UseView: React.FunctionComponent<CategoryViewProps> = (props) => (
|
|||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
);
|
||||||
const UseContainer = withCopyEdit(UseView);
|
const UseContainer = withCopyEdit(UseView);
|
||||||
|
|
||||||
export default UseContainer;
|
export default UseContainer;
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { EditHistoryEntry } from '../models/edit-history-entry';
|
|
||||||
import { arrayToDictionary, parseDate } from '../../helpers';
|
|
||||||
import { dataFields } from '../../data_fields';
|
import { dataFields } from '../../data_fields';
|
||||||
|
import { arrayToDictionary, parseDate } from '../../helpers';
|
||||||
|
import { EditHistoryEntry } from '../../models/edit-history-entry';
|
||||||
|
|
||||||
import { CategoryEditSummary } from './category-edit-summary';
|
import { CategoryEditSummary } from './category-edit-summary';
|
||||||
|
|
||||||
import './building-edit-summary.css';
|
import './building-edit-summary.css';
|
||||||
|
|
||||||
interface BuildingEditSummaryProps {
|
interface BuildingEditSummaryProps {
|
||||||
historyEntry: EditHistoryEntry
|
historyEntry: EditHistoryEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatDate(dt: Date) {
|
function formatDate(dt: Date) {
|
||||||
@ -45,7 +47,7 @@ const BuildingEditSummary: React.FunctionComponent<BuildingEditSummaryProps> = p
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
BuildingEditSummary
|
BuildingEditSummary
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import './category-edit-summary.css';
|
|
||||||
import { FieldEditSummary } from './field-edit-summary';
|
import { FieldEditSummary } from './field-edit-summary';
|
||||||
|
|
||||||
|
import './category-edit-summary.css';
|
||||||
|
|
||||||
interface CategoryEditSummaryProps {
|
interface CategoryEditSummaryProps {
|
||||||
category: string;
|
category: string;
|
||||||
fields: {
|
fields: {
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { EditHistoryEntry } from '../models/edit-history-entry';
|
|
||||||
|
import { Building } from '../../models/building';
|
||||||
|
import { EditHistoryEntry } from '../../models/edit-history-entry';
|
||||||
|
import ContainerHeader from '../container-header';
|
||||||
|
|
||||||
import { BuildingEditSummary } from './building-edit-summary';
|
import { BuildingEditSummary } from './building-edit-summary';
|
||||||
|
|
||||||
import './edit-history.css';
|
import './edit-history.css';
|
||||||
import { Building } from '../../models/building';
|
|
||||||
import ContainerHeader from '../container-header';
|
|
||||||
|
|
||||||
interface EditHistoryProps {
|
interface EditHistoryProps {
|
||||||
building: Building;
|
building: Building;
|
||||||
@ -39,7 +41,7 @@ const EditHistory: React.FunctionComponent<EditHistoryProps> = (props) => {
|
|||||||
</ul>
|
</ul>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
EditHistory
|
EditHistory
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Building } from '../../models/building';
|
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
import { ViewIcon, EditIcon } from '../../components/icons';
|
|
||||||
|
import { EditIcon, ViewIcon } from '../../components/icons';
|
||||||
|
import { Building } from '../../models/building';
|
||||||
|
|
||||||
interface ViewEditControlProps {
|
interface ViewEditControlProps {
|
||||||
cat: string;
|
cat: string;
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
|
import { parse } from 'query-string';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link, Redirect, RouteComponentProps } from 'react-router-dom';
|
import { Link, Redirect, RouteComponentProps } from 'react-router-dom';
|
||||||
import { parse } from 'query-string';
|
|
||||||
|
|
||||||
import Sidebar from './sidebar';
|
import { BackIcon } from '../components/icons';
|
||||||
import InfoBox from '../components/info-box';
|
import InfoBox from '../components/info-box';
|
||||||
import { BackIcon }from '../components/icons';
|
|
||||||
import DataEntry from './data-components/data-entry';
|
|
||||||
import { dataFields } from '../data_fields';
|
import { dataFields } from '../data_fields';
|
||||||
import { User } from '../models/user';
|
import { User } from '../models/user';
|
||||||
|
|
||||||
|
import DataEntry from './data-components/data-entry';
|
||||||
|
import Sidebar from './sidebar';
|
||||||
|
|
||||||
interface MultiEditRouteParams {
|
interface MultiEditRouteParams {
|
||||||
cat: string;
|
cat: string;
|
||||||
}
|
}
|
||||||
@ -19,7 +20,7 @@ interface MultiEditProps extends RouteComponentProps<MultiEditRouteParams> {
|
|||||||
|
|
||||||
const MultiEdit: React.FC<MultiEditProps> = (props) => {
|
const MultiEdit: React.FC<MultiEditProps> = (props) => {
|
||||||
if (!props.user){
|
if (!props.user){
|
||||||
return <Redirect to="/sign-up.html" />
|
return <Redirect to="/sign-up.html" />;
|
||||||
}
|
}
|
||||||
const cat = props.match.params.cat;
|
const cat = props.match.params.cat;
|
||||||
if (cat === 'like') {
|
if (cat === 'like') {
|
||||||
@ -45,14 +46,14 @@ const MultiEdit: React.FC<MultiEditProps> = (props) => {
|
|||||||
|
|
||||||
let data: object;
|
let data: object;
|
||||||
if (cat === 'like'){
|
if (cat === 'like'){
|
||||||
data = { like: true }
|
data = { like: true };
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
// TODO: verify what happens if data is string[]
|
// TODO: verify what happens if data is string[]
|
||||||
data = JSON.parse(q.data as string);
|
data = JSON.parse(q.data as string);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error, q)
|
console.error(error, q);
|
||||||
data = {}
|
data = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +80,7 @@ const MultiEdit: React.FC<MultiEditProps> = (props) => {
|
|||||||
disabled={true}
|
disabled={true}
|
||||||
value={data[key]}
|
value={data[key]}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
</form>
|
</form>
|
||||||
@ -90,6 +91,6 @@ const MultiEdit: React.FC<MultiEditProps> = (props) => {
|
|||||||
</section>
|
</section>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default MultiEdit;
|
export default MultiEdit;
|
||||||
|
@ -3,14 +3,14 @@ import React from 'react';
|
|||||||
import './confirmation-modal.css';
|
import './confirmation-modal.css';
|
||||||
|
|
||||||
interface ConfirmationModalProps {
|
interface ConfirmationModalProps {
|
||||||
show: boolean,
|
show: boolean;
|
||||||
title: string,
|
title: string;
|
||||||
description: string,
|
description: string;
|
||||||
confirmButtonText?: string,
|
confirmButtonText?: string;
|
||||||
confirmButtonClass?: string,
|
confirmButtonClass?: string;
|
||||||
cancelButtonClass?: string,
|
cancelButtonClass?: string;
|
||||||
onConfirm: () => void,
|
onConfirm: () => void;
|
||||||
onCancel: () => void
|
onCancel: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ConfirmationModal: React.FunctionComponent<ConfirmationModalProps> = ({
|
const ConfirmationModal: React.FunctionComponent<ConfirmationModalProps> = ({
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
/**
|
/**
|
||||||
* Mini-library of icons
|
* Mini-library of icons
|
||||||
*/
|
*/
|
||||||
import React from 'react'
|
import { library } from '@fortawesome/fontawesome-svg-core';
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { faAngleLeft, faCaretDown, faCaretRight, faCaretUp, faCheck, faCheckDouble,
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
faEye, faInfoCircle, faPaintBrush, faQuestionCircle, faSearch, faTimes } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { faQuestionCircle, faPaintBrush, faInfoCircle, faTimes, faCheck, faCheckDouble,
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
faAngleLeft, faCaretDown, faSearch, faEye, faCaretUp, faCaretRight } from '@fortawesome/free-solid-svg-icons'
|
import React from 'react';
|
||||||
|
|
||||||
library.add(
|
library.add(
|
||||||
faQuestionCircle,
|
faQuestionCircle,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import './logo.css';
|
import './logo.css';
|
||||||
|
|
||||||
interface LogoProps {
|
interface LogoProps {
|
||||||
@ -44,6 +45,6 @@ const LogoGrid: React.FunctionComponent = () => (
|
|||||||
<div className="cell background-like"></div>
|
<div className="cell background-like"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
|
|
||||||
export { Logo };
|
export { Logo };
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
import './tooltip.css';
|
|
||||||
import { InfoIcon } from './icons';
|
import { InfoIcon } from './icons';
|
||||||
|
|
||||||
|
import './tooltip.css';
|
||||||
|
|
||||||
interface TooltipProps {
|
interface TooltipProps {
|
||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
import urlapi from 'url';
|
import urlapi from 'url';
|
||||||
|
|
||||||
function sanitiseURL(string){
|
function sanitiseURL(string){
|
||||||
let url_
|
let url_;
|
||||||
|
|
||||||
// http or https
|
// http or https
|
||||||
if (!(string.substring(0, 7) === 'http://' || string.substring(0, 8) === 'https://')){
|
if (!(string.substring(0, 7) === 'http://' || string.substring(0, 8) === 'https://')){
|
||||||
return null
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
url_ = document.createElement('a')
|
url_ = document.createElement('a');
|
||||||
url_.href = string
|
url_.href = string;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
try {
|
try {
|
||||||
url_ = urlapi.parse(string)
|
url_ = urlapi.parse(string);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return null
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// required (www.example.com)
|
// required (www.example.com)
|
||||||
if (!url_.hostname || url_.hostname === '' || url_.hostname === 'localhost'){
|
if (!url_.hostname || url_.hostname === '' || url_.hostname === 'localhost'){
|
||||||
return null
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// optional (/some/path)
|
// optional (/some/path)
|
||||||
@ -33,7 +33,7 @@ function sanitiseURL(string){
|
|||||||
// optional (#anchor)
|
// optional (#anchor)
|
||||||
// url_.hash;
|
// url_.hash;
|
||||||
|
|
||||||
return `${url_.protocol}//${url_.hostname}${url_.pathname || ''}${url_.search || ''}${url_.hash || ''}`
|
return `${url_.protocol}//${url_.hostname}${url_.pathname || ''}${url_.search || ''}${url_.hash || ''}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,8 +63,8 @@ function parseDate(isoUtcDate: string): Date {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function compareObjects(objA: object, objB: object): [object, object] {
|
function compareObjects(objA: object, objB: object): [object, object] {
|
||||||
const reverse = {}
|
const reverse = {};
|
||||||
const forward = {}
|
const forward = {};
|
||||||
for (const [key, value] of Object.entries(objB)) {
|
for (const [key, value] of Object.entries(objB)) {
|
||||||
if (objA[key] !== value) {
|
if (objA[key] !== value) {
|
||||||
reverse[key] = objA[key];
|
reverse[key] = objA[key];
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import React, { Fragment } from 'react';
|
|
||||||
import { Switch, Route, RouteComponentProps, Redirect } from 'react-router-dom';
|
|
||||||
|
|
||||||
import Welcome from './pages/welcome';
|
|
||||||
import Sidebar from './building/sidebar';
|
|
||||||
import Categories from './building/categories';
|
|
||||||
import MultiEdit from './building/multi-edit';
|
|
||||||
import BuildingView from './building/building-view';
|
|
||||||
import ColouringMap from './map/map';
|
|
||||||
import { parse } from 'query-string';
|
import { parse } from 'query-string';
|
||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import { Redirect, Route, RouteComponentProps, Switch } from 'react-router-dom';
|
||||||
|
|
||||||
|
import BuildingView from './building/building-view';
|
||||||
|
import Categories from './building/categories';
|
||||||
import { EditHistory } from './building/edit-history/edit-history';
|
import { EditHistory } from './building/edit-history/edit-history';
|
||||||
|
import MultiEdit from './building/multi-edit';
|
||||||
|
import Sidebar from './building/sidebar';
|
||||||
|
import ColouringMap from './map/map';
|
||||||
import { Building } from './models/building';
|
import { Building } from './models/building';
|
||||||
|
import Welcome from './pages/welcome';
|
||||||
|
|
||||||
interface MapAppRouteParams {
|
interface MapAppRouteParams {
|
||||||
mode: 'view' | 'edit' | 'multi-edit';
|
mode: 'view' | 'edit' | 'multi-edit';
|
||||||
@ -85,7 +85,7 @@ class MapApp extends React.Component<MapAppProps, MapAppState> {
|
|||||||
revisionId = +revisionId;
|
revisionId = +revisionId;
|
||||||
// bump revision id, only ever increasing
|
// bump revision id, only ever increasing
|
||||||
if (revisionId > this.state.revision_id) {
|
if (revisionId > this.state.revision_id) {
|
||||||
this.setState({ revision_id: revisionId })
|
this.setState({ revision_id: revisionId });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ class MapApp extends React.Component<MapAppProps, MapAppState> {
|
|||||||
this.setState({ building: building });
|
this.setState({ building: building });
|
||||||
}
|
}
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.error(err)
|
console.error(err);
|
||||||
this.setState({ building: building });
|
this.setState({ building: building });
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -138,7 +138,7 @@ class MapApp extends React.Component<MapAppProps, MapAppState> {
|
|||||||
this.props.history.push(`/${mode}/${category}/${building.building_id}`);
|
this.props.history.push(`/${mode}/${category}/${building.building_id}`);
|
||||||
}
|
}
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.error(err)
|
console.error(err);
|
||||||
this.setState({ building_like: false });
|
this.setState({ building_like: false });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -157,14 +157,14 @@ class MapApp extends React.Component<MapAppProps, MapAppState> {
|
|||||||
const q = parse(window.location.search);
|
const q = parse(window.location.search);
|
||||||
|
|
||||||
if (cat === 'like') {
|
if (cat === 'like') {
|
||||||
this.likeBuilding(building.building_id)
|
this.likeBuilding(building.building_id);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
// TODO: verify what happens if data is string[]
|
// TODO: verify what happens if data is string[]
|
||||||
const data = JSON.parse(q.data as string);
|
const data = JSON.parse(q.data as string);
|
||||||
this.updateBuilding(building.building_id, data)
|
this.updateBuilding(building.building_id, data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error, q)
|
console.error(error, q);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,7 +181,7 @@ class MapApp extends React.Component<MapAppProps, MapAppState> {
|
|||||||
res => res.json()
|
res => res.json()
|
||||||
).then(function (res) {
|
).then(function (res) {
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
console.error({ error: res.error })
|
console.error({ error: res.error });
|
||||||
} else {
|
} else {
|
||||||
this.increaseRevision(res.revision_id);
|
this.increaseRevision(res.revision_id);
|
||||||
}
|
}
|
||||||
@ -202,7 +202,7 @@ class MapApp extends React.Component<MapAppProps, MapAppState> {
|
|||||||
res => res.json()
|
res => res.json()
|
||||||
).then(res => {
|
).then(res => {
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
console.error({ error: res.error })
|
console.error({ error: res.error });
|
||||||
} else {
|
} else {
|
||||||
this.increaseRevision(res.revision_id);
|
this.increaseRevision(res.revision_id);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import './legend.css';
|
import { DownIcon, UpIcon } from '../components/icons';
|
||||||
import { Logo } from '../components/logo';
|
import { Logo } from '../components/logo';
|
||||||
import { DownIcon, UpIcon, BackIcon } from '../components/icons';
|
|
||||||
|
import './legend.css';
|
||||||
|
|
||||||
const LEGEND_CONFIG = {
|
const LEGEND_CONFIG = {
|
||||||
location: {
|
location: {
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
import React, { Component, Fragment } from 'react';
|
|
||||||
import { Map, TileLayer, ZoomControl, AttributionControl, GeoJSON } from 'react-leaflet-universal';
|
|
||||||
import { GeoJsonObject } from 'geojson';
|
import { GeoJsonObject } from 'geojson';
|
||||||
|
import React, { Component, Fragment } from 'react';
|
||||||
import '../../../node_modules/leaflet/dist/leaflet.css'
|
import { AttributionControl, GeoJSON, Map, TileLayer, ZoomControl } from 'react-leaflet-universal';
|
||||||
import './map.css'
|
|
||||||
|
|
||||||
import { HelpIcon } from '../components/icons';
|
import { HelpIcon } from '../components/icons';
|
||||||
|
import { Building } from '../models/building';
|
||||||
|
|
||||||
import Legend from './legend';
|
import Legend from './legend';
|
||||||
import SearchBox from './search-box';
|
import SearchBox from './search-box';
|
||||||
import ThemeSwitcher from './theme-switcher';
|
import ThemeSwitcher from './theme-switcher';
|
||||||
import { Building } from '../models/building';
|
|
||||||
|
import '../../../node_modules/leaflet/dist/leaflet.css';
|
||||||
|
import './map.css';
|
||||||
|
|
||||||
const OS_API_KEY = 'NVUxtY5r8eA6eIfwrPTAGKrAAsoeI9E9';
|
const OS_API_KEY = 'NVUxtY5r8eA6eIfwrPTAGKrAAsoeI9E9';
|
||||||
|
|
||||||
@ -83,7 +84,7 @@ class ColouringMap extends Component<ColouringMapProps, ColouringMapState> {
|
|||||||
}
|
}
|
||||||
}.bind(this)).catch(
|
}.bind(this)).catch(
|
||||||
(err) => console.error(err)
|
(err) => console.error(err)
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
themeSwitch(e) {
|
themeSwitch(e) {
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
import { Point } from 'geojson';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
import './search-box.css';
|
|
||||||
import { SearchIcon } from '../components/icons';
|
import { SearchIcon } from '../components/icons';
|
||||||
import { Point } from 'geojson';
|
|
||||||
|
import './search-box.css';
|
||||||
|
|
||||||
interface SearchResult {
|
interface SearchResult {
|
||||||
type: string;
|
type: string;
|
||||||
@ -10,12 +11,12 @@ interface SearchResult {
|
|||||||
label: string;
|
label: string;
|
||||||
zoom: number;
|
zoom: number;
|
||||||
};
|
};
|
||||||
geometry: Point
|
geometry: Point;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
interface SearchBoxProps {
|
interface SearchBoxProps {
|
||||||
onLocate: (lat: number, lng: number, zoom: number) => void
|
onLocate: (lat: number, lng: number, zoom: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SearchBoxState {
|
interface SearchBoxState {
|
||||||
@ -40,7 +41,7 @@ class SearchBox extends Component<SearchBoxProps, SearchBoxState> {
|
|||||||
collapsedSearch: true,
|
collapsedSearch: true,
|
||||||
//is this a small screen device? if not we will disable collapse option
|
//is this a small screen device? if not we will disable collapse option
|
||||||
smallScreen: false
|
smallScreen: false
|
||||||
}
|
};
|
||||||
this.handleChange = this.handleChange.bind(this);
|
this.handleChange = this.handleChange.bind(this);
|
||||||
this.search = this.search.bind(this);
|
this.search = this.search.bind(this);
|
||||||
this.handleKeyPress = this.handleKeyPress.bind(this);
|
this.handleKeyPress = this.handleKeyPress.bind(this);
|
||||||
@ -93,7 +94,7 @@ class SearchBox extends Component<SearchBoxProps, SearchBoxState> {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.setState({
|
this.setState({
|
||||||
fetching: true
|
fetching: true
|
||||||
})
|
});
|
||||||
|
|
||||||
fetch(
|
fetch(
|
||||||
'/api/search?q='+this.state.q
|
'/api/search?q='+this.state.q
|
||||||
@ -104,23 +105,23 @@ class SearchBox extends Component<SearchBoxProps, SearchBoxState> {
|
|||||||
this.setState({
|
this.setState({
|
||||||
results: data.results,
|
results: data.results,
|
||||||
fetching: false
|
fetching: false
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
console.error(data);
|
console.error(data);
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
results: [],
|
results: [],
|
||||||
fetching: false
|
fetching: false
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.error(err)
|
console.error(err);
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
results: [],
|
results: [],
|
||||||
fetching: false
|
fetching: false
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@ -148,7 +149,7 @@ class SearchBox extends Component<SearchBoxProps, SearchBoxState> {
|
|||||||
<div className="collapse-btn" onClick={this.expandSearch}>
|
<div className="collapse-btn" onClick={this.expandSearch}>
|
||||||
<SearchIcon />
|
<SearchIcon />
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const resultsList = this.state.results.length?
|
const resultsList = this.state.results.length?
|
||||||
@ -159,7 +160,7 @@ class SearchBox extends Component<SearchBoxProps, SearchBoxState> {
|
|||||||
const lng = result.geometry.coordinates[0];
|
const lng = result.geometry.coordinates[0];
|
||||||
const lat = result.geometry.coordinates[1];
|
const lat = result.geometry.coordinates[1];
|
||||||
const zoom = result.attributes.zoom;
|
const zoom = result.attributes.zoom;
|
||||||
const href = `?lng=${lng}&lat=${lat}&zoom=${zoom}`
|
const href = `?lng=${lng}&lat=${lat}&zoom=${zoom}`;
|
||||||
return (
|
return (
|
||||||
<li key={result.attributes.label}>
|
<li key={result.attributes.label}>
|
||||||
<a
|
<a
|
||||||
@ -171,7 +172,7 @@ class SearchBox extends Component<SearchBoxProps, SearchBoxState> {
|
|||||||
href={href}
|
href={href}
|
||||||
>{`${label.substring(0, 4)} ${label.substring(4, 7)}`}</a>
|
>{`${label.substring(0, 4)} ${label.substring(4, 7)}`}</a>
|
||||||
</li>
|
</li>
|
||||||
)
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
@ -196,7 +197,7 @@ class SearchBox extends Component<SearchBoxProps, SearchBoxState> {
|
|||||||
</form>
|
</form>
|
||||||
{ resultsList }
|
{ resultsList }
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import SupporterLogos from '../components/supporter-logos';
|
|
||||||
import './about.css';
|
|
||||||
import Categories from '../building/categories';
|
import Categories from '../building/categories';
|
||||||
|
import SupporterLogos from '../components/supporter-logos';
|
||||||
|
|
||||||
|
import './about.css';
|
||||||
|
|
||||||
const AboutPage = () => (
|
const AboutPage = () => (
|
||||||
<article>
|
<article>
|
||||||
@ -140,7 +141,7 @@ const AboutPage = () => (
|
|||||||
onSubmit={function() {window.open(
|
onSubmit={function() {window.open(
|
||||||
'https://tinyletter.com/colouringlondon',
|
'https://tinyletter.com/colouringlondon',
|
||||||
'popupwindow',
|
'popupwindow',
|
||||||
'scrollbars=yes,width=800,height=600'); return true}}>
|
'scrollbars=yes,width=800,height=600'); return true;}}>
|
||||||
<h3 className="h1">Keep in touch</h3>
|
<h3 className="h1">Keep in touch</h3>
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import InfoBox from '../components/info-box';
|
|
||||||
|
|
||||||
const ContributorAgreementPage : React.SFC<any> = () => (
|
const ContributorAgreementPage : React.SFC<any> = () => (
|
||||||
<article>
|
<article>
|
||||||
<section className='main-col'>
|
<section className='main-col'>
|
||||||
@ -40,6 +38,6 @@ const ContributorAgreementPage : React.SFC<any> = () => (
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</article>
|
</article>
|
||||||
)
|
);
|
||||||
|
|
||||||
export default ContributorAgreementPage;
|
export default ContributorAgreementPage;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import React, { FunctionComponent } from 'react';
|
import React, { FunctionComponent } from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { dateReviver } from '../../helpers';
|
import { dateReviver } from '../../helpers';
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
interface ExtractViewModel {
|
interface ExtractViewModel {
|
||||||
extract_id: number;
|
extract_id: number;
|
||||||
|
@ -11,4 +11,4 @@ const OrdnanceSurveyUprnPage = () => (
|
|||||||
</article>
|
</article>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default OrdnanceSurveyUprnPage;
|
export default OrdnanceSurveyUprnPage;
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import InfoBox from '../components/info-box';
|
|
||||||
|
|
||||||
const PrivacyPolicyPage: React.SFC<any> = () => (
|
const PrivacyPolicyPage: React.SFC<any> = () => (
|
||||||
<article>
|
<article>
|
||||||
<section className='main-col'>
|
<section className='main-col'>
|
||||||
@ -118,6 +116,6 @@ const PrivacyPolicyPage: React.SFC<any> = () => (
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</article>
|
</article>
|
||||||
)
|
);
|
||||||
|
|
||||||
export default PrivacyPolicyPage;
|
export default PrivacyPolicyPage;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React, { FormEvent, ChangeEvent } from 'react';
|
import React, { ChangeEvent, FormEvent } from 'react';
|
||||||
import InfoBox from '../components/info-box';
|
|
||||||
import ErrorBox from '../components/error-box';
|
import ErrorBox from '../components/error-box';
|
||||||
|
import InfoBox from '../components/info-box';
|
||||||
|
|
||||||
interface ForgottenPasswordState {
|
interface ForgottenPasswordState {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
@ -79,6 +80,6 @@ export default class ForgottenPassword extends React.Component<{}, ForgottenPass
|
|||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
</article>
|
</article>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Redirect, Link } from 'react-router-dom';
|
import { Link, Redirect } from 'react-router-dom';
|
||||||
|
|
||||||
import ErrorBox from '../components/error-box';
|
import ErrorBox from '../components/error-box';
|
||||||
import InfoBox from '../components/info-box';
|
import InfoBox from '../components/info-box';
|
||||||
@ -7,7 +7,7 @@ import SupporterLogos from '../components/supporter-logos';
|
|||||||
import { User } from '../models/user';
|
import { User } from '../models/user';
|
||||||
|
|
||||||
interface LoginProps {
|
interface LoginProps {
|
||||||
user: User,
|
user: User;
|
||||||
login: (user: User) => void;
|
login: (user: User) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ class Login extends Component<LoginProps, any> {
|
|||||||
|
|
||||||
handleSubmit(event) {
|
handleSubmit(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.setState({error: undefined})
|
this.setState({error: undefined});
|
||||||
|
|
||||||
fetch('/api/login', {
|
fetch('/api/login', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -50,7 +50,7 @@ class Login extends Component<LoginProps, any> {
|
|||||||
res => res.json()
|
res => res.json()
|
||||||
).then(function(res){
|
).then(function(res){
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
this.setState({error: res.error})
|
this.setState({error: res.error});
|
||||||
} else {
|
} else {
|
||||||
fetch('/api/users/me', {
|
fetch('/api/users/me', {
|
||||||
credentials: 'same-origin'
|
credentials: 'same-origin'
|
||||||
@ -58,13 +58,13 @@ class Login extends Component<LoginProps, any> {
|
|||||||
(res) => res.json()
|
(res) => res.json()
|
||||||
).then(user => {
|
).then(user => {
|
||||||
if (user.error) {
|
if (user.error) {
|
||||||
this.setState({error: user.error})
|
this.setState({error: user.error});
|
||||||
} else {
|
} else {
|
||||||
this.props.login(user)
|
this.props.login(user);
|
||||||
}
|
}
|
||||||
}).catch(
|
}).catch(
|
||||||
(err) => this.setState({error: err})
|
(err) => this.setState({error: err})
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}.bind(this)).catch(
|
}.bind(this)).catch(
|
||||||
(err) => this.setState({error: err})
|
(err) => this.setState({error: err})
|
||||||
@ -73,7 +73,7 @@ class Login extends Component<LoginProps, any> {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.props.user && !this.props.user.error) {
|
if (this.props.user && !this.props.user.error) {
|
||||||
return <Redirect to="/my-account.html" />
|
return <Redirect to="/my-account.html" />;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<article>
|
<article>
|
||||||
@ -130,7 +130,7 @@ class Login extends Component<LoginProps, any> {
|
|||||||
<SupporterLogos />
|
<SupporterLogos />
|
||||||
</section>
|
</section>
|
||||||
</article>
|
</article>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ class MyAccountPage extends Component<MyAccountPageProps, MyAccountPageState> {
|
|||||||
res => res.json()
|
res => res.json()
|
||||||
).then(function(res){
|
).then(function(res){
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
this.setState({error: res.error})
|
this.setState({error: res.error});
|
||||||
} else {
|
} else {
|
||||||
this.props.logout();
|
this.props.logout();
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ class MyAccountPage extends Component<MyAccountPageProps, MyAccountPageState> {
|
|||||||
res => res.json()
|
res => res.json()
|
||||||
).then(function(res){
|
).then(function(res){
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
this.setState({error: res.error})
|
this.setState({error: res.error});
|
||||||
} else {
|
} else {
|
||||||
this.props.updateUser(res);
|
this.props.updateUser(res);
|
||||||
}
|
}
|
||||||
@ -176,7 +176,7 @@ class MyAccountPage extends Component<MyAccountPageProps, MyAccountPageState> {
|
|||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Redirect to="/login.html" />
|
<Redirect to="/login.html" />
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React, { FormEvent } from 'react';
|
import React, { FormEvent } from 'react';
|
||||||
import { RouteComponentProps, Redirect } from 'react-router';
|
import { Redirect, RouteComponentProps } from 'react-router';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import ErrorBox from '../components/error-box';
|
import ErrorBox from '../components/error-box';
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
interface PasswordResetState {
|
interface PasswordResetState {
|
||||||
error: string;
|
error: string;
|
||||||
@ -117,6 +117,6 @@ export default class PasswordReset extends React.Component<RouteComponentProps,
|
|||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
</article>
|
</article>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Redirect, Link } from 'react-router-dom';
|
import { Link, Redirect } from 'react-router-dom';
|
||||||
|
|
||||||
import ErrorBox from '../components/error-box';
|
import ErrorBox from '../components/error-box';
|
||||||
import InfoBox from '../components/info-box';
|
import InfoBox from '../components/info-box';
|
||||||
@ -50,7 +50,7 @@ class SignUp extends Component<SignUpProps, SignUpState> {
|
|||||||
|
|
||||||
handleSubmit(event) {
|
handleSubmit(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.setState({error: undefined})
|
this.setState({error: undefined});
|
||||||
|
|
||||||
fetch('/api/users', {
|
fetch('/api/users', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -63,7 +63,7 @@ class SignUp extends Component<SignUpProps, SignUpState> {
|
|||||||
res => res.json()
|
res => res.json()
|
||||||
).then(function(res){
|
).then(function(res){
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
this.setState({error: res.error})
|
this.setState({error: res.error});
|
||||||
} else {
|
} else {
|
||||||
fetch('/api/users/me', {
|
fetch('/api/users/me', {
|
||||||
credentials: 'same-origin'
|
credentials: 'same-origin'
|
||||||
@ -73,7 +73,7 @@ class SignUp extends Component<SignUpProps, SignUpState> {
|
|||||||
(user) => this.props.login(user)
|
(user) => this.props.login(user)
|
||||||
).catch(
|
).catch(
|
||||||
(err) => this.setState({error: err})
|
(err) => this.setState({error: err})
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}.bind(this)).catch(
|
}.bind(this)).catch(
|
||||||
(err) => this.setState({error: err})
|
(err) => this.setState({error: err})
|
||||||
@ -82,7 +82,7 @@ class SignUp extends Component<SignUpProps, SignUpState> {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.props.user) {
|
if (this.props.user) {
|
||||||
return <Redirect to="/my-account.html" />
|
return <Redirect to="/my-account.html" />;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<article>
|
<article>
|
||||||
@ -175,7 +175,7 @@ class SignUp extends Component<SignUpProps, SignUpState> {
|
|||||||
<SupporterLogos />
|
<SupporterLogos />
|
||||||
</section>
|
</section>
|
||||||
</article>
|
</article>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,20 +1,18 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StaticRouter } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { renderToString } from 'react-dom/server';
|
import { renderToString } from 'react-dom/server';
|
||||||
|
import { StaticRouter } from 'react-router-dom';
|
||||||
import serialize from 'serialize-javascript';
|
import serialize from 'serialize-javascript';
|
||||||
|
|
||||||
import App from './frontend/app';
|
|
||||||
|
|
||||||
import { parseBuildingURL } from './parse';
|
|
||||||
import { getUserById } from './api/services/user';
|
|
||||||
import {
|
import {
|
||||||
getBuildingById,
|
getBuildingById,
|
||||||
getBuildingLikeById,
|
getBuildingLikeById,
|
||||||
getBuildingUPRNsById,
|
getBuildingUPRNsById,
|
||||||
getLatestRevisionId
|
getLatestRevisionId
|
||||||
} from './api/services/building';
|
} from './api/services/building';
|
||||||
|
import { getUserById } from './api/services/user';
|
||||||
|
import App from './frontend/app';
|
||||||
|
import { parseBuildingURL } from './parse';
|
||||||
|
|
||||||
|
|
||||||
// reference packed assets
|
// reference packed assets
|
||||||
|
@ -27,7 +27,7 @@ function parseBuildingURL(url) {
|
|||||||
const matches = re.exec(url);
|
const matches = re.exec(url);
|
||||||
|
|
||||||
if (matches && matches.length >= 2) {
|
if (matches && matches.length >= 2) {
|
||||||
return strictParseInt(matches[1])
|
return strictParseInt(matches[1]);
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,14 @@
|
|||||||
* - entry-point to shared React App
|
* - entry-point to shared React App
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import express from 'express';
|
|
||||||
|
|
||||||
import session from 'express-session';
|
|
||||||
import pgConnect from 'connect-pg-simple';
|
import pgConnect from 'connect-pg-simple';
|
||||||
|
import express from 'express';
|
||||||
|
import session from 'express-session';
|
||||||
|
|
||||||
import db from './db';
|
|
||||||
import tileserver from './tiles/tileserver';
|
|
||||||
import apiServer from './api/api';
|
import apiServer from './api/api';
|
||||||
|
import db from './db';
|
||||||
import frontendRoute from './frontendRoute';
|
import frontendRoute from './frontendRoute';
|
||||||
|
import tileserver from './tiles/tileserver';
|
||||||
|
|
||||||
// create server
|
// create server
|
||||||
const server = express();
|
const server = express();
|
||||||
@ -38,9 +37,9 @@ const sess: any = { // TODO: remove any
|
|||||||
};
|
};
|
||||||
if (server.get('env') === 'production') {
|
if (server.get('env') === 'production') {
|
||||||
// trust first proxy
|
// trust first proxy
|
||||||
server.set('trust proxy', 1)
|
server.set('trust proxy', 1);
|
||||||
// serve secure cookies
|
// serve secure cookies
|
||||||
sess.cookie.secure = true
|
sess.cookie.secure = true;
|
||||||
}
|
}
|
||||||
server.use(session(sess));
|
server.use(session(sess));
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { strictParseInt } from "../parse";
|
import { strictParseInt } from "../parse";
|
||||||
|
|
||||||
import { DataConfig } from "./types";
|
import { DataConfig } from "./types";
|
||||||
|
|
||||||
const BUILDING_LAYER_DEFINITIONS = {
|
const BUILDING_LAYER_DEFINITIONS = {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { TileCache } from "./tileCache";
|
import { getAllLayerNames, getBuildingLayerNames, getBuildingsDataConfig, getHighlightDataConfig } from "./dataDefinition";
|
||||||
import { BoundingBox, TileParams, Tile } from "./types";
|
|
||||||
import { getBuildingsDataConfig, getHighlightDataConfig, getAllLayerNames, getBuildingLayerNames } from "./dataDefinition";
|
|
||||||
import { isOutsideExtent } from "./util";
|
|
||||||
import { renderDataSourceTile } from "./renderers/renderDataSourceTile";
|
|
||||||
import { getTileWithCaching } from "./renderers/getTileWithCaching";
|
|
||||||
import { stitchTile } from "./renderers/stitchTile";
|
|
||||||
import { createBlankTile } from "./renderers/createBlankTile";
|
import { createBlankTile } from "./renderers/createBlankTile";
|
||||||
|
import { getTileWithCaching } from "./renderers/getTileWithCaching";
|
||||||
|
import { renderDataSourceTile } from "./renderers/renderDataSourceTile";
|
||||||
|
import { stitchTile } from "./renderers/stitchTile";
|
||||||
|
import { TileCache } from "./tileCache";
|
||||||
|
import { BoundingBox, Tile, TileParams } from "./types";
|
||||||
|
import { isOutsideExtent } from "./util";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of all tilesets handled by the tile server
|
* A list of all tilesets handled by the tile server
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { TileParams, RendererFunction, Tile } from "../types";
|
|
||||||
import { TileCache } from "../tileCache";
|
import { TileCache } from "../tileCache";
|
||||||
|
import { RendererFunction, Tile, TileParams } from "../types";
|
||||||
|
|
||||||
|
|
||||||
async function getTileWithCaching(tileParams: TileParams, dataParams: any, tileCache: TileCache, renderTile: RendererFunction): Promise<Tile> {
|
async function getTileWithCaching(tileParams: TileParams, dataParams: any, tileCache: TileCache, renderTile: RendererFunction): Promise<Tile> {
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import path from 'path';
|
|
||||||
|
|
||||||
import mapnik from "mapnik";
|
import mapnik from "mapnik";
|
||||||
|
import path from 'path';
|
||||||
import { TileParams, Tile, TableDefinitionFunction } from "../types";
|
|
||||||
import { getBbox, TILE_SIZE } from "../util";
|
|
||||||
import { promisify } from "util";
|
import { promisify } from "util";
|
||||||
|
|
||||||
|
import { TableDefinitionFunction, Tile, TileParams } from "../types";
|
||||||
|
import { getBbox, TILE_SIZE } from "../util";
|
||||||
|
|
||||||
|
|
||||||
const TILE_BUFFER_SIZE = 64;
|
const TILE_BUFFER_SIZE = 64;
|
||||||
const PROJ4_STRING = '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over';
|
const PROJ4_STRING = '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
|
|
||||||
import { TileParams, RendererFunction, Tile } from "../types";
|
import { RendererFunction, Tile, TileParams } from "../types";
|
||||||
import { getBbox, getXYZ, TILE_SIZE } from "../util";
|
import { getBbox, getXYZ, TILE_SIZE } from "../util";
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,12 +16,12 @@
|
|||||||
// Using node-fs package to patch fs
|
// Using node-fs package to patch fs
|
||||||
// for node >10 we could drop this in favour of fs.mkdir (which has recursive option)
|
// for node >10 we could drop this in favour of fs.mkdir (which has recursive option)
|
||||||
// and then use stdlib `import fs from 'fs';`
|
// and then use stdlib `import fs from 'fs';`
|
||||||
import fs from 'node-fs';
|
|
||||||
import { promisify } from 'util'
|
|
||||||
import { Image } from 'mapnik';
|
import { Image } from 'mapnik';
|
||||||
|
import fs from 'node-fs';
|
||||||
|
import { promisify } from 'util';
|
||||||
|
|
||||||
import { TileParams, BoundingBox } from './types';
|
import { BoundingBox, TileParams } from './types';
|
||||||
import { getXYZ, formatParams } from './util';
|
import { formatParams, getXYZ } from './util';
|
||||||
|
|
||||||
// TODO: switch to modern node and use built-in fs with promise-based API
|
// TODO: switch to modern node and use built-in fs with promise-based API
|
||||||
const readFile = promisify(fs.readFile),
|
const readFile = promisify(fs.readFile),
|
||||||
@ -113,7 +113,7 @@ class TileCache {
|
|||||||
if(!this.shouldBulkClearTileset(tileset)) continue;
|
if(!this.shouldBulkClearTileset(tileset)) continue;
|
||||||
|
|
||||||
for (let z = this.cacheDomain.minZoom; z <= this.cacheDomain.maxZoom; z++) {
|
for (let z = this.cacheDomain.minZoom; z <= this.cacheDomain.maxZoom; z++) {
|
||||||
let tileBounds = getXYZ(bbox, z)
|
let tileBounds = getXYZ(bbox, z);
|
||||||
for (let x = tileBounds.minX; x <= tileBounds.maxX; x++) {
|
for (let x = tileBounds.minX; x <= tileBounds.maxX; x++) {
|
||||||
for (let y = tileBounds.minY; y <= tileBounds.maxY; y++) {
|
for (let y = tileBounds.minY; y <= tileBounds.maxY; y++) {
|
||||||
for (const scale of this.cacheDomain.scales) {
|
for (const scale of this.cacheDomain.scales) {
|
||||||
|
@ -5,10 +5,11 @@
|
|||||||
*/
|
*/
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
|
|
||||||
import { strictParseInt } from '../parse';
|
|
||||||
import { TileParams } from './types';
|
|
||||||
import { renderTile, allTilesets } from './rendererDefinition';
|
|
||||||
import asyncController from '../api/routes/asyncController';
|
import asyncController from '../api/routes/asyncController';
|
||||||
|
import { strictParseInt } from '../parse';
|
||||||
|
|
||||||
|
import { allTilesets, renderTile } from './rendererDefinition';
|
||||||
|
import { TileParams } from './types';
|
||||||
|
|
||||||
const handleTileRequest = asyncController(async function (req: express.Request, res: express.Response) {
|
const handleTileRequest = asyncController(async function (req: express.Request, res: express.Response) {
|
||||||
try {
|
try {
|
||||||
@ -30,7 +31,7 @@ const handleTileRequest = asyncController(async function (req: express.Request,
|
|||||||
});
|
});
|
||||||
|
|
||||||
// tiles router
|
// tiles router
|
||||||
const router = express.Router()
|
const router = express.Router();
|
||||||
|
|
||||||
router.get('/:tileset/:z/:x/:y(\\d+):scale(@\\dx)?.png', handleTileRequest);
|
router.get('/:tileset/:z/:x/:y(\\d+):scale(@\\dx)?.png', handleTileRequest);
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ type Tile = Image | Sharp;
|
|||||||
type RendererFunction = (tileParams: TileParams, dataParams: any) => Promise<Tile>;
|
type RendererFunction = (tileParams: TileParams, dataParams: any) => Promise<Tile>;
|
||||||
|
|
||||||
interface TileRenderer {
|
interface TileRenderer {
|
||||||
getTile: RendererFunction
|
getTile: RendererFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import SphericalMercator from '@mapbox/sphericalmercator';
|
import SphericalMercator from '@mapbox/sphericalmercator';
|
||||||
|
|
||||||
import { TileParams, BoundingBox } from './types';
|
import { BoundingBox, TileParams } from './types';
|
||||||
|
|
||||||
const TILE_SIZE = 256;
|
const TILE_SIZE = 256;
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ function getBbox(z, x, y) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getXYZ(bbox, z) {
|
function getXYZ(bbox, z) {
|
||||||
return mercator.xyz(bbox, z, false, '900913')
|
return mercator.xyz(bbox, z, false, '900913');
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatParams({ tileset, z, x, y, scale }: TileParams): string {
|
function formatParams({ tileset, z, x, y, scale }: TileParams): string {
|
||||||
|
@ -1,6 +1,19 @@
|
|||||||
{
|
{
|
||||||
"defaultSeverity": "warning",
|
"defaultSeverity": "warning",
|
||||||
"rules": {
|
"rules": {
|
||||||
"eofline": true
|
"eofline": true,
|
||||||
|
"ordered-imports": [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
"grouped-imports": true,
|
||||||
|
"groups": [
|
||||||
|
{ "name": "css", "match": "\\.css$", "order": 40 },
|
||||||
|
{ "name": "parent directories", "match": "^\\.\\.", "order": 20 },
|
||||||
|
{ "name": "current directory", "match": "^\\.", "order": 30 },
|
||||||
|
{ "name": "libraries", "match": ".*", "order": 10 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"semicolon": true
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user