finish chores
This commit is contained in:
parent
64ad60a8e2
commit
1cc4af9c1b
24 changed files with 542 additions and 224 deletions
|
|
@ -7,7 +7,7 @@ RUN go install ./...
|
|||
|
||||
|
||||
FROM alpine:latest
|
||||
RUN apk --no-cache add ca-certificates
|
||||
RUN apk --no-cache add ca-certificates tzdata
|
||||
WORKDIR /app
|
||||
COPY --from=builder /go/bin/app .
|
||||
COPY config.yaml config.yaml
|
||||
|
|
|
|||
|
|
@ -13,3 +13,5 @@ SpaceAPI extensions
|
|||
===================
|
||||
|
||||
* Key `ext_ccc` describes Chaos Computer Club relation status (Example values: `"chaostreff"` or `"erfa"`)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ func getCalendars() {
|
|||
outputChan := parser.GetOutputChan()
|
||||
calendar := Calendar{}
|
||||
calendar.Space = spaceData.Space
|
||||
events := []Event{}
|
||||
var events []Event
|
||||
go func() {
|
||||
for event := range outputChan {
|
||||
events = append(events, mapEventObject(event))
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ func readSpaceurl() []SpaceUrl {
|
|||
return result
|
||||
}
|
||||
|
||||
func deleteSpaceurl(id string) {
|
||||
func deleteSpaceurl(url string) {
|
||||
session, err := mgo.Dial(config.MongoDbServer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
@ -108,7 +108,7 @@ func deleteSpaceurl(id string) {
|
|||
session.SetMode(mgo.Monotonic, true)
|
||||
|
||||
c := session.DB(config.MongoDbDatabase).C("spaceurl")
|
||||
c.Remove(bson.M{"_id": id})
|
||||
c.Remove(bson.M{"url": url})
|
||||
}
|
||||
|
||||
func readCalendar() []Calendar {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,10 @@ var config = ConfigFile{}
|
|||
|
||||
func main() {
|
||||
data, _ := ioutil.ReadFile("config.yaml")
|
||||
yaml.Unmarshal(data, &config)
|
||||
err := yaml.Unmarshal(data, &config)
|
||||
if err != nil {
|
||||
panic("Can't load config")
|
||||
}
|
||||
config.SharedSecret = os.Getenv("SHARED_SECRET")
|
||||
|
||||
router := NewRouter()
|
||||
|
|
@ -55,7 +58,7 @@ func SpaceUrlAdd(w http.ResponseWriter, r *http.Request) {
|
|||
func SpaceUrlUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
SharedSecret := vars["SharedSecret"]
|
||||
if(SharedSecret == config.SharedSecret) {
|
||||
if SharedSecret == config.SharedSecret {
|
||||
spaceUrl := SpaceUrl{}
|
||||
createEntry(&spaceUrl, w, r)
|
||||
updateSpaceurl(spaceUrl)
|
||||
|
|
@ -65,9 +68,9 @@ func SpaceUrlUpdate(w http.ResponseWriter, r *http.Request) {
|
|||
func SpaceUrlDelete(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
SharedSecret := vars["SharedSecret"]
|
||||
Id := vars["id"]
|
||||
Url := vars["url"]
|
||||
if SharedSecret == config.SharedSecret {
|
||||
deleteSpaceurl(Id)
|
||||
deleteSpaceurl(Url)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -77,11 +80,10 @@ func loadSpaceData() {
|
|||
timestamp := time.Now().Unix()
|
||||
|
||||
for _, spaceUrl := range spaceUrls {
|
||||
if(spaceUrl.Validated && int64(spaceUrl.LastUpdated + 60) < timestamp) {
|
||||
if spaceUrl.Validated && int64(spaceUrl.LastUpdated + 60) < timestamp {
|
||||
spaceData := SpaceData{}
|
||||
err := getJson(spaceUrl.Url, &spaceData)
|
||||
if(err != nil)
|
||||
{
|
||||
if err != nil {
|
||||
log.Println(spaceUrl.Url)
|
||||
log.Println(err)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,5 @@ type Event struct {
|
|||
Class string `json:"class"`
|
||||
Url string `json:"url"`
|
||||
Sequence int `json:"sequence"`
|
||||
WholeDayEvent bool `json:"whileDayEvent"`
|
||||
|
||||
WholeDayEvent bool `json:"wholeDayEvent"`
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ var IndexRoutes = Routes{
|
|||
Route{
|
||||
"SpaceUrlDelete",
|
||||
"DELETE",
|
||||
"/urls/{id}/{SharedSecret}",
|
||||
"/urls/{url}/{SharedSecret}",
|
||||
SpaceUrlDelete,
|
||||
},
|
||||
Route{
|
||||
|
|
|
|||
|
|
@ -1,12 +1,20 @@
|
|||
version: "3"
|
||||
services:
|
||||
frontend:
|
||||
build: ./frontend
|
||||
networks:
|
||||
spaceapi-network:
|
||||
ipv4_address: 172.16.238.10
|
||||
image: gidsi/spaceapi-ccc-frontend:latest
|
||||
restart: always
|
||||
expose:
|
||||
- "80"
|
||||
depends_on:
|
||||
- backend
|
||||
backend:
|
||||
build: ./backend
|
||||
networks:
|
||||
- spaceapi-network
|
||||
image: gidsi/spaceapi-ccc-backend:latest
|
||||
restart: always
|
||||
environment:
|
||||
|
|
@ -15,6 +23,15 @@ services:
|
|||
- database
|
||||
database:
|
||||
image: mongo:latest
|
||||
networks:
|
||||
- spaceapi-network
|
||||
restart: always
|
||||
volumes:
|
||||
- /opt/eva:/data/db
|
||||
|
||||
networks:
|
||||
spaceapi-network:
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
- subnet: 172.16.238.0/24
|
||||
|
|
@ -9,3 +9,5 @@ yarn install
|
|||
yarn start
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,10 @@ http {
|
|||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
proxy_connect_timeout 600;
|
||||
proxy_send_timeout 600;
|
||||
proxy_read_timeout 600;
|
||||
send_timeout 600;
|
||||
}
|
||||
|
||||
location /map/tiles/ {
|
||||
|
|
|
|||
|
|
@ -3,28 +3,37 @@
|
|||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"leaflet": "^1.0.3",
|
||||
"material-ui": "^0.17.4",
|
||||
"moment": "^2.18.1",
|
||||
"react": "^15.5.4",
|
||||
"react-dom": "^15.5.4",
|
||||
"react-leaflet": "^1.1.6",
|
||||
"react-redux": "^5.0.4",
|
||||
"react-router-dom": "^4.1.1",
|
||||
"react-tap-event-plugin": "^2.0.1",
|
||||
"redux": "^3.6.0",
|
||||
"redux-actions": "^2.0.2",
|
||||
"redux-thunk": "^2.2.0",
|
||||
"rrule": "^2.2.0",
|
||||
"superagent": "^3.5.2"
|
||||
"@material-ui/core": "^3.9.3",
|
||||
"@material-ui/icons": "^3.0.2",
|
||||
"leaflet": "^1.4.0",
|
||||
"moment": "^2.24.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-leaflet": "^2.2.1",
|
||||
"react-redux": "^6.0.1",
|
||||
"react-router-dom": "^5.0.0",
|
||||
"react-tap-event-plugin": "^3.0.3",
|
||||
"react-virtualized": "^9.21.0",
|
||||
"redux": "^4.0.1",
|
||||
"redux-actions": "^2.6.5",
|
||||
"redux-thunk": "^2.3.0",
|
||||
"rrule": "^2.6.0",
|
||||
"superagent": "^5.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"react-scripts": "0.9.5"
|
||||
"react-scripts": "2.1.8"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test --env=jsdom",
|
||||
"eject": "react-scripts eject"
|
||||
}
|
||||
},
|
||||
"browserslist": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not ie <= 11",
|
||||
"not op_mini all"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,8 @@ import {
|
|||
BrowserRouter as Router,
|
||||
Route
|
||||
} from 'react-router-dom';
|
||||
import injectTapEventPlugin from 'react-tap-event-plugin';
|
||||
import { Provider } from 'react-redux';
|
||||
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
|
||||
import getMuiTheme from 'material-ui/styles/getMuiTheme';
|
||||
import { MuiThemeProvider } from '@material-ui/core/styles';
|
||||
import theme from './style/theme';
|
||||
import store from './redux/store';
|
||||
import IndexContainer from './views/Index';
|
||||
|
|
@ -14,10 +12,9 @@ import SpaceList from './views/SpaceList';
|
|||
import UrlListView from './views/UrlListView';
|
||||
import layout from './layout';
|
||||
|
||||
injectTapEventPlugin();
|
||||
|
||||
const App = () => (
|
||||
<MuiThemeProvider muiTheme={getMuiTheme(theme)}>
|
||||
<MuiThemeProvider theme={theme}>
|
||||
<Provider store={store}>
|
||||
<Router>
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -1,26 +1,27 @@
|
|||
import React from 'react';
|
||||
/* import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Table, TableBody, TableRow, TableRowColumn }
|
||||
from 'material-ui/Table';
|
||||
import InfoIcon from 'material-ui/svg-icons/action/info-outline';
|
||||
// import Table from '@material-ui/core/Table';
|
||||
import TableBody from '@material-ui/core/TableBody';
|
||||
import TableRow from '@material-ui/core/TableRow';
|
||||
import TableCell from '@material-ui/core/TableCell';
|
||||
import PropTypes from 'prop-types';
|
||||
import { AutoSizer, Column, SortDirection, Table } from 'react-virtualized';
|
||||
import InfoIcon from '@material-ui/icons/InfoOutlined';
|
||||
import { actions as calendarActions, eventStruct } from '../redux/modules/calendar';
|
||||
import { spacedataStruct } from '../redux/modules/spacedata';
|
||||
import {actions as spaceDataActions, spacedataStruct} from '../redux/modules/spacedata';
|
||||
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
events: state.calendars.items,
|
||||
spacedata: state.spacedata,
|
||||
});
|
||||
|
||||
class EventList extends React.Component {
|
||||
static propTypes = {
|
||||
events: React.PropTypes.arrayOf(
|
||||
React.PropTypes.shape(eventStruct),
|
||||
events: PropTypes.arrayOf(
|
||||
PropTypes.shape(eventStruct),
|
||||
),
|
||||
fetchCalendars: React.PropTypes.func,
|
||||
fetchCalendars: PropTypes.func,
|
||||
spacedata: spacedataStruct,
|
||||
};
|
||||
|
||||
defaultProps = {
|
||||
static defaultProps = {
|
||||
events: [],
|
||||
};
|
||||
|
||||
|
|
@ -33,15 +34,8 @@ class EventList extends React.Component {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<Table
|
||||
selectable
|
||||
multiSelectable
|
||||
>
|
||||
<TableBody
|
||||
showRowHover
|
||||
stripedRows
|
||||
displayRowCheckbox={false}
|
||||
>
|
||||
<Table headerRenderer={()=>{}}>
|
||||
<TableBody>
|
||||
{this.props.events
|
||||
.filter(event =>
|
||||
(
|
||||
|
|
@ -53,23 +47,23 @@ class EventList extends React.Component {
|
|||
<TableRow
|
||||
key={event.importId + event.start.toLocaleString() + event.description}
|
||||
>
|
||||
<TableRowColumn style={{ width: '80px', padding: '5px' }}>
|
||||
<TableCell style={{ width: '80px', padding: '5px' }}>
|
||||
{this.formatDate(event.start)}
|
||||
</TableRowColumn>
|
||||
<TableRowColumn style={{ width: '55px', padding: '5px' }}>
|
||||
</TableCell>
|
||||
<TableCell style={{ width: '55px', padding: '5px' }}>
|
||||
{event.wholeDayEvent ? null : this.formatTime(event.start)}
|
||||
</TableRowColumn>
|
||||
<TableRowColumn>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{event.summary || event.description}
|
||||
</TableRowColumn>
|
||||
<TableRowColumn>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{event.space}
|
||||
</TableRowColumn>
|
||||
<TableRowColumn style={{ textAlign: 'right' }}>
|
||||
</TableCell>
|
||||
<TableCell style={{ textAlign: 'right' }}>
|
||||
{event.url && <a href={event.url}>
|
||||
<InfoIcon style={{ cursor: 'pointer' }} />
|
||||
</a>}
|
||||
</TableRowColumn>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
|
|
@ -78,6 +72,223 @@ class EventList extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, {
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(EventList);
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import TableCell from '@material-ui/core/TableCell';
|
||||
import PropTypes from 'prop-types';
|
||||
import InfoIcon from '@material-ui/icons/InfoOutlined';
|
||||
import { actions as calendarActions, eventStruct } from '../redux/modules/calendar';
|
||||
import {actions as spaceDataActions, spacedataStruct} from '../redux/modules/spacedata';
|
||||
import classNames from 'classnames';
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import { AutoSizer, Column, Table } from 'react-virtualized';
|
||||
|
||||
const styles = theme => ({
|
||||
table: {
|
||||
fontFamily: theme.typography.fontFamily,
|
||||
},
|
||||
flexContainer: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
boxSizing: 'border-box',
|
||||
},
|
||||
tableRow: {
|
||||
border: 0,
|
||||
},
|
||||
tableRowHover: {
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.grey[600],
|
||||
},
|
||||
},
|
||||
tableCell: {
|
||||
flex: 1,
|
||||
color: '#fff',
|
||||
},
|
||||
noClick: {
|
||||
cursor: 'initial',
|
||||
},
|
||||
});
|
||||
|
||||
class MuiVirtualizedTable extends React.PureComponent {
|
||||
getRowClassName = ({ index }) => {
|
||||
const { classes, rowClassName, onRowClick } = this.props;
|
||||
|
||||
return classNames(classes.tableRow, classes.flexContainer, rowClassName, {
|
||||
[classes.tableRowHover]: index !== -1 && onRowClick != null,
|
||||
});
|
||||
};
|
||||
|
||||
cellRenderer = ({ cellData, columnIndex = null }) => {
|
||||
const { columns, classes, rowHeight, onRowClick } = this.props;
|
||||
return (
|
||||
<TableCell
|
||||
component="div"
|
||||
className={classNames(classes.tableCell, classes.flexContainer, {
|
||||
[classes.noClick]: onRowClick == null,
|
||||
})}
|
||||
variant="body"
|
||||
style={{ height: rowHeight }}
|
||||
align={(columnIndex != null && columns[columnIndex].numeric) || false ? 'right' : 'left'}
|
||||
>
|
||||
{cellData}
|
||||
</TableCell>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { classes, columns, ...tableProps } = this.props;
|
||||
return (
|
||||
<AutoSizer>
|
||||
{({ height, width }) => (
|
||||
<Table
|
||||
className={classes.table}
|
||||
height={height}
|
||||
width={width}
|
||||
{...tableProps}
|
||||
rowClassName={this.getRowClassName}
|
||||
>
|
||||
{columns.map(({ cellContentRenderer = null, className, dataKey, ...other }, index) => {
|
||||
let renderer;
|
||||
if (cellContentRenderer != null) {
|
||||
renderer = cellRendererProps =>
|
||||
this.cellRenderer({
|
||||
cellData: cellContentRenderer(cellRendererProps),
|
||||
columnIndex: index,
|
||||
});
|
||||
} else {
|
||||
renderer = this.cellRenderer;
|
||||
}
|
||||
|
||||
return (
|
||||
<Column
|
||||
key={dataKey}
|
||||
headerRenderer={() => {}}
|
||||
className={classNames(classes.flexContainer, className)}
|
||||
cellRenderer={renderer}
|
||||
dataKey={dataKey}
|
||||
{...other}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Table>
|
||||
)}
|
||||
</AutoSizer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
MuiVirtualizedTable.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
columns: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
cellContentRenderer: PropTypes.func,
|
||||
dataKey: PropTypes.string.isRequired,
|
||||
width: PropTypes.number.isRequired,
|
||||
}),
|
||||
).isRequired,
|
||||
headerHeight: PropTypes.number,
|
||||
onRowClick: PropTypes.func,
|
||||
rowClassName: PropTypes.string,
|
||||
rowHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),
|
||||
sort: PropTypes.func,
|
||||
};
|
||||
|
||||
MuiVirtualizedTable.defaultProps = {
|
||||
rowHeight: 40,
|
||||
headerHeight: 0,
|
||||
};
|
||||
|
||||
const WrappedVirtualizedTable = withStyles(styles)(MuiVirtualizedTable);
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
events: state.calendars.items,
|
||||
spacedata: state.spacedata,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
...calendarActions,
|
||||
})(EventList);
|
||||
...spaceDataActions,
|
||||
};
|
||||
|
||||
|
||||
class EventList extends React.Component {
|
||||
static propTypes = {
|
||||
events: PropTypes.arrayOf(
|
||||
PropTypes.shape(eventStruct),
|
||||
),
|
||||
fetchCalendars: PropTypes.func,
|
||||
spacedata: spacedataStruct,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
events: [],
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
this.props.fetchCalendars();
|
||||
}
|
||||
|
||||
formatDate = date => (date.format('DD.MM.YYYY'));
|
||||
formatTime = date => (date.format('HH:mm'));
|
||||
|
||||
render() {
|
||||
|
||||
const rows = this.props.events.filter(event =>
|
||||
(
|
||||
this.props.spacedata.filter.indexOf(event.space) !== -1
|
||||
|| this.props.spacedata.filter.length === 0
|
||||
)
|
||||
).map(event => {
|
||||
return {
|
||||
...event,
|
||||
start_date: this.formatDate(event.start),
|
||||
start_time: this.formatTime(event.start),
|
||||
summary: event.summary || event.description,
|
||||
link: event.url && <a href={event.url}>
|
||||
<InfoIcon style={{ cursor: 'pointer', color: '#fff' }} />
|
||||
</a>,
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<WrappedVirtualizedTable
|
||||
rowCount={rows.length}
|
||||
rowGetter={({ index }) => rows[index]}
|
||||
columns={[
|
||||
{
|
||||
width: 120,
|
||||
label: 'Date',
|
||||
dataKey: 'start_date',
|
||||
},
|
||||
{
|
||||
width: 120,
|
||||
label: 'Time',
|
||||
dataKey: 'start_time',
|
||||
},
|
||||
{
|
||||
width: 120,
|
||||
flexGrow: 1,
|
||||
label: 'Summary',
|
||||
dataKey: 'summary',
|
||||
},
|
||||
{
|
||||
width: 120,
|
||||
flexGrow: 1,
|
||||
label: 'Carbs (g)',
|
||||
dataKey: 'space',
|
||||
},
|
||||
{
|
||||
width: 120,
|
||||
label: 'Protein (g)',
|
||||
dataKey: 'link',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(EventList);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Map as LeafletMap, TileLayer } from 'react-leaflet';
|
||||
import { connect } from 'react-redux';
|
||||
import Marker from './Marker';
|
||||
|
|
@ -15,8 +16,8 @@ const mapDispatchToProps = {
|
|||
class Map extends React.Component {
|
||||
static propTypes = {
|
||||
spacedata: spacedataStruct.isRequired,
|
||||
fetchSpacedata: React.PropTypes.func.isRequired,
|
||||
toggleFilterSpacedata: React.PropTypes.func.isRequired,
|
||||
fetchSpacedata: PropTypes.func.isRequired,
|
||||
toggleFilterSpacedata: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
|
|
@ -29,7 +30,7 @@ class Map extends React.Component {
|
|||
<LeafletMap
|
||||
center={centerGermany}
|
||||
zoom={5}
|
||||
style={{ width: '100vw', height: '50vh', margin: 0, padding: 0, maxWidth: '100%' }}
|
||||
style={{ width: '100vw', height: 'calc(50vh - 60px)', margin: 0, padding: 0, maxWidth: '100%' }}
|
||||
>
|
||||
<TileLayer
|
||||
url="https://spaceapi.ccc.de/map/tiles/{z}/{x}/{y}.png"
|
||||
|
|
@ -40,8 +41,7 @@ class Map extends React.Component {
|
|||
spacedata={spacedata}
|
||||
key={spacedata.space}
|
||||
highlight={
|
||||
this.props.spacedata.filter.length === 0
|
||||
|| this.props.spacedata.filter.indexOf(spacedata.space) !== -1}
|
||||
this.props.spacedata.filter.indexOf(spacedata.space) !== -1}
|
||||
toggleFilterSpacedata={this.props.toggleFilterSpacedata}
|
||||
/>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { CircleMarker, Popup } from 'react-leaflet';
|
||||
import theme from '../style/theme';
|
||||
import { spacedataElementStruct } from '../redux/modules/spacedata';
|
||||
|
||||
const Marker = (props) => {
|
||||
const color = props.highlight ? theme.palette.accent2Color : theme.palette.primary1Color;
|
||||
const color = props.highlight ? theme.palette.secondary.light : theme.palette.primary.main;
|
||||
|
||||
const style = {
|
||||
container: {
|
||||
|
|
@ -23,7 +24,10 @@ const Marker = (props) => {
|
|||
radius={5}
|
||||
center={[props.spacedata.location.lat, props.spacedata.location.lon]}
|
||||
>
|
||||
<Popup>
|
||||
<Popup
|
||||
onOpen={() => props.toggleFilterSpacedata(props.spacedata.space)}
|
||||
onClose={() => props.toggleFilterSpacedata(props.spacedata.space)}
|
||||
>
|
||||
<div style={style.container}>
|
||||
<div>
|
||||
{props.spacedata.space}
|
||||
|
|
@ -40,7 +44,8 @@ const Marker = (props) => {
|
|||
|
||||
Marker.propTypes = {
|
||||
spacedata: spacedataElementStruct.isRequired,
|
||||
highlight: React.PropTypes.bool.isRequired,
|
||||
highlight: PropTypes.bool.isRequired,
|
||||
toggleFilterSpacedata: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default Marker;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import React from 'react';
|
||||
import request from 'superagent';
|
||||
import TextField from 'material-ui/TextField';
|
||||
import FloatingActionButton from 'material-ui/FloatingActionButton';
|
||||
import ContentAdd from 'material-ui/svg-icons/content/add';
|
||||
import Snackbar from 'material-ui/Snackbar';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import FloatingActionButton from '@material-ui/core/Fab';
|
||||
import ContentAdd from '@material-ui/icons/AddOutlined';
|
||||
import Snackbar from '@material-ui/core/Snackbar';
|
||||
import PropTypes from 'prop-types';
|
||||
import config from '../api/config';
|
||||
|
||||
class SpaceApiInput extends React.Component {
|
||||
static propTypes = {
|
||||
style: React.PropTypes.shape({}),
|
||||
style: PropTypes.shape({}),
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableRow,
|
||||
TableRowColumn,
|
||||
} from 'material-ui/Table';
|
||||
import Table from '@material-ui/core/Table';
|
||||
import TableBody from '@material-ui/core/TableBody';
|
||||
import TableRow from '@material-ui/core/TableRow';
|
||||
import TableCell from '@material-ui/core/TableCell';
|
||||
import PropTypes from 'prop-types';
|
||||
import { spacedataStruct } from '../redux/modules/spacedata';
|
||||
|
||||
export class SpaceList extends React.Component {
|
||||
static propTypes = {
|
||||
fetchSpacedata: React.PropTypes.func.isRequired,
|
||||
fetchSpacedata: PropTypes.func.isRequired,
|
||||
spacedata: spacedataStruct,
|
||||
};
|
||||
|
||||
|
|
@ -28,34 +27,29 @@ export class SpaceList extends React.Component {
|
|||
(a, b) => a.space.toUpperCase().localeCompare(b.space.toUpperCase())
|
||||
);
|
||||
return (
|
||||
<Table
|
||||
selectable
|
||||
multiSelectable
|
||||
>
|
||||
<TableBody
|
||||
showRowHover
|
||||
stripedRows
|
||||
displayRowCheckbox={false}
|
||||
>
|
||||
<div style={{ paddingTop:'60px' }}>
|
||||
<Table>
|
||||
<TableBody>
|
||||
{items
|
||||
.map(space => (
|
||||
<TableRow key={space.space}>
|
||||
<TableRowColumn>
|
||||
<TableCell style={{ color: '#fff' }}>
|
||||
{space.space}
|
||||
</TableRowColumn>
|
||||
<TableRowColumn>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<a
|
||||
href={space.url}
|
||||
style={{ textDecoration: 'none', color: 'white' }}
|
||||
>
|
||||
{space.url}
|
||||
</a>
|
||||
</TableRowColumn>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,43 +1,93 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
Toolbar as MuiToolbar,
|
||||
ToolbarGroup,
|
||||
ToolbarTitle,
|
||||
} from 'material-ui/Toolbar';
|
||||
import IconMenu from 'material-ui/IconMenu';
|
||||
import IconButton from 'material-ui/IconButton';
|
||||
import NavigationExpandMoreIcon from 'material-ui/svg-icons/navigation/expand-more';
|
||||
import MenuItem from 'material-ui/MenuItem';
|
||||
import Toolbar from '@material-ui/core/Toolbar';
|
||||
import AppBar from '@material-ui/core/AppBar';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import ClickAwayListener from '@material-ui/core/ClickAwayListener';
|
||||
import Grow from '@material-ui/core/Grow';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import Popper from '@material-ui/core/Popper';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
import MenuList from '@material-ui/core/MenuList';
|
||||
import MenuIcon from '@material-ui/icons/Menu';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const Toolbar = () => (
|
||||
<MuiToolbar>
|
||||
<ToolbarTitle
|
||||
text={'CCC Spaces'}
|
||||
/>
|
||||
<ToolbarGroup>
|
||||
<IconMenu
|
||||
iconButtonElement={
|
||||
<IconButton touch>
|
||||
<NavigationExpandMoreIcon />
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
<MenuItem
|
||||
primaryText={'Events'}
|
||||
containerElement={<Link to="/" />}
|
||||
/>
|
||||
<MenuItem
|
||||
primaryText={'Spaces'}
|
||||
containerElement={<Link to="/list" />}
|
||||
/>
|
||||
<MenuItem
|
||||
primaryText={'Impressum'}
|
||||
href={'http://ccc.de/de/imprint'}
|
||||
/>
|
||||
</IconMenu>
|
||||
</ToolbarGroup>
|
||||
</MuiToolbar>
|
||||
);
|
||||
|
||||
export default Toolbar;
|
||||
class MyToolbar extends React.Component {
|
||||
state = {
|
||||
open: false,
|
||||
};
|
||||
|
||||
handleToggle = () => {
|
||||
this.setState(state => ({ open: !state.open }));
|
||||
};
|
||||
|
||||
handleClose = event => {
|
||||
if (this.anchorEl.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ open: false });
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<AppBar position="fixed">
|
||||
<Toolbar>
|
||||
<div style={{ width: "100%", display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
||||
<Typography variant="h6" color="inherit">
|
||||
CCC Spaces
|
||||
</Typography>
|
||||
<div>
|
||||
<IconButton
|
||||
buttonRef={node => {
|
||||
this.anchorEl = node;
|
||||
}}
|
||||
aria-owns={this.state.open ? 'menu-list-grow' : undefined}
|
||||
aria-haspopup="true"
|
||||
onClick={this.handleToggle}
|
||||
>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
<Popper open={this.state.open} anchorEl={this.anchorEl} transition disablePortal>
|
||||
{({ TransitionProps, placement }) => (
|
||||
<Grow
|
||||
{...TransitionProps}
|
||||
id="menu-list-grow"
|
||||
style={{ transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom' }}
|
||||
>
|
||||
<Paper>
|
||||
<ClickAwayListener onClickAway={this.handleClose}>
|
||||
<MenuList>
|
||||
<MenuItem
|
||||
onClick={() => window.location.href = '/'}
|
||||
containerElement={<Link to="/" />}
|
||||
>
|
||||
Events
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => window.location.href = '/list'}
|
||||
containerElement={<Link to="/list" />}
|
||||
>
|
||||
Spaces
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => window.location.href = 'http://ccc.de/de/imprint'}
|
||||
>
|
||||
Impressum
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Grow>
|
||||
)}
|
||||
</Popper>
|
||||
</div>
|
||||
</div>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MyToolbar;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableRow,
|
||||
TableRowColumn,
|
||||
} from 'material-ui/Table';
|
||||
import TextField from 'material-ui/TextField';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
import Table from '@material-ui/core/Table';
|
||||
import TableBody from '@material-ui/core/TableBody';
|
||||
import TableRow from '@material-ui/core/TableRow';
|
||||
import TableCell from '@material-ui/core/TableCell';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import PropTypes from 'prop-types';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import moment from 'moment';
|
||||
import { spaceUrlStruct } from '../redux/modules/spaceurl';
|
||||
|
||||
export class UrlList extends React.Component {
|
||||
static propTypes = {
|
||||
fetchSpaceUrl: React.PropTypes.func.isRequired,
|
||||
validateSpaceUrl: React.PropTypes.func.isRequired,
|
||||
fetchSpaceUrl: PropTypes.func.isRequired,
|
||||
validateSpaceUrl: PropTypes.func.isRequired,
|
||||
deleteSpaceUrl: PropTypes.func.isRequired,
|
||||
spaceurls: spaceUrlStruct,
|
||||
};
|
||||
|
||||
|
|
@ -38,42 +38,46 @@ export class UrlList extends React.Component {
|
|||
url: spaceUrl.url,
|
||||
validated: true,
|
||||
};
|
||||
this.props.validateSpaceUrl(validatedSpaceUrl, this.secretInput.input.value);
|
||||
this.props.validateSpaceUrl(validatedSpaceUrl, this.state.secret);
|
||||
};
|
||||
|
||||
deleteSpaceUrl = (spaceUrl) => {
|
||||
this.props.deleteSpaceUrl(spaceUrl.url, this.state.secret);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Table
|
||||
selectable
|
||||
multiSelectable
|
||||
>
|
||||
<TableBody
|
||||
showRowHover
|
||||
stripedRows
|
||||
displayRowCheckbox={false}
|
||||
>
|
||||
<Table>
|
||||
<TableBody>
|
||||
{this.props.spaceurls.items
|
||||
.map(spaceurl => (
|
||||
<TableRow key={spaceurl.url}>
|
||||
<TableRowColumn>
|
||||
<TableCell>
|
||||
<a
|
||||
href={spaceurl.url}
|
||||
style={{ color: 'white', textDecoration: 'none' }}
|
||||
>
|
||||
{spaceurl.url}
|
||||
</a>
|
||||
</TableRowColumn>
|
||||
<TableRowColumn>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{this.getFormatedDateTime(spaceurl.lastUpdated)}
|
||||
</TableRowColumn>
|
||||
<TableRowColumn>
|
||||
{!spaceurl.validated ? <FlatButton
|
||||
label={'validated'}
|
||||
onTouchTap={() => this.validateSpaceUrl(spaceurl)}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{!spaceurl.validated ? <Button
|
||||
onClick={() => this.validateSpaceUrl(spaceurl)}
|
||||
primary
|
||||
/> : null}
|
||||
</TableRowColumn>
|
||||
>validated</Button> : null}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Button
|
||||
onClick={() => this.deleteSpaceUrl(spaceurl)}
|
||||
primary
|
||||
>
|
||||
delete
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
|
|
@ -81,6 +85,7 @@ export class UrlList extends React.Component {
|
|||
</Table>
|
||||
<TextField
|
||||
name={'secret-input'}
|
||||
onChange={(event)=> this.setState({ secret: event.target.value })}
|
||||
ref={ref => (this.secretInput = ref)}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { PropTypes } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import request from 'superagent';
|
||||
import flatten from 'lodash/flatten';
|
||||
import moment from 'moment';
|
||||
|
|
@ -35,6 +35,9 @@ export const actions = {
|
|||
fetchCalendars,
|
||||
};
|
||||
|
||||
const formatDate = date => (date.format('DD.MM.YYYY'));
|
||||
const formatTime = date => (date.format('HH:mm'));
|
||||
|
||||
export default handleActions({
|
||||
[CALENDARS_FETCHED]: (state, { payload }) => {
|
||||
const items = flatten(flatten(
|
||||
|
|
@ -60,7 +63,7 @@ export default handleActions({
|
|||
));
|
||||
}
|
||||
catch (ex) {
|
||||
console.log(ex);
|
||||
console.log(ex, event);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { PropTypes } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import request from 'superagent';
|
||||
import { createAction, handleActions } from 'redux-actions';
|
||||
import config from '../../api/config';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { PropTypes } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import request from 'superagent';
|
||||
import { createAction, handleActions } from 'redux-actions';
|
||||
import config from '../../api/config';
|
||||
|
|
@ -14,9 +14,11 @@ export const spaceUrlStruct = PropTypes.shape({
|
|||
|
||||
const SPACEURL_FETCHED = 'SPACEURL_FETCHED';
|
||||
const SPACEURL_VALIDATE = 'SPACEURL_VALIDATE';
|
||||
const SPACEURL_DELETE = 'SPACEURL_DELETE';
|
||||
|
||||
export const fetched = createAction(SPACEURL_FETCHED, result => result);
|
||||
export const validate = createAction(SPACEURL_VALIDATE, result => result);
|
||||
export const deleteSpace = createAction(SPACEURL_DELETE, result => result);
|
||||
|
||||
export const fetchSpaceUrl = () => (dispatch) => {
|
||||
request
|
||||
|
|
@ -45,9 +47,23 @@ export const validateSpaceUrl = (spaceUrl, secret) => (dispatch) => {
|
|||
);
|
||||
};
|
||||
|
||||
export const deleteSpaceUrl = (spaceUrl, secret) => (dispatch) => {
|
||||
request
|
||||
.delete(`${config.api.url}/urls/spaceUrl/${secret}`)
|
||||
.set('Content-Type', 'application/json')
|
||||
.end(
|
||||
(err) => {
|
||||
if (!err) {
|
||||
dispatch(deleteSpace(spaceUrl));
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
fetchSpaceUrl,
|
||||
validateSpaceUrl,
|
||||
deleteSpaceUrl,
|
||||
};
|
||||
|
||||
export default handleActions({
|
||||
|
|
@ -67,4 +83,5 @@ export default handleActions({
|
|||
|
||||
return newState;
|
||||
},
|
||||
[SPACEURL_DELETE]: (state, { payload }) => state.items.filter(ele => ele.url != payload.url),
|
||||
}, { items: [] });
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
import { fade } from 'material-ui/utils/colorManipulator';
|
||||
import { spacing, colors } from 'material-ui/styles';
|
||||
import grey from '@material-ui/core/colors/grey';
|
||||
import green from '@material-ui/core/colors/green';
|
||||
import red from '@material-ui/core/colors/red';
|
||||
import { createMuiTheme } from '@material-ui/core/styles';
|
||||
|
||||
export default {
|
||||
spacing,
|
||||
export default createMuiTheme({
|
||||
typography: {
|
||||
fontFamily: 'Roboto, sans-serif',
|
||||
palette: {
|
||||
primary1Color: colors.grey900,
|
||||
primary2Color: colors.grey900,
|
||||
primary3Color: colors.grey600,
|
||||
accent1Color: colors.grey500,
|
||||
accent2Color: colors.grey500,
|
||||
accent3Color: colors.grey100,
|
||||
textColor: colors.fullWhite,
|
||||
secondaryTextColor: fade(colors.fullWhite, 0.7),
|
||||
alternateTextColor: '#303030',
|
||||
canvasColor: '#303030',
|
||||
borderColor: fade(colors.fullWhite, 0.3),
|
||||
disabledColor: fade(colors.fullWhite, 0.3),
|
||||
pickerHeaderColor: fade(colors.fullWhite, 0.12),
|
||||
clockCircleColor: fade(colors.fullWhite, 0.12),
|
||||
useNextVariants: true,
|
||||
},
|
||||
};
|
||||
palette: {
|
||||
primary: grey,
|
||||
secondary: green,
|
||||
error: red,
|
||||
// Used by `getContrastText()` to maximize the contrast between the background and
|
||||
// the text.
|
||||
contrastThreshold: 3,
|
||||
// Used to shift a color's luminance by approximately
|
||||
// two indexes within its tonal palette.
|
||||
// E.g., shift from Red 500 to Red 300 or Red 700.
|
||||
tonalOffset: 0.2,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,14 +6,14 @@ const style = {
|
|||
container: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
};
|
||||
|
||||
const IndexContainer = () => (
|
||||
<div style={style.container}>
|
||||
<div style={{ height: 'calc(50vh - 60px)', paddingTop: '60px'}}>
|
||||
<Map />
|
||||
</div>
|
||||
<EventList />
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue