diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5019475 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.idea +/dist +/node_modules diff --git a/index.html b/index.html new file mode 100644 index 0000000..2624095 --- /dev/null +++ b/index.html @@ -0,0 +1,18 @@ + + + + + MyEvents + + + + +
+ + + + + + + + \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..457e642 --- /dev/null +++ b/package.json @@ -0,0 +1,36 @@ +{ + "name": "myevents-ui", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@types/react": "^15.0.16", + "@types/react-dom": "^0.14.23", + "@types/react-router-dom": "^4.0.1", + "@types/whatwg-fetch": "0.0.33", + "bootstrap": "^3.3.7", + "promise-polyfill": "^6.0.2", + "react": "^15.4.2", + "react-dom": "^15.4.2", + "react-router-dom": "^4.0.0", + "whatwg-fetch": "^2.0.3" + }, + "devDependencies": { + "awesome-typescript-loader": "^3.1.2", + "babel-core": "^6.24.0", + "babel-loader": "^6.4.1", + "babel-preset-es2015": "^6.24.0", + "css-loader": "^0.27.3", + "file-loader": "^0.10.1", + "source-map-loader": "^0.2.0", + "style-loader": "^0.16.0", + "ts-loader": "^2.0.3", + "typescript": "^2.2.1", + "url-loader": "^0.5.8" + } +} diff --git a/src/components/event_booking_form.tsx b/src/components/event_booking_form.tsx new file mode 100644 index 0000000..f1cbe2a --- /dev/null +++ b/src/components/event_booking_form.tsx @@ -0,0 +1,55 @@ +import * as React from "react"; +import {Event} from "../model/event"; +import {FormRow} from "./form_row"; + +export interface EventBookingFormProps { + event: Event; + onSubmit: (seats: number) => any +} + +export interface EventBookingFormState { + selectedAmount: number; +} + +export class EventBookingForm extends React.Component { + constructor(p: EventBookingFormProps) { + super(p); + + this.state = { + selectedAmount: 1 + } + } + + private handleNewAmount(n: number) { + const newState: EventBookingFormState = { + selectedAmount: n + }; + + this.setState(newState); + } + + render() { + return
+

Book tickets for {this.props.event.Name}!

+
+ +

{this.props.event.Name}

+
+ + + + +

{this.state.selectedAmount * 99} €

+
+ + + +
+
+ } +} \ No newline at end of file diff --git a/src/components/event_booking_form_container.tsx b/src/components/event_booking_form_container.tsx new file mode 100644 index 0000000..3a4e201 --- /dev/null +++ b/src/components/event_booking_form_container.tsx @@ -0,0 +1,49 @@ +import * as React from "react"; +import {EventBookingForm} from "./event_booking_form"; +import {Event} from "../model/event"; + +export interface EventBookingFormContainerProps { + eventID: string; + eventServiceURL: string; + bookingServiceURL: string; +} + +export interface EventBookingFormState { + loading: boolean; + event?: Event; +} + +export class EventBookingFormContainer extends React.Component { + constructor(p: EventBookingFormContainerProps) { + super(p); + + this.state = { + loading: true + }; + + fetch(p.eventServiceURL + "/events/" + p.eventID) + .then(resp => resp.json()) + .then(event => { + this.setState({ + loading: false, + event: event + }); + }) + } + + render() { + if (this.state.loading) { + return
Loading...
; + } + + if (!this.state.event) { + return
Unknown error
; + } + + return this.handleSubmit(amount)}/> + } + + private handleSubmit(amount: number) { + + } +} \ No newline at end of file diff --git a/src/components/event_list.tsx b/src/components/event_list.tsx new file mode 100644 index 0000000..463c7e5 --- /dev/null +++ b/src/components/event_list.tsx @@ -0,0 +1,30 @@ +import * as React from "react"; +import {EventListItem} from "./event_list_item"; +import {Event} from "../model/event"; + +export interface EventListProps { + events: Event[] + onEventBooked: (e: Event) => any +} + +export class EventList extends React.Component { + public render() { + const items = this.props.events.map(event => + this.props.onEventBooked(event)}/> + ); + + return + + + + + + + + + + {items} + +
NameWhereWhen (start/end)Actions
; + } +} \ No newline at end of file diff --git a/src/components/event_list_container.tsx b/src/components/event_list_container.tsx new file mode 100644 index 0000000..ad6a109 --- /dev/null +++ b/src/components/event_list_container.tsx @@ -0,0 +1,43 @@ +import * as React from "react"; +import {EventList} from "./event_list"; +import {Loader} from "./loader"; +import {Event} from "../model/event"; + +export interface EventListContainerProps { + eventServiceURL: string; +} + +export interface EventListContainerState { + loading: boolean; + events: Event[]; +} + +export class EventListContainer extends React.Component { + constructor(p: EventListContainerProps) { + super(p); + + this.state = { + loading: true, + events: [] + }; + + fetch(p.eventServiceURL + "/events", {method: "GET"}) + .then(response => response.json()) + .then(events => { + this.setState({ + loading: false, + events: events + }) + }) + } + + private handleEventBooked(e: Event) { + console.log("booking event..."); + } + + render() { + return + this.handleEventBooked(e)}/> + + } +} \ No newline at end of file diff --git a/src/components/event_list_item.tsx b/src/components/event_list_item.tsx new file mode 100644 index 0000000..9d303e1 --- /dev/null +++ b/src/components/event_list_item.tsx @@ -0,0 +1,29 @@ +import * as React from "react"; +import {Link} from "react-router-dom"; +import {Event} from "../model/event"; + +export interface EventListItemProps { + event: Event; + selected?: boolean; + + onBooked: () => any; +} + +export class EventListItem extends React.Component { + render() { + const start = new Date(this.props.event.StartDate * 1000); + const end = new Date(this.props.event.EndDate * 1000); + + const locationName = this.props.event.Location ? this.props.event.Location.Name : "unknown"; + + console.log(this.props.event); + + return + {this.props.event.Name} + {locationName} + {start.toLocaleDateString()} + {end.toLocaleDateString()} + Book now! + + } +} \ No newline at end of file diff --git a/src/components/form_row.tsx b/src/components/form_row.tsx new file mode 100644 index 0000000..4ef7758 --- /dev/null +++ b/src/components/form_row.tsx @@ -0,0 +1,19 @@ +import * as React from "react"; + +export interface FormRowProps { + label?: string; +} + +export class FormRow extends React.Component { + render() { + const label = this.props.label ? : undefined; + const cls = "col-sm-10" + (this.props.label ? "" : " col-sm-offset-2"); + + return
+ {label} +
+ {this.props.children} +
+
+ } +} \ No newline at end of file diff --git a/src/components/loader.tsx b/src/components/loader.tsx new file mode 100644 index 0000000..db4796b --- /dev/null +++ b/src/components/loader.tsx @@ -0,0 +1,20 @@ +import * as React from "react"; + +export interface LoaderProps { + loading: boolean; + message?: string; +} + +export class Loader extends React.Component { + render() { + const msg = this.props.message || "Loading. Please wait..."; + + if (this.props.loading) { + return
{msg}
+ } + + return
+ {this.props.children} +
; + } +} \ No newline at end of file diff --git a/src/components/navigation.tsx b/src/components/navigation.tsx new file mode 100644 index 0000000..793dbf9 --- /dev/null +++ b/src/components/navigation.tsx @@ -0,0 +1,30 @@ +import * as React from "react"; +import {Link} from "react-router-dom"; + +export interface NavigationProps { + brandName: string; +} + +export class Navigation extends React.Component { + render() { + return + } +} \ No newline at end of file diff --git a/src/index.tsx b/src/index.tsx new file mode 100644 index 0000000..7d4cc3a --- /dev/null +++ b/src/index.tsx @@ -0,0 +1,36 @@ +import * as React from "react"; +import * as ReactDOM from "react-dom"; +import {HashRouter as Router, Route} from "react-router-dom"; +import {EventListContainer} from "./components/event_list_container"; +import {Navigation} from "./components/navigation"; +import {EventBookingFormContainer} from "./components/event_booking_form_container"; + +declare function require(n: string): any; + +const bs = require("bootstrap/dist/css/bootstrap.css"); + +class App extends React.Component<{}, {}> { + render() { + const eventList = () => ; + const eventBooking = ({match}:any) => ; + + return +
+ +
+

My Events

+ + + +
+
+
+ } +} + +ReactDOM.render( + , + document.getElementById("myevents-app") +); \ No newline at end of file diff --git a/src/model/event.ts b/src/model/event.ts new file mode 100644 index 0000000..c076eba --- /dev/null +++ b/src/model/event.ts @@ -0,0 +1,11 @@ + +export interface Event { + ID: string; + Name: string; + StartDate: number; + EndDate: number; + Location: { + ID: string; + Name: string; + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..002988d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "outDir": "./dist", + "module": "commonjs", + "target": "es5", + "sourceMap": true, + "noImplicitAny": true, + "jsx": "react" + }, + "include": [ + "./src/**/*" + ] +} diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..56b3f6a --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,68 @@ +module.exports = { + entry: "./src/index.tsx", + output: { + filename: "bundle.js", + path: __dirname + "/dist" + }, + + devtool: "source-map", + + resolve: { + extensions: [".ts", ".tsx", ".css", ".js"] + }, + + module: { + /*loaders: [ + { + test: /\.tsc?$/, + loader: "awesome-typescript-loader" + } + ],*/ + + rules: [ + { + test: /\.tsx?$/, + //loader: "awesome-typescript-loader" + loader: "ts-loader" + }, + { + test: /.jsx?$/, + loader: 'babel-loader', + query: { + //presets: [['es2015', {}]] + } + }, + { + test: /\.css$/, + loader: "style-loader!css-loader" + }, + { + test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, + loader: 'url-loader?limit=10000&mimetype=application/font-woff' + }, + { + test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, + loader: 'url-loader?limit=10000&mimetype=application/octet-stream' + }, + { + test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, + loader: 'file-loader' + }, + { + test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, + loader: 'url-loader?limit=10000&mimetype=image/svg+xml' + } + /*, + { + test: /\.js$/, + enforce: "pre", + loader: "source-map-loader" + }*/ + ] + }, + + externals: { + "react": "React", + "react-dom": "ReactDOM" + } +}; \ No newline at end of file