Remove client code
This commit is contained in:
parent
2e61bdf813
commit
335a7b21fc
|
@ -1,3 +1,4 @@
|
|||
node_modules
|
||||
client/node_modules
|
||||
server/node_modules
|
||||
tests
|
||||
k8s
|
||||
@types
|
||||
|
|
|
@ -14,4 +14,3 @@
|
|||
.gitignore
|
||||
|
||||
node_modules
|
||||
#!include:.gitignore
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
dist/
|
||||
yarn-error.log
|
||||
config.ts
|
||||
node_modules
|
||||
.idea
|
||||
yarn-error.log
|
||||
.DS_Store
|
||||
lib
|
||||
.env
|
||||
lib/
|
||||
|
|
13
Makefile
13
Makefile
|
@ -1,13 +0,0 @@
|
|||
configure-functions:
|
||||
@cat server/src/config.tmpl.ts \
|
||||
| sed 's&_EMAIL_DOMAIN_&'"${EMAIL_DOMAIN}"'&' \
|
||||
| sed 's&_DOMAIN_&'"${DOMAIN}"'&' \
|
||||
| sed 's&_MAILGUN_API_KEY_&'"${MAILGUN_API_KEY}"'&' \
|
||||
| sed 's&_GCP_PROJECT_ID_&'"${GCP_PROJECT_ID}"'&' > server/src/config.ts
|
||||
configure-functions-staging:
|
||||
@echo "Will populate config.ts with runtime secrets"
|
||||
@$(MAKE) DOMAIN=${DOMAIN_STAGING} \
|
||||
MAILGUN_API_KEY=${MAILGUN_API_KEY_STAGING} \
|
||||
GCP_PROJECT_ID=${GCP_PROJECT_ID_STAGING} \
|
||||
EMAIL_DOMAIN=${EMAIL_DOMAIN_STAGING} \
|
||||
configure-functions
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"presets": [
|
||||
"@babel/env",
|
||||
"@babel/preset-typescript",
|
||||
"@emotion/babel-preset-css-prop"
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/proposal-class-properties",
|
||||
"@babel/proposal-object-rest-spread"
|
||||
]
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
import * as React from "react"
|
||||
import { Redirect, Route, Switch } from "react-router-dom"
|
||||
import Calendar from "../Calendar/Calendar"
|
||||
import CreateEvent from "../Event/CreateEvent"
|
||||
import EditEvent from "../Event/EditEvent"
|
||||
import { RouteComponentProps } from "react-router"
|
||||
import Header from "./Header/Header"
|
||||
import Login from "./Auth/Login"
|
||||
import styled from "@emotion/styled"
|
||||
import Footer from "./Footer"
|
||||
import InitializeSession from "./Auth/InitializeSession"
|
||||
import Signup from "./Auth/Signup"
|
||||
import Signout from "./Auth/Signout"
|
||||
import OccurrenceDetails from "./Occurrence/OccurrenceDetails"
|
||||
import { Global, css } from "@emotion/core"
|
||||
|
||||
const App = () => (
|
||||
<Root>
|
||||
<Global
|
||||
styles={css`
|
||||
body {
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
}
|
||||
#app {
|
||||
height: 100%;
|
||||
}
|
||||
`}
|
||||
/>
|
||||
<StyledHeader />
|
||||
<Content>
|
||||
<Switch>
|
||||
<Route path="/create" component={CreateEvent} />
|
||||
<Route
|
||||
path="/event/:eventId/edit"
|
||||
render={(routeProps: RouteComponentProps<{ eventId: string }>) => (
|
||||
<EditEvent eventId={routeProps.match.params.eventId} />
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path="/o/:sanitizedEventName/:occurrenceId"
|
||||
component={OccurrenceDetails}
|
||||
/>
|
||||
<Route path="/init-session/:hash" component={InitializeSession} />
|
||||
<Route path="/login" component={Login} />
|
||||
<Route path="/signup" component={Signup} />
|
||||
<Route path="/logout" component={Signout} />
|
||||
<Route path="/" component={Calendar} />
|
||||
<Redirect to="/" />
|
||||
</Switch>
|
||||
</Content>
|
||||
<StyledFooter />
|
||||
</Root>
|
||||
)
|
||||
|
||||
const Root = styled.div`
|
||||
display: grid;
|
||||
height: 100%;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: [header] 48px [body] 1fr [footer] 32px;
|
||||
`
|
||||
|
||||
const Content = styled.div`
|
||||
grid-row: body;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
const StyledFooter = styled(Footer)`
|
||||
grid-row: footer;
|
||||
`
|
||||
const StyledHeader = styled(Header)`
|
||||
grid-row: header;
|
||||
`
|
||||
export default App
|
|
@ -1,43 +0,0 @@
|
|||
import * as React from "react"
|
||||
import { RouteComponentProps, withRouter } from "react-router"
|
||||
import styled from "@emotion/styled"
|
||||
|
||||
interface RouteParams {
|
||||
hash: string
|
||||
}
|
||||
interface Props extends RouteComponentProps<{ hash: string }> {}
|
||||
|
||||
const InitializeSession = (props: Props) => {
|
||||
const [responseCode, setResponseCode] = React.useState(0)
|
||||
const [loading, setLoading] = React.useState(true)
|
||||
React.useEffect(() => {
|
||||
fetch(`/api/init-session`, {
|
||||
method: "post",
|
||||
headers: {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({ hash: props.match.params.hash })
|
||||
}).then(res => {
|
||||
setResponseCode(res.status)
|
||||
setLoading(false)
|
||||
if (res.status === 200) {
|
||||
props.history.replace("/my-events")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return (
|
||||
<Root>
|
||||
<p>Welcome back. Validating you link should take just a moment</p>
|
||||
{loading
|
||||
? "Loading..."
|
||||
: responseCode === 200
|
||||
? "You will be redirected now"
|
||||
: "Validation failed. Please try to log in again."}
|
||||
</Root>
|
||||
)
|
||||
}
|
||||
|
||||
const Root = styled.div``
|
||||
|
||||
export default withRouter<Props>(InitializeSession)
|
|
@ -1,96 +0,0 @@
|
|||
import * as React from "react"
|
||||
import styled from "@emotion/styled"
|
||||
|
||||
const sendLogin = (email: string) => {
|
||||
return fetch("/api/login", {
|
||||
method: "post",
|
||||
body: JSON.stringify({email}),
|
||||
headers:{
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const Login = () => {
|
||||
const [loading, setLoading] = React.useState(false)
|
||||
const [email, setEmail] = React.useState("")
|
||||
const [success, setSuccess] = React.useState(false)
|
||||
const isValid = /\w@\w.\w/.test(email)
|
||||
const [error, setError] = React.useState(false)
|
||||
|
||||
return (
|
||||
<Root onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
setError(false)
|
||||
setLoading(true)
|
||||
sendLogin(email)
|
||||
.then((res) => {
|
||||
if (res.status === 200) {
|
||||
setSuccess(true)
|
||||
} else {
|
||||
setError(true)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
setError(true)
|
||||
})
|
||||
}}>
|
||||
{
|
||||
error ? (
|
||||
<p>
|
||||
Could not find a user with the mentioned email. Please sign up first.
|
||||
</p>
|
||||
) : (
|
||||
success ? (
|
||||
<>
|
||||
<p>Invitation was sent to your email: {email}</p>
|
||||
<p>Please check your email and click on the link provided in the invitation</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<label htmlFor="email">Please enter your email to log in</label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={e => {
|
||||
setEmail(e.target.value)
|
||||
}}
|
||||
disabled={loading || success}
|
||||
/>
|
||||
<Button
|
||||
disabled={!isValid || success}
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
|
||||
)
|
||||
}
|
||||
</Root>
|
||||
)
|
||||
}
|
||||
|
||||
const Root = styled.form`
|
||||
display: flex;
|
||||
font-size: 24px;
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
flex-direction: column;
|
||||
width: 320px;
|
||||
`
|
||||
|
||||
const Input = styled.input`
|
||||
height: 38px;
|
||||
font-size: 24px;
|
||||
text-align: center;
|
||||
color: rgba(0, 0, 0, 0.7);
|
||||
margin-top: 18px;
|
||||
`
|
||||
const Button = styled.button`
|
||||
height: 38px;
|
||||
width: 68px;
|
||||
align-self: center;
|
||||
margin-top: 48px;
|
||||
`
|
||||
export default Login
|
|
@ -1,21 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import {RouteComponentProps, withRouter} from "react-router"
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
|
||||
}
|
||||
|
||||
const Signout = (props: Props) => {
|
||||
React.useEffect(() => {
|
||||
fetch('/api/signout')
|
||||
.then(() => {
|
||||
props.history.push('/')
|
||||
}).finally(() => {
|
||||
props.history.push('/')
|
||||
|
||||
})
|
||||
})
|
||||
return <p>You will be logged out now</p>
|
||||
}
|
||||
|
||||
export default withRouter(Signout)
|
|
@ -1,64 +0,0 @@
|
|||
import * as React from "react"
|
||||
import styled from "@emotion/styled"
|
||||
import {FormEvent} from "react"
|
||||
|
||||
const Signup = () => {
|
||||
const [loading, setLoading] = React.useState(false)
|
||||
const [success, setSuccess] = React.useState(null)
|
||||
const [error, setError] = React.useState(null)
|
||||
const [emailTaken, setEmailTaken] = React.useState(false)
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={(e: FormEvent) => {
|
||||
e.preventDefault()
|
||||
const form = e.target as any
|
||||
fetch("/api/signup", {
|
||||
method: "post",
|
||||
headers: {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: form.name.value,
|
||||
email: form.email.value
|
||||
})
|
||||
}).then((res) => {
|
||||
if (res.status === 200) {
|
||||
setSuccess(true)
|
||||
return
|
||||
} else {
|
||||
setError(true)
|
||||
setSuccess(false)
|
||||
}
|
||||
|
||||
if (res.status === 409) {
|
||||
setEmailTaken(true)
|
||||
}
|
||||
}).catch(() => {
|
||||
setError(true)
|
||||
})
|
||||
|
||||
}}
|
||||
>
|
||||
<Input name="name" placeholder="Your name"/>
|
||||
<Input name="username" placeholder="Choose a username"/>
|
||||
<Input name="email" type="email" placeholder="Your email"/>
|
||||
<Button type="submit">Sign Up</Button>
|
||||
{
|
||||
emailTaken ? (
|
||||
<p>This email is already registered. Please log in.</p>
|
||||
) : null
|
||||
}
|
||||
{
|
||||
error ? <p>We have encountered an error. Please try again.</p> : null
|
||||
}
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
const Root = styled.form``
|
||||
|
||||
const Input = styled.input``
|
||||
|
||||
const Button = styled.button``
|
||||
export default Signup
|
|
@ -1,24 +0,0 @@
|
|||
import * as React from "react"
|
||||
import MeQuery, { UserData } from "./MeQuery"
|
||||
|
||||
interface IAppContext {
|
||||
me: UserData
|
||||
}
|
||||
|
||||
const { Provider, Consumer } = React.createContext<IAppContext>({ me: null })
|
||||
|
||||
const AppContextProvider = props => (
|
||||
<MeQuery>
|
||||
{({ data, loading, error }) => {
|
||||
return (
|
||||
<Provider value={data}>
|
||||
{
|
||||
props.children
|
||||
}
|
||||
</Provider>
|
||||
)
|
||||
}}
|
||||
</MeQuery>
|
||||
)
|
||||
|
||||
export { AppContextProvider, Consumer as AppContext }
|
|
@ -1,34 +0,0 @@
|
|||
import { Query } from 'react-apollo'
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
const query = gql`
|
||||
query Me {
|
||||
me {
|
||||
id
|
||||
username
|
||||
events {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export interface UserEventData {
|
||||
id: string
|
||||
}
|
||||
|
||||
export interface UserData {
|
||||
id: string
|
||||
username: string
|
||||
events: UserEventData[]
|
||||
}
|
||||
|
||||
interface Data {
|
||||
me: UserData
|
||||
}
|
||||
|
||||
export default class MeQuery extends Query<Data> {
|
||||
static defaultProps = {
|
||||
query
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import styled from '@emotion/styled'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
}
|
||||
const Footer = (props: Props) => (
|
||||
<Root className={props.className}>
|
||||
|
||||
</Root>
|
||||
)
|
||||
|
||||
const Root = styled.div`
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
`
|
||||
|
||||
export default Footer
|
|
@ -1,55 +0,0 @@
|
|||
import { AppContext } from "../Context/AppContext"
|
||||
import { Link } from "react-router-dom"
|
||||
import * as React from "react"
|
||||
import styled from "@emotion/styled"
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
}
|
||||
|
||||
const Header = (props: Props) => (
|
||||
<AppContext>
|
||||
{({ me }) => (
|
||||
<Root className={props.className}>
|
||||
<Menu />
|
||||
<Title />
|
||||
<LinksSection>
|
||||
{me ? (
|
||||
<>
|
||||
<StyledLink to="/create">Create event</StyledLink>
|
||||
<StyledLink to="/logout">Log out</StyledLink>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<StyledLink to="/login">Log In</StyledLink>
|
||||
<StyledLink to="/signup">Sign Up</StyledLink>
|
||||
</>
|
||||
)}
|
||||
</LinksSection>
|
||||
</Root>
|
||||
)}
|
||||
</AppContext>
|
||||
)
|
||||
|
||||
const StyledLink = styled(Link)`
|
||||
color: rgba(0,0,0,.7);
|
||||
&:not(:first-of-type) {
|
||||
margin-left: 8px;
|
||||
}
|
||||
`
|
||||
|
||||
const Menu = styled.div``
|
||||
const Title = styled.div`
|
||||
flex: 1;
|
||||
`
|
||||
const Root = styled.div`
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
`
|
||||
|
||||
const LinksSection = styled.div`
|
||||
align-self: center;
|
||||
padding: 4px;
|
||||
`
|
||||
export default Header
|
|
@ -1,2 +0,0 @@
|
|||
import Header from './Header'
|
||||
export default Header
|
|
@ -1,53 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import {RouteComponentProps, withRouter} from "react-router"
|
||||
import OccurrenceDetailsQuery from "./OccurrenceDetailsQuery"
|
||||
import styled from '@emotion/styled'
|
||||
|
||||
interface RouteParams {
|
||||
occurrenceId: string
|
||||
sanitizedEventName: string
|
||||
}
|
||||
|
||||
interface Props extends RouteComponentProps<RouteParams> {
|
||||
}
|
||||
|
||||
const OccurrenceDetails = (props: Props) => {
|
||||
return <OccurrenceDetailsQuery variables={{occurrenceId: props.match.params.occurrenceId}}>
|
||||
{
|
||||
({data, loading, error}) => {
|
||||
if (loading) {
|
||||
return <p>loading...</p>
|
||||
}
|
||||
if (error) {
|
||||
return <p>{error.message}</p>
|
||||
}
|
||||
const info = data.occurrence.event.info[0]
|
||||
return (
|
||||
<Root>
|
||||
<Title>
|
||||
{ info.title }
|
||||
</Title>
|
||||
<Info>
|
||||
{ info.description }
|
||||
</Info>
|
||||
</Root>
|
||||
)
|
||||
|
||||
} }
|
||||
</OccurrenceDetailsQuery>
|
||||
}
|
||||
|
||||
const Title = styled.div`
|
||||
grid-row: title;
|
||||
`
|
||||
|
||||
const Info = styled.div`
|
||||
grid-row: info
|
||||
`
|
||||
|
||||
const Root = styled.div`
|
||||
display: grid;
|
||||
grid-template-rows: 48px [title] 1fr [info];
|
||||
`
|
||||
|
||||
export default withRouter(OccurrenceDetails)
|
|
@ -1,53 +0,0 @@
|
|||
import { Query } from 'react-apollo'
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
const query = gql`
|
||||
query GetOccurrenceDetails($occurrenceId: ID!) {
|
||||
occurrence(id: $occurrenceId) {
|
||||
id
|
||||
start
|
||||
end
|
||||
event {
|
||||
id
|
||||
owner {
|
||||
id
|
||||
name
|
||||
}
|
||||
info {
|
||||
description
|
||||
language
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export interface OccurrenceDetailsData {
|
||||
occurrence: {
|
||||
id: string
|
||||
start: string
|
||||
end: string
|
||||
event: {
|
||||
owner: {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
info: Array<{
|
||||
description
|
||||
language
|
||||
title
|
||||
}>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Variables {
|
||||
occurrenceId: string
|
||||
}
|
||||
|
||||
export default class OccurrenceDetailsQuery extends Query<OccurrenceDetailsData, Variables> {
|
||||
static defaultProps = {
|
||||
query
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import {AppContextProvider} from "./Context/AppContext"
|
||||
import {BrowserRouter as Router} from "react-router-dom"
|
||||
import {ApolloProvider} from "react-apollo"
|
||||
import {HttpLink} from "apollo-link-http"
|
||||
import {ApolloClient} from "apollo-client"
|
||||
import {InMemoryCache} from "apollo-cache-inmemory"
|
||||
import fetch from 'node-fetch'
|
||||
|
||||
interface Props {
|
||||
children: React.ReactChild | React.ReactChildren
|
||||
}
|
||||
|
||||
const httpLink = new HttpLink({
|
||||
uri: "/graphql",
|
||||
fetch
|
||||
})
|
||||
|
||||
const graphqlClient = new ApolloClient({
|
||||
connectToDevTools: true,
|
||||
link: httpLink,
|
||||
cache: new InMemoryCache().restore(window.__APOLLO_DATA__)
|
||||
}) as ApolloClient<any>
|
||||
|
||||
const Providers = (props: Props) => (
|
||||
<ApolloProvider client={graphqlClient}>
|
||||
<AppContextProvider>
|
||||
<Router>
|
||||
{ props.children }
|
||||
</Router>
|
||||
</AppContextProvider>
|
||||
</ApolloProvider>
|
||||
)
|
||||
|
||||
|
||||
export default Providers
|
|
@ -1,49 +0,0 @@
|
|||
import removeTypename from '../remove-typename'
|
||||
|
||||
describe('Remove Typename Util', () => {
|
||||
it('remove nested typename', () => {
|
||||
const input = {
|
||||
"input": {
|
||||
"id": "4b18628f-6069-4d6f-9708-f85862b2c3e6",
|
||||
"meta": {"tags": []},
|
||||
"time": {
|
||||
"start": "2019-06-12T10:00",
|
||||
"end": "2019-06-12T12:00",
|
||||
"timeZone": "Europe/Madrid",
|
||||
"recurrence": null
|
||||
},
|
||||
"location": {"address": "housy str...", "name": "my house", "__typename": "Location"},
|
||||
"info": [{
|
||||
"description": "Party in my house (:",
|
||||
"language": "en",
|
||||
"title": "My Event",
|
||||
"__typename": "EventInformation"
|
||||
}],
|
||||
"status": "confirmed"
|
||||
}
|
||||
}
|
||||
|
||||
const expected = {
|
||||
"input": {
|
||||
"id": "4b18628f-6069-4d6f-9708-f85862b2c3e6",
|
||||
"meta": {"tags": []},
|
||||
"time": {
|
||||
"start": "2019-06-12T10:00",
|
||||
"end": "2019-06-12T12:00",
|
||||
"timeZone": "Europe/Madrid",
|
||||
"recurrence": null
|
||||
},
|
||||
"location": {"address": "housy str...", "name": "my house"},
|
||||
"info": [{
|
||||
"description": "Party in my house (:",
|
||||
"language": "en",
|
||||
"title": "My Event",
|
||||
}],
|
||||
"status": "confirmed"
|
||||
}
|
||||
}
|
||||
|
||||
expect(removeTypename(input)).toEqual(expected)
|
||||
|
||||
})
|
||||
})
|
|
@ -1,12 +0,0 @@
|
|||
import * as React from "react"
|
||||
import * as ReactDOM from "react-dom"
|
||||
import App from "./App"
|
||||
import Providers from "./Providers"
|
||||
|
||||
const container = document.getElementById("app")
|
||||
ReactDOM.render(
|
||||
<Providers>
|
||||
<App />
|
||||
</Providers>,
|
||||
container
|
||||
)
|
|
@ -1,11 +0,0 @@
|
|||
import { clone } from 'ramda'
|
||||
|
||||
const omitTypename = (key, value) => {
|
||||
return key === '__typename' ? undefined : value
|
||||
}
|
||||
|
||||
const removeTypename = (object: any) => {
|
||||
return JSON.parse(JSON.stringify(object), omitTypename)
|
||||
}
|
||||
|
||||
export default removeTypename
|
|
@ -1,53 +0,0 @@
|
|||
import * as React from "react"
|
||||
import OccurrencesQuery from "../Event/OccurrencesQuery"
|
||||
import List from "./List"
|
||||
|
||||
interface Props {
|
||||
}
|
||||
|
||||
const beginningOfThisMonth = (() => {
|
||||
const dt = new Date()
|
||||
dt.setDate(1)
|
||||
return dt
|
||||
}
|
||||
)()
|
||||
|
||||
const endOfThisMonth = (
|
||||
() => {
|
||||
const dt = new Date()
|
||||
dt.setMonth(dt.getMonth() + 1)
|
||||
dt.setDate(0)
|
||||
return dt
|
||||
}
|
||||
)()
|
||||
|
||||
|
||||
const Calendar = (props: Props) => {
|
||||
return (
|
||||
<OccurrencesQuery
|
||||
variables={{
|
||||
filter: {
|
||||
from: "2019-06-01",
|
||||
to: "2019-06-30"
|
||||
}
|
||||
}}
|
||||
>
|
||||
{({data, error, loading}) => {
|
||||
if (loading) {
|
||||
return "I am a Spinner"
|
||||
}
|
||||
if (error) {
|
||||
return error.message
|
||||
}
|
||||
|
||||
if (!data.occurrences.length) {
|
||||
return <p>No occurrences</p>
|
||||
}
|
||||
return <List occurrences={data.occurrences}/>
|
||||
}}
|
||||
</OccurrencesQuery>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
export default Calendar
|
|
@ -1,20 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import {OccurrenceData} from "../Event/OccurrencesQuery"
|
||||
|
||||
interface Props {
|
||||
occurrence: OccurrenceData
|
||||
language?: string
|
||||
}
|
||||
|
||||
const CalendarOccurrence = (props: Props) => {
|
||||
const event = props.occurrence.event
|
||||
const localInfo = props.language
|
||||
? event.info.find(info => info.language === props.language)
|
||||
: event.info[0]
|
||||
return (<div>
|
||||
{event.info[0].title}
|
||||
</div>)
|
||||
|
||||
}
|
||||
|
||||
export default CalendarOccurrence
|
|
@ -1,44 +0,0 @@
|
|||
import * as React from "react"
|
||||
import { OccurrenceData } from "../../Event/OccurrencesQuery"
|
||||
import ListItem from "./ListItem"
|
||||
|
||||
interface Props {
|
||||
occurrences: OccurrenceData[]
|
||||
}
|
||||
|
||||
const List = (props: Props) => {
|
||||
const sorted = [...props.occurrences]
|
||||
sorted.sort((occA, occB) => {
|
||||
if (occA.start > occB.start) {
|
||||
return 1
|
||||
}
|
||||
if (occA.start < occB.start) {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
})
|
||||
const days: { [day: string]: OccurrenceData[] } = {}
|
||||
sorted.forEach(occ => {
|
||||
const day = occ.start.substring(0, 10)
|
||||
if (!days[day]) {
|
||||
days[day] = []
|
||||
}
|
||||
days[day].push(occ)
|
||||
})
|
||||
const dayNames = Object.keys(days)
|
||||
return (
|
||||
<div>
|
||||
<h3>Events for the dates between {dayNames[0]} and {dayNames[dayNames.length-1]}</h3>
|
||||
{dayNames.map(dayName => (
|
||||
<ul key={dayName}>
|
||||
<li>{dayName}</li>
|
||||
{days[dayName].map(occ => (
|
||||
<ListItem key={occ.id} occurrence={occ} />
|
||||
))}
|
||||
</ul>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default List
|
|
@ -1,46 +0,0 @@
|
|||
import * as React from "react"
|
||||
import { OccurrenceData } from "../../Event/OccurrencesQuery"
|
||||
import { AppContext } from "../../App/Context/AppContext"
|
||||
import { Link } from 'react-router-dom'
|
||||
import styled from '@emotion/styled'
|
||||
|
||||
interface Props {
|
||||
occurrence: OccurrenceData
|
||||
}
|
||||
|
||||
const sanitizeEventName = (name: string) => {
|
||||
return encodeURIComponent(name.trim().toLocaleLowerCase()
|
||||
.replace(/\s+/g,'-'))
|
||||
|
||||
}
|
||||
const ListItem = (props: Props) => {
|
||||
const { occurrence } = props
|
||||
const { event } = occurrence
|
||||
const info = event.info[0]
|
||||
const startTime = occurrence.start.split(" ")[1].substring(0, 5)
|
||||
return (
|
||||
<AppContext>
|
||||
{({ me }) => (
|
||||
<div>
|
||||
{startTime}
|
||||
|
||||
<Link to={`/o/${sanitizeEventName(event.info[0].title)}/${occurrence.id}`}>
|
||||
{info.title}
|
||||
</Link>
|
||||
{
|
||||
me && me.events.find(myEvent => myEvent.id === event.id) ? (
|
||||
<EditLink to={`/event/${event.id}/edit`}>Edit</EditLink>
|
||||
) : null
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</AppContext>
|
||||
)
|
||||
}
|
||||
|
||||
const EditLink = styled(Link)`
|
||||
margin-left: 8px;
|
||||
font-size: 0.6em;
|
||||
`
|
||||
|
||||
export default ListItem
|
|
@ -1,2 +0,0 @@
|
|||
import List from './List'
|
||||
export default List
|
|
@ -1,31 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import CreateEventMutation from './CreateEventMutation'
|
||||
import EventForm, {EventFormData} from "./EventForm"
|
||||
|
||||
const CreateEvent = () => {
|
||||
return <CreateEventMutation>
|
||||
{
|
||||
(createEvent, { loading }) => (
|
||||
<EventForm
|
||||
loading={loading}
|
||||
onSubmit={(values: EventFormData) => {
|
||||
createEvent({
|
||||
variables: {
|
||||
input: {
|
||||
info: values.info,
|
||||
location: values.location,
|
||||
time: {
|
||||
...values.time,
|
||||
timeZone: 'Europe/Madrid',
|
||||
},
|
||||
status: 'confirmed',
|
||||
meta: values.meta
|
||||
}
|
||||
}
|
||||
})
|
||||
}} />
|
||||
)}
|
||||
</CreateEventMutation>
|
||||
}
|
||||
|
||||
export default CreateEvent
|
|
@ -1,57 +0,0 @@
|
|||
import { Mutation } from 'react-apollo'
|
||||
import gql from 'graphql-tag'
|
||||
import {GQL} from "../../@types"
|
||||
|
||||
const mutation = gql`
|
||||
mutation CreateEvent($input: CreateEventInput!) {
|
||||
createEvent(input: $input) {
|
||||
id
|
||||
info {
|
||||
description
|
||||
language
|
||||
title
|
||||
}
|
||||
location {
|
||||
address
|
||||
name
|
||||
}
|
||||
occurrences {
|
||||
id
|
||||
start
|
||||
end
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
interface Variables {
|
||||
input: GQL.ICreateEventInput
|
||||
}
|
||||
|
||||
interface InfoData {
|
||||
desciption: string
|
||||
language: string
|
||||
title: string
|
||||
}
|
||||
|
||||
interface OccurrenceData {
|
||||
id: string
|
||||
start: string
|
||||
end: string
|
||||
}
|
||||
interface Data {
|
||||
id: string
|
||||
info: InfoData[]
|
||||
location: {
|
||||
address: string
|
||||
name: string
|
||||
|
||||
}
|
||||
occurrences: OccurrenceData[]
|
||||
}
|
||||
|
||||
export default class CreateEventMutation extends Mutation<Data, Variables> {
|
||||
static defaultProps = {
|
||||
mutation
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
import * as React from "react"
|
||||
|
||||
interface Props {
|
||||
value: string
|
||||
onChange: (value: string) => void
|
||||
}
|
||||
|
||||
const DateTime = ({ value, onChange }: Props) => {
|
||||
const dateTimeSplit = value.split('T')
|
||||
return (
|
||||
<div>
|
||||
<input type="date" value={dateTimeSplit[0]} onChange={(e) => {
|
||||
const newDateValue = e.target.value
|
||||
onChange([newDateValue, dateTimeSplit[1]].join('T'))
|
||||
}}/>
|
||||
<input type="time" value={dateTimeSplit[1]} onChange={(e) => {
|
||||
const newTimeValue = e.target.value
|
||||
onChange([dateTimeSplit[0], newTimeValue].join('T'))
|
||||
}}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DateTime
|
|
@ -1,56 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import GetEventQuery from "./GetEventQuery"
|
||||
import EventForm from "./EventForm"
|
||||
import EditEventMutation from "./EditEventMutation"
|
||||
import removeTypename from "../App/remove-typename";
|
||||
|
||||
interface Props {
|
||||
eventId: string
|
||||
}
|
||||
|
||||
const EditEvent = (props: Props) => (
|
||||
<EditEventMutation>
|
||||
{
|
||||
(editEvent, { loading: editLoading }) => (
|
||||
<GetEventQuery skip={!props.eventId} variables={{id: props.eventId}}>
|
||||
{
|
||||
({data, error, loading}) => {
|
||||
if (loading) {
|
||||
return 'Loading...'
|
||||
}
|
||||
if (error) {
|
||||
return error.message
|
||||
}
|
||||
const event = removeTypename(data.event)
|
||||
|
||||
return (
|
||||
<EventForm
|
||||
loading={loading}
|
||||
onSubmit={values => {
|
||||
editEvent({
|
||||
variables: {
|
||||
input: {
|
||||
id: props.eventId,
|
||||
...values
|
||||
}
|
||||
}
|
||||
})
|
||||
}}
|
||||
values={{
|
||||
meta: {
|
||||
tags: event.meta.tags
|
||||
},
|
||||
time: event.time,
|
||||
location: event.location,
|
||||
info: event.info,
|
||||
status: event.status
|
||||
}}/>
|
||||
)
|
||||
}
|
||||
}
|
||||
</GetEventQuery>
|
||||
)
|
||||
}
|
||||
</EditEventMutation>
|
||||
)
|
||||
export default EditEvent
|
|
@ -1,27 +0,0 @@
|
|||
import { Mutation } from 'react-apollo'
|
||||
import gql from 'graphql-tag'
|
||||
import {EventData, EventFragment} from "./GetEventQuery"
|
||||
import {GQL} from "../../@types"
|
||||
|
||||
const mutation = gql`
|
||||
${EventFragment}
|
||||
mutation EditEvent($input: UpdateEventInput!) {
|
||||
updateEvent(input: $input) {
|
||||
...EventData
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
interface Data {
|
||||
updateEvent: EventData
|
||||
}
|
||||
|
||||
interface Variables {
|
||||
input: GQL.IUpdateEventInput
|
||||
}
|
||||
|
||||
export default class EditEventMutation extends Mutation<Data, Variables> {
|
||||
static defaultProps = {
|
||||
mutation
|
||||
}
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
import gql from 'graphql-tag'
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
import * as React from "react"
|
||||
import { EventData } from "./EventsQuery"
|
||||
import {OccurrenceData} from "./OccurrencesQuery";
|
||||
|
||||
interface Props {
|
||||
event: EventData
|
||||
language?: string
|
||||
}
|
||||
|
||||
const Event = (props: Props) => {
|
||||
// Get desired language or fallback to first language
|
||||
const localInfo = props.language
|
||||
? props.event.info.find(info => info.language === props.language)
|
||||
: props.event.info[0]
|
||||
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
localInfo ? (
|
||||
<React.Fragment>
|
||||
<h1> {localInfo.title} </h1>
|
||||
<p> {localInfo.description} </p>
|
||||
</React.Fragment>
|
||||
) : 'Info not available'
|
||||
}
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Event
|
|
@ -1,115 +0,0 @@
|
|||
import * as React from "react"
|
||||
import { Formik, Form, Field } from "formik"
|
||||
import styled from "styled-components"
|
||||
import { EventStatus } from "../../@types"
|
||||
import DateTime from "./DateTime";
|
||||
|
||||
interface Props {
|
||||
values?: EventFormData
|
||||
onSubmit: (values: EventFormData) => void
|
||||
loading: boolean
|
||||
}
|
||||
|
||||
export interface EventFormData {
|
||||
time: {
|
||||
timeZone: string;
|
||||
start: string;
|
||||
end: string;
|
||||
recurrence?: string;
|
||||
}
|
||||
info: Array<{
|
||||
language: string;
|
||||
title: string;
|
||||
description: string;
|
||||
}>
|
||||
location: {
|
||||
address?: string;
|
||||
name: string;
|
||||
}
|
||||
status: EventStatus
|
||||
meta: {
|
||||
tags: string[];
|
||||
}
|
||||
}
|
||||
|
||||
class EventFormik extends Formik<EventFormData> {}
|
||||
|
||||
const nextWeekTenAM = new Date()
|
||||
nextWeekTenAM.setUTCDate(nextWeekTenAM.getDate() + 7)
|
||||
nextWeekTenAM.setUTCHours(10, 0)
|
||||
|
||||
const nextWeekMidday = new Date(nextWeekTenAM)
|
||||
nextWeekMidday.setUTCHours(12)
|
||||
|
||||
const EventForm = (props: Props) => {
|
||||
const isEdit = !!props.values
|
||||
return (
|
||||
<EventFormik
|
||||
onSubmit={props.onSubmit}
|
||||
initialValues={
|
||||
props.values
|
||||
? props.values
|
||||
: {
|
||||
time: {
|
||||
timeZone: "Europe/Madrid",
|
||||
start: nextWeekTenAM.toISOString().substring(0, 16),
|
||||
end: nextWeekMidday.toISOString().substring(0, 16)
|
||||
},
|
||||
info: [
|
||||
{
|
||||
language: "en",
|
||||
title: "",
|
||||
description: ""
|
||||
}
|
||||
],
|
||||
location: {
|
||||
name: ""
|
||||
},
|
||||
meta: {
|
||||
tags: []
|
||||
},
|
||||
status: "confirmed"
|
||||
}
|
||||
}
|
||||
validate={values => {
|
||||
const errors: any = {}
|
||||
}}
|
||||
>
|
||||
{({ isValid, setFieldValue }) => (
|
||||
<StyledForm>
|
||||
<Field name="info[0].title">
|
||||
{({ field }) => <input {...field} placeholder="Name your event" />}
|
||||
</Field>
|
||||
<Field name="info[0].description">
|
||||
{({ field }) => (
|
||||
<textarea
|
||||
{...field}
|
||||
placeholder="Write a few words about your event"
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
<Field name="time.start">
|
||||
{
|
||||
({ field }) => <DateTime {...field} onChange={(newStartValue) => setFieldValue('time.start', newStartValue)} />
|
||||
}
|
||||
</Field>
|
||||
<Field name="time.end">
|
||||
{
|
||||
({ field }) => <DateTime {...field} onChange={(newEndValue) => setFieldValue('time.end', newEndValue)} />
|
||||
}
|
||||
</Field>
|
||||
<Field name="location.name">
|
||||
{({ field }) => <input {...field} placeholder="Location's name" />}
|
||||
</Field>
|
||||
<Field name="location.address">
|
||||
{({ field }) => <input {...field} placeholder="Address" />}
|
||||
</Field>
|
||||
|
||||
<button type="submit">{isEdit ? "Edit" : "Create"}</button>
|
||||
</StyledForm>
|
||||
)}
|
||||
</EventFormik>
|
||||
)
|
||||
}
|
||||
const StyledForm = styled(Form)``
|
||||
export default EventForm
|
|
@ -1,46 +0,0 @@
|
|||
import {Query} from 'react-apollo'
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
const query = gql`
|
||||
query EventsQuery($filter: EventsQueryFilter!) {
|
||||
events(filter: $filter) {
|
||||
id
|
||||
status
|
||||
info {
|
||||
title
|
||||
description
|
||||
language
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
interface Variables {
|
||||
filter: {
|
||||
limit?: number
|
||||
owner?: number
|
||||
from?: string
|
||||
to?: string
|
||||
categories?: string[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface EventData {
|
||||
id: string
|
||||
status: string
|
||||
info: Array<{
|
||||
title: string
|
||||
description: string
|
||||
language: string
|
||||
}>
|
||||
}
|
||||
|
||||
interface Data {
|
||||
events: EventData[]
|
||||
}
|
||||
|
||||
export default class EventsQuery extends Query<Data, Variables> {
|
||||
static defaultProps = {
|
||||
query
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
import {Query} from 'react-apollo'
|
||||
import gql from 'graphql-tag'
|
||||
import {EventStatus} from "../../@types"
|
||||
|
||||
export const EventFragment = gql`
|
||||
fragment EventData on CalendarEvent {
|
||||
id
|
||||
info {
|
||||
description
|
||||
language
|
||||
title
|
||||
}
|
||||
location {
|
||||
address
|
||||
name
|
||||
}
|
||||
time {
|
||||
start
|
||||
end
|
||||
timeZone
|
||||
recurrence
|
||||
}
|
||||
status
|
||||
meta {
|
||||
tags
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export interface EventTimeData {
|
||||
start: string
|
||||
end: string
|
||||
timeZone: string
|
||||
recurrence: string
|
||||
}
|
||||
|
||||
export interface EventInfoData {
|
||||
description: string
|
||||
language: string
|
||||
title: string
|
||||
}
|
||||
|
||||
export interface ContactData {
|
||||
contact: {
|
||||
email: string
|
||||
phone: string
|
||||
}
|
||||
languages: string[]
|
||||
name: string
|
||||
}
|
||||
export interface EventMetaData {
|
||||
tags: string[]
|
||||
}
|
||||
|
||||
export interface EventData {
|
||||
id: string
|
||||
info: EventInfoData[]
|
||||
contact: ContactData[]
|
||||
location: {
|
||||
address: string
|
||||
name: string
|
||||
}
|
||||
time: EventTimeData
|
||||
status: EventStatus
|
||||
meta: EventMetaData
|
||||
}
|
||||
|
||||
interface Data {
|
||||
event: EventData
|
||||
}
|
||||
|
||||
const query = gql`
|
||||
${EventFragment}
|
||||
query GetEvent($id: ID!) {
|
||||
event(id: $id) {
|
||||
...EventData
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
interface Variables {
|
||||
id: string
|
||||
}
|
||||
|
||||
export default class GetEventQuery extends Query<Data, Variables> {
|
||||
static defaultProps = { query }
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
import {Query} from 'react-apollo'
|
||||
import gql from 'graphql-tag'
|
||||
import {GQL} from "../../@types"
|
||||
|
||||
const query = gql`
|
||||
query EventsQuery($filter: OccurrencesQueryFilter!) {
|
||||
occurrences(filter: $filter) {
|
||||
id
|
||||
start
|
||||
end
|
||||
event {
|
||||
id
|
||||
info {
|
||||
language
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
interface Variables {
|
||||
filter: GQL.IOccurrencesQueryFilter
|
||||
}
|
||||
|
||||
export interface InfoData {
|
||||
language: string
|
||||
title: string
|
||||
}
|
||||
|
||||
export interface OccurrenceData {
|
||||
id: string
|
||||
start: string
|
||||
end: string
|
||||
event: {
|
||||
id: string
|
||||
info: InfoData[]
|
||||
}
|
||||
}
|
||||
|
||||
interface Data {
|
||||
occurrences: OccurrenceData[]
|
||||
}
|
||||
|
||||
export default class OccurrencesQuery extends Query<Data, Variables> {
|
||||
static defaultProps = {
|
||||
query
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import {AppContextProvider} from "../App/Context/AppContext"
|
||||
import {ApolloProvider} from "react-apollo"
|
||||
import {ApolloClient} from "apollo-client"
|
||||
import {InMemoryCache} from "apollo-cache-inmemory"
|
||||
import { StaticRouter } from 'react-router'
|
||||
|
||||
interface Props {
|
||||
children: React.ReactChild | React.ReactChildren
|
||||
graphqlClient: ApolloClient<InMemoryCache>
|
||||
location: string
|
||||
}
|
||||
|
||||
const SSRProviders = (props: Props) => (
|
||||
<ApolloProvider client={props.graphqlClient}>
|
||||
<AppContextProvider>
|
||||
<StaticRouter location={props.location}>
|
||||
{ props.children }
|
||||
</StaticRouter>
|
||||
</AppContextProvider>
|
||||
</ApolloProvider>
|
||||
)
|
||||
|
||||
|
||||
export default SSRProviders
|
|
@ -1,57 +0,0 @@
|
|||
import { Request, Response } from "express-serve-static-core"
|
||||
import * as ReactDOMServer from "react-dom/server"
|
||||
import { renderStylesToString } from "emotion-server"
|
||||
import App from "../App/App"
|
||||
import * as React from "react"
|
||||
import SSRProviders from "./SSRProviders"
|
||||
import * as fs from "fs"
|
||||
import * as path from "path"
|
||||
import * as Mustache from "mustache"
|
||||
import { ApolloClient } from "apollo-client"
|
||||
import { InMemoryCache } from "apollo-cache-inmemory"
|
||||
import { HttpLink } from "apollo-link-http"
|
||||
import fetch from "node-fetch"
|
||||
import { getDataFromTree } from "react-apollo"
|
||||
import apolloLogger from "apollo-link-logger"
|
||||
import { ApolloLink } from "apollo-link"
|
||||
export const httpSSRHandler = async (req: Request, res: Response) => {
|
||||
res.status(200)
|
||||
const httpLink = new HttpLink({
|
||||
uri: "http://localhost:4000/graphql",
|
||||
fetch,
|
||||
headers: {
|
||||
cookie: req.header("Cookie")
|
||||
}
|
||||
})
|
||||
|
||||
const link = ApolloLink.from([apolloLogger, httpLink])
|
||||
|
||||
const graphqlClient = new ApolloClient({
|
||||
connectToDevTools: true,
|
||||
link,
|
||||
cache: new InMemoryCache(),
|
||||
ssrMode: true,
|
||||
}) as ApolloClient<any>
|
||||
|
||||
const app = (
|
||||
<SSRProviders location={req.path} graphqlClient={graphqlClient}>
|
||||
<App />
|
||||
</SSRProviders>
|
||||
)
|
||||
|
||||
const appWithData = await getDataFromTree(app)
|
||||
|
||||
const appBody = renderStylesToString(appWithData)
|
||||
const initialState = graphqlClient.extract()
|
||||
|
||||
const template = fs.readFileSync(
|
||||
path.join(__dirname, "./index.html.mustache"),
|
||||
"utf-8"
|
||||
)
|
||||
|
||||
const result = Mustache.render(template, {
|
||||
appBody,
|
||||
apolloData: JSON.stringify(initialState)
|
||||
})
|
||||
res.send(result)
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Quepasa Alpujarra</title>
|
||||
<script type="application/javascript">
|
||||
__APOLLO_DATA__ = {{{ apolloData }}};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">{{{ appBody }}}</div>
|
||||
<script type="application/javascript" src="/bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,9 +0,0 @@
|
|||
import * as express from 'express'
|
||||
import { httpSSRHandler } from './handler'
|
||||
|
||||
const port = 5000
|
||||
console.log(`Starting SSR on port ${port}...`)
|
||||
const app = express()
|
||||
app.get('/*', httpSSRHandler)
|
||||
app.listen(port)
|
||||
console.log('Successfully started')
|
|
@ -1,79 +0,0 @@
|
|||
{
|
||||
"name": "client",
|
||||
"version": "0.0.1",
|
||||
"main": "index.ts",
|
||||
"license": "private",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "webpack --config ./webpack.config.ts",
|
||||
"start": "NODE_ENV=development webpack-dev-server --config ./webpack.config.ts --hot --progress",
|
||||
"ssr": "ts-node SSR/index.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/styled": "^10.0.12",
|
||||
"@types/mustache": "^0.8.32",
|
||||
"@types/react-router-dom": "^4.3.3",
|
||||
"apollo-cache-inmemory": "^1.3.8",
|
||||
"apollo-client": "^2.4.5",
|
||||
"apollo-link": "^1.2.12",
|
||||
"apollo-link-http": "^1.5.14",
|
||||
"apollo-link-logger": "^1.2.3",
|
||||
"date-fns": "^1.29.0",
|
||||
"emotion-server": "^10.0.14",
|
||||
"express": "^4.17.1",
|
||||
"formik": "^1.4.1",
|
||||
"graphql": "^14.0.2",
|
||||
"graphql-tag": "^2.9.2",
|
||||
"graphql-tools": "^4.0.3",
|
||||
"jest": "^24.8.0",
|
||||
"jest-cli": "^24.8.0",
|
||||
"keycode": "^2.2.0",
|
||||
"mustache": "^3.0.1",
|
||||
"node-fetch": "^2.6.0",
|
||||
"nodemon": "^1.19.1",
|
||||
"ramda": "^0.26.1",
|
||||
"react": "^16.8.6",
|
||||
"react-apollo": "^2.2.4",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-router": "^5.0.0",
|
||||
"react-router-dom": "^5.0.0",
|
||||
"styled-components": "^4.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.2.3",
|
||||
"@babel/core": "^7.3.3",
|
||||
"@babel/plugin-proposal-class-properties": "^7.3.3",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.3.2",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||
"@babel/plugin-syntax-export-default-from": "^7.2.0",
|
||||
"@babel/plugin-syntax-jsx": "^7.2.0",
|
||||
"@babel/preset-env": "^7.3.1",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"@babel/preset-typescript": "^7.3.3",
|
||||
"@emotion/babel-preset-css-prop": "^10.0.9",
|
||||
"@types/jest": "^24.0.6",
|
||||
"@types/node": "^11.9.4",
|
||||
"@types/react": "^16.4.18",
|
||||
"@types/react-dom": "^16.8.4",
|
||||
"@types/react-loadable": "^5.4.1",
|
||||
"@types/react-router": "^4.0.31",
|
||||
"@types/styled-components": "^4.1.10",
|
||||
"@types/webpack": "^4.4.27",
|
||||
"@types/webpack-dev-server": "^3.1.2",
|
||||
"apollo-link-context": "^1.0.14",
|
||||
"babel-loader": "^8.0.5",
|
||||
"babel-plugin-styled-components": "^1.10.0",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"react-hot-loader": "^4.8.2",
|
||||
"ts-jest": "^24.0.0",
|
||||
"ts-node": "^8.0.2",
|
||||
"tslint": "^5.12.1",
|
||||
"tslint-config-prettier": "^1.16.0",
|
||||
"typescript": "^3.5.3",
|
||||
"webpack": "^4.30.0",
|
||||
"webpack-cli": "^3.2.3",
|
||||
"webpack-dev-server": "^3.3.1",
|
||||
"webpack-html-plugin": "^0.1.1",
|
||||
"yarn": "^1.13.0"
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"jsx": "react",
|
||||
"module": "commonjs",
|
||||
"lib": [
|
||||
"dom",
|
||||
"es7",
|
||||
"esnext.asynciterable"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
".*/__tests__/.*", "node_modules"
|
||||
]
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
import * as webpack from 'webpack'
|
||||
import * as path from "path"
|
||||
import * as HtmlWebpackPlugin from 'html-webpack-plugin'
|
||||
import * as express from 'express'
|
||||
import * as WebpackDevServer from "webpack-dev-server"
|
||||
import {httpSSRHandler} from "./SSR/handler"
|
||||
|
||||
const config: webpack.Configuration = {
|
||||
entry: './App/index.tsx',
|
||||
resolve: {
|
||||
extensions: [".js", ".jsx", ".ts", ".tsx"]
|
||||
},
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
hot: true,
|
||||
before: (app: express.Application, server: WebpackDevServer) => {
|
||||
//todo: improve the regex
|
||||
app.get(/^((?!\.\w+).)*$/, httpSSRHandler)
|
||||
},
|
||||
proxy: {
|
||||
'/graphql': {
|
||||
redirect: false,
|
||||
changeOrigin: true,
|
||||
target: `http://localhost:4000`,
|
||||
},
|
||||
'/api': {
|
||||
redirect: false,
|
||||
changeOrigin: true,
|
||||
target: `http://localhost:4000`,
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
exclude: path.resolve(__dirname, "node_modules"),
|
||||
test: /\.tsx?$/,
|
||||
use: {
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
presets: [
|
||||
"@babel/typescript",
|
||||
"@babel/react"
|
||||
],
|
||||
plugins: [].filter(Boolean)
|
||||
}
|
||||
}
|
||||
} ]
|
||||
},
|
||||
devtool: '@source-map',
|
||||
output: {
|
||||
path: path.resolve(__dirname, './dist'),
|
||||
filename: 'bundle.js',
|
||||
publicPath: '/'
|
||||
},
|
||||
plugins: [new HtmlWebpackPlugin({
|
||||
title: "blabla",
|
||||
})]
|
||||
|
||||
}
|
||||
|
||||
export default config
|
7952
client/yarn.lock
7952
client/yarn.lock
File diff suppressed because it is too large
Load Diff
|
@ -48,7 +48,7 @@ spec:
|
|||
spec:
|
||||
containers:
|
||||
- name: master
|
||||
image: eu.gcr.io/qpa-staging-237606/qpa:0.0.4
|
||||
image: eu.gcr.io/qpa-staging-237606/qpa:0.0.6
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
|
@ -56,6 +56,8 @@ spec:
|
|||
ports:
|
||||
- containerPort: 4000
|
||||
env:
|
||||
- name: DOMAIN
|
||||
value: "alpha.quapasaalpujarra.com"
|
||||
- name: DB_USER
|
||||
value: "qpa"
|
||||
- name: POSTGRES_DB
|
||||
|
@ -64,3 +66,9 @@ spec:
|
|||
value: "1zrBGfidoPzf"
|
||||
- name: POSTGRES_HOST
|
||||
value: "10.79.112.3"
|
||||
- name: MAILGUN_KEY
|
||||
value: "key-59ad615180cb44e4ff5fd41236a9694a"
|
||||
- name: EMAIL_DOMAIN
|
||||
value: "staging.quepasaalpujarra.com"
|
||||
|
||||
|
||||
|
|
82
package.json
82
package.json
|
@ -1,30 +1,62 @@
|
|||
{
|
||||
"name": "gcf-auth",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"repository": "git@gitlab.com:amiiit/gcf-auth.git",
|
||||
"author": "Amit Jakubowicz <a.jakubowicz@travelaudience.com>",
|
||||
"license": "MIT",
|
||||
"resolutions": {
|
||||
"@types/node": "11.11.0"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
"client",
|
||||
"server"
|
||||
],
|
||||
"name": "functions",
|
||||
"scripts": {
|
||||
"codegen": "gql2ts ./schema.graphql -o ./@types/graphql.d.ts",
|
||||
"ssr": "(cd client; yarn ssr)",
|
||||
"server": "(cd server; yarn start)",
|
||||
"server-build": "(rm -r ./server/lib; cd server; yarn build)",
|
||||
"client": "(cd client; yarn start)",
|
||||
"prod-server": "(cd server; yarn prod-server)"
|
||||
"lint": "tslint --project tsconfig.json",
|
||||
"build": "tsc --version && tsc ; cp ./src/schema.graphql ./lib/",
|
||||
"start": "nodemon --exec ts-node --files src/index.ts",
|
||||
"test": "jest --runInBand",
|
||||
"prod-server": "DB_USER=qpa DB_PASSWORD=qpa POSTGRES_DB=qpa forever start -v -c ts-node ./src/index.ts"
|
||||
},
|
||||
"main": "lib/index.js",
|
||||
"dependencies": {
|
||||
"gql2ts": "^1.10.1",
|
||||
"typescript": "^3.4.5"
|
||||
|
||||
}
|
||||
"@types/graphql": "^14.2.3",
|
||||
"apollo-server": "^2.8.1",
|
||||
"apollo-server-testing": "^2.8.1",
|
||||
"atob": "^2.1.2",
|
||||
"cookie": "^0.4.0",
|
||||
"cors": "^2.8.5",
|
||||
"graphql": "^14.4.2",
|
||||
"graphql-import": "^0.7.1",
|
||||
"graphql-tag": "^2.10.1",
|
||||
"graphql-tools": "^4.0.5",
|
||||
"luxon": "^1.17.2",
|
||||
"mailgun-js": "^0.22.0",
|
||||
"node-pre-gyp": "^0.13.0",
|
||||
"randomstring": "^1.1.5",
|
||||
"rrule": "^2.6.2",
|
||||
"typeorm": "^0.2.18",
|
||||
"typescript": "^3.5.3",
|
||||
"uuid": "^3.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^24.0.16",
|
||||
"@types/luxon": "^1.15.2",
|
||||
"@types/node": "^10.12.30",
|
||||
"@types/uuid": "^3.4.5",
|
||||
"jest": "^24.8.0",
|
||||
"nodemon": "^1.19.1",
|
||||
"ts-jest": "^24.0.2",
|
||||
"ts-node": "^8.3.0",
|
||||
"tslint": "^5.18.0",
|
||||
"typescript": "^3.5.3"
|
||||
},
|
||||
"jest": {
|
||||
"globalSetup": "./jest-setup.js",
|
||||
"transform": {
|
||||
"^.+\\.tsx?$": "ts-jest"
|
||||
},
|
||||
"testMatch": [
|
||||
"**/*.spec.ts"
|
||||
],
|
||||
"moduleFileExtensions": [
|
||||
"ts",
|
||||
"tsx",
|
||||
"js",
|
||||
"jsx",
|
||||
"json",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"private": true,
|
||||
"version": "1.0.0"
|
||||
}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
import MUIInput, {InputProps as MUIInputProps} from '@material-ui/core/Input'
|
||||
import styled from 'cc-styled'
|
||||
import * as React from 'react'
|
||||
|
||||
interface Props extends MUIInputProps {
|
||||
|
||||
}
|
||||
|
||||
const InputBase = (props: Props) => <MUIInput {...props} />
|
||||
|
||||
const Input = styled(InputBase)`
|
||||
|
||||
`
|
||||
|
||||
export default Input
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"name": "cc-components",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@material-ui/core": "^3.1.2",
|
||||
"react": "^16.5.2"
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"outDir": "build/dist",
|
||||
"module": "esnext",
|
||||
"target": "es5",
|
||||
"lib": ["es6", "dom"],
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"jsx": "react",
|
||||
"moduleResolution": "node",
|
||||
"rootDir": "src",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"noUnusedLocals": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"build",
|
||||
"scripts",
|
||||
"acceptance-tests",
|
||||
"webpack",
|
||||
"jest",
|
||||
"src/setupTests.ts"
|
||||
],
|
||||
"indlude": [
|
||||
"node_modules/@types"
|
||||
]
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
import * as styledComponents from 'styled-components';
|
||||
import{ default as defaultTheme, Theme } from './theme';
|
||||
const {
|
||||
default: styled,
|
||||
css,
|
||||
injectGlobal,
|
||||
keyframes,
|
||||
ThemeProvider
|
||||
} = styledComponents as styledComponents.ThemedStyledComponentsModule<Theme>;
|
||||
|
||||
export { css, injectGlobal, keyframes, ThemeProvider, Theme, defaultTheme};
|
||||
export default styled;
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"name": "cc-styled",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@emotion/core": "^10.0.10",
|
||||
"emotion": "^10.0.9",
|
||||
"styled-components": "^3.4.9"
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
export interface Theme {
|
||||
color: {
|
||||
[K: string]: string
|
||||
};
|
||||
nestedDepthBackgrounds: string[];
|
||||
font: {
|
||||
textL: string;
|
||||
textM: string;
|
||||
textS: string;
|
||||
textXL: string;
|
||||
textXS: string;
|
||||
textXXL: string;
|
||||
};
|
||||
input: {
|
||||
height: string;
|
||||
borderRadius: string;
|
||||
};
|
||||
chipColor: {
|
||||
tag: string;
|
||||
productType: string;
|
||||
};
|
||||
snackbar: {
|
||||
borderColor: {
|
||||
info: string;
|
||||
debug: string;
|
||||
warning: string;
|
||||
error: string;
|
||||
success: string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const color = {
|
||||
azure: '#00a9e0',
|
||||
black: '#000000',
|
||||
clearBlue: '#1890ff',
|
||||
darkGrey: '#4a4a4a',
|
||||
deepPink: '#ce0058',
|
||||
dustyOrange: '#f78a27',
|
||||
green: '#00a34e',
|
||||
grey: '#666666',
|
||||
lightBlack: '#333333',
|
||||
lightGrey: '#d3d3d3',
|
||||
lightSilver: '#f1f1f1',
|
||||
perrywinkle: '#7fbae3',
|
||||
powderBlue: '#9bcaeb',
|
||||
red: '#ff0000',
|
||||
silver: '#eff0f0',
|
||||
tangerine: '#e95326',
|
||||
warmPurple: '#6f2b8d',
|
||||
white: '#fff',
|
||||
yellow: '#feeb3d',
|
||||
};
|
||||
|
||||
const theme: Theme = {
|
||||
color,
|
||||
font: {
|
||||
textL: '16px',
|
||||
textM: '14px',
|
||||
textS: '13px',
|
||||
textXL: '18px',
|
||||
textXS: '11px',
|
||||
textXXL: '20px',
|
||||
},
|
||||
nestedDepthBackgrounds: [
|
||||
'rgba(155, 202, 235, 0.7)',
|
||||
'rgba(155, 202, 235, 0.5)',
|
||||
'rgba(155, 202, 235, 0.3)',
|
||||
'rgba(155, 202, 235, 0.1)',
|
||||
],
|
||||
input: {
|
||||
height: '24px',
|
||||
borderRadius: '4px',
|
||||
},
|
||||
chipColor: {
|
||||
productType: color.perrywinkle,
|
||||
tag: color.azure,
|
||||
},
|
||||
snackbar: {
|
||||
borderColor: {
|
||||
success: color.green,
|
||||
warning: color.yellow,
|
||||
info: color.clearBlue,
|
||||
debug: color.deepPink,
|
||||
error: color.red,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default theme;
|
|
@ -1,16 +0,0 @@
|
|||
# This file specifies files that are *not* uploaded to Google Cloud Platform
|
||||
# using gcloud. It follows the same syntax as .gitignore, with the addition of
|
||||
# "#!include" directives (which insert the entries of the given .gitignore-style
|
||||
# file at that point).
|
||||
#
|
||||
# For more information, run:
|
||||
# $ gcloud topic gcloudignore
|
||||
#
|
||||
.gcloudignore
|
||||
# If you would like to upload your .git directory, .gitignore file or files
|
||||
# from your .gitignore file, remove the corresponding line
|
||||
# below:
|
||||
.git
|
||||
.gitignore
|
||||
|
||||
node_modules
|
|
@ -1,2 +0,0 @@
|
|||
yarn-error.log
|
||||
config.ts
|
|
@ -1,72 +0,0 @@
|
|||
{
|
||||
"name": "functions",
|
||||
"scripts": {
|
||||
"lint": "tslint --project tsconfig.json",
|
||||
"build": "tsc --version && tsc; cp ./src/schema.graphql ./lib/",
|
||||
"start": "nodemon --exec ts-node --files src/index.ts",
|
||||
"test": "jest --runInBand",
|
||||
"prod-server": "DB_USER=qpa DB_PASSWORD=qpa POSTGRES_DB=qpa forever start -v -c ts-node ./src/index.ts"
|
||||
},
|
||||
"main": "lib/index.js",
|
||||
"dependencies": {
|
||||
"@types/graphql": "^14.2.3",
|
||||
"apollo-server": "^2.8.1",
|
||||
"apollo-server-testing": "^2.8.1",
|
||||
"atob": "^2.1.2",
|
||||
"axios": "^0.19.0",
|
||||
"client": "0.0.1",
|
||||
"cookie": "^0.4.0",
|
||||
"cors": "^2.8.5",
|
||||
"graphql": "^14.4.2",
|
||||
"graphql-import": "^0.7.1",
|
||||
"graphql-tag": "^2.10.1",
|
||||
"graphql-tools": "^4.0.5",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"luxon": "^1.17.2",
|
||||
"mailgun-js": "^0.22.0",
|
||||
"node-pre-gyp": "^0.13.0",
|
||||
"pg": "^7.12.0",
|
||||
"random-string": "^0.2.0",
|
||||
"randomstring": "^1.1.5",
|
||||
"react-dom": "^16.8.6",
|
||||
"rrule": "^2.6.2",
|
||||
"superagent": "^5.1.0",
|
||||
"typeorm": "^0.2.18",
|
||||
"typescript": "^3.5.3",
|
||||
"uuid": "^3.3.2",
|
||||
"webpack-node-externals": "^1.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^24.0.16",
|
||||
"@types/luxon": "^1.15.2",
|
||||
"@types/node": "^10.12.30",
|
||||
"@types/uuid": "^3.4.5",
|
||||
"jest": "^24.8.0",
|
||||
"nodemon": "^1.19.1",
|
||||
"ts-jest": "^24.0.2",
|
||||
"ts-node": "^8.3.0",
|
||||
"tslint": "^5.18.0",
|
||||
"typescript": "^3.5.3"
|
||||
},
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"proxy": "https://staging.quepasaalpujarra.com",
|
||||
"jest": {
|
||||
"globalSetup": "../jest-setup.js",
|
||||
"transform": {
|
||||
"^.+\\.tsx?$": "ts-jest"
|
||||
},
|
||||
"testMatch": [
|
||||
"**/*.spec.ts"
|
||||
],
|
||||
"moduleFileExtensions": [
|
||||
"ts",
|
||||
"tsx",
|
||||
"js",
|
||||
"jsx",
|
||||
"json",
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
config.ts
|
|
@ -1,124 +0,0 @@
|
|||
import {auth} from 'google-auth-library'
|
||||
import {OAuth2Client} from "google-auth-library/build/src/auth/oauth2client"
|
||||
import {atob} from 'atob'
|
||||
import { Event } from './Event.entity'
|
||||
|
||||
type GCalConfig = {
|
||||
calendarId: string
|
||||
privateKeyBase64: string
|
||||
clientEmail: string
|
||||
}
|
||||
export default class CalendarManager {
|
||||
gcalConfig: GCalConfig
|
||||
gcalBaseURL: string
|
||||
|
||||
constructor(options: {
|
||||
gcalConfig: GCalConfig,
|
||||
}) {
|
||||
this.gcalConfig = options.gcalConfig
|
||||
this.gcalBaseURL = `https://www.googleapis.com/calendar/v3/calendars/${options.gcalConfig.calendarId}`
|
||||
}
|
||||
|
||||
getClient = async () => {
|
||||
const client: any = auth.fromJSON({
|
||||
private_key: atob(this.gcalConfig.privateKeyBase64),
|
||||
client_email: this.gcalConfig.clientEmail
|
||||
})
|
||||
|
||||
client.scopes = ['https://www.googleapis.com/auth/calendar']
|
||||
await client.authorize()
|
||||
return client as OAuth2Client
|
||||
}
|
||||
|
||||
createPrimaryCalendar = async () => {
|
||||
const client = await this.getClient()
|
||||
const res = await client.request({
|
||||
method: 'post',
|
||||
url: 'https://www.googleapis.com/calendar/v3/calendars',
|
||||
data: {
|
||||
summary: 'primary events calendar'
|
||||
}
|
||||
})
|
||||
return res.data
|
||||
}
|
||||
|
||||
listCalendars = async () => {
|
||||
const client = await this.getClient()
|
||||
const res = await client.request({
|
||||
url: 'https://www.googleapis.com/calendar/v3/users/me/calendarList'
|
||||
})
|
||||
return (res.data as any).items
|
||||
}
|
||||
|
||||
listEvents = async () => {
|
||||
const client = await this.getClient()
|
||||
let eventsResponse
|
||||
try {
|
||||
eventsResponse = await client.request({
|
||||
method: 'get',
|
||||
url: `${this.gcalBaseURL}/events`,
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('Error fetching events', e)
|
||||
throw e
|
||||
}
|
||||
|
||||
|
||||
const eventsDBPromises = []
|
||||
const dbIdToGCalEvents = {}
|
||||
|
||||
eventsResponse.data.items.forEach(gCalEvent => {
|
||||
const dbId = gCalEvent.extendedProperties.private.eventId
|
||||
dbIdToGCalEvents[dbId] = gCalEvent
|
||||
eventsDBPromises.push(
|
||||
Event.findOne(dbId)
|
||||
.catch(e => {
|
||||
console.warn(`Error fetching DB event ${dbId}, will skip this one`, e)
|
||||
return null
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
const allEvents = await Promise.all(eventsDBPromises)
|
||||
const result = allEvents.filter(Boolean).map(dbEvent => ({
|
||||
...dbEvent,
|
||||
...dbIdToGCalEvents[dbEvent.id]
|
||||
}))
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
createEvent = async (event: Event): Promise<String> => {
|
||||
if (!event.id) {
|
||||
throw new Error('Event doesn\'t have id')
|
||||
}
|
||||
const calEvent = {
|
||||
// ...event.timing,
|
||||
extendedProperties: {
|
||||
private: {
|
||||
eventId: event.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let response
|
||||
try {
|
||||
response = await (await this.getClient()).request({
|
||||
method: 'post',
|
||||
url: `${this.gcalBaseURL}/events`,
|
||||
data: calEvent
|
||||
})
|
||||
console.log('Event was saved', response.data)
|
||||
} catch (e) {
|
||||
console.error('There was an error saving event with gcal', e)
|
||||
throw e
|
||||
}
|
||||
|
||||
if (!(response.data && response.data.id)) {
|
||||
throw new Error(`Could not save event on gcal. Event id: ${event.id}`)
|
||||
}
|
||||
|
||||
return response.data.id
|
||||
}
|
||||
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
{
|
||||
"rules": {
|
||||
// -- Strict errors --
|
||||
// These lint rules are likely always a good idea.
|
||||
|
||||
// Force function overloads to be declared together. This ensures readers understand APIs.
|
||||
"adjacent-overload-signatures": true,
|
||||
|
||||
// Do not allow the subtle/obscure comma operator.
|
||||
"ban-comma-operator": true,
|
||||
|
||||
// Do not allow internal modules or namespaces . These are deprecated in favor of ES6 modules.
|
||||
"no-namespace": true,
|
||||
|
||||
// Do not allow parameters to be reassigned. To avoid bugs, developers should instead assign new values to new vars.
|
||||
"no-parameter-reassignment": true,
|
||||
|
||||
// Force the use of ES6-style imports instead of /// <reference path=> imports.
|
||||
"no-reference": true,
|
||||
"semicolon": [true, "never"],
|
||||
|
||||
// Do not allow type assertions that do nothing. This is a big warning that the developer may not understand the
|
||||
// code currently being edited (they may be incorrectly handling a different type case that does not exist).
|
||||
"no-unnecessary-type-assertion": true,
|
||||
|
||||
// Disallow nonsensical label usage.
|
||||
"label-position": true,
|
||||
|
||||
// Disallows the (often typo) syntax if (var1 = var2). Replace with if (var2) { var1 = var2 }.
|
||||
"no-conditional-assignment": true,
|
||||
|
||||
// Disallows constructors for primitive types (e.g. new Number('123'), though Number('123') is still allowed).
|
||||
"no-construct": true,
|
||||
|
||||
// Do not allow super() to be called twice in a constructor.
|
||||
"no-duplicate-super": true,
|
||||
|
||||
// Do not allow the same case to appear more than once in a switch block.
|
||||
"no-duplicate-switch-case": true,
|
||||
|
||||
// Do not allow a variable to be declared more than once in the same block. Consider function parameters in this
|
||||
// rule.
|
||||
"no-duplicate-variable": [true, "check-parameters"],
|
||||
|
||||
// Disallows a variable definition in an inner scope from shadowing a variable in an outer scope. Developers should
|
||||
// instead use a separate variable name.
|
||||
"no-shadowed-variable": true,
|
||||
|
||||
// Empty blocks are almost never needed. Allow the one general exception: empty catch blocks.
|
||||
"no-empty": [true, "allow-empty-catch"],
|
||||
|
||||
// Functions must either be handled directly (e.g. with a catch() handler) or returned to another function.
|
||||
// This is a major source of errors in Cloud Functions and the team strongly recommends leaving this rule on.
|
||||
"no-floating-promises": true,
|
||||
|
||||
// Do not allow any imports for modules that are not in package.json. These will almost certainly fail when
|
||||
// deployed.
|
||||
"no-implicit-dependencies": true,
|
||||
|
||||
// The 'this' keyword can only be used inside of classes.
|
||||
"no-invalid-this": true,
|
||||
|
||||
// Do not allow strings to be thrown because they will not include stack traces. Throw Errors instead.
|
||||
"no-string-throw": true,
|
||||
|
||||
// Disallow control flow statements, such as return, continue, break, and throw in finally blocks.
|
||||
"no-unsafe-finally": true,
|
||||
|
||||
// Do not allow variables to be used before they are declared.
|
||||
"no-use-before-declare": true,
|
||||
|
||||
// Expressions must always return a value. Avoids common errors like const myValue = functionReturningVoid();
|
||||
"no-void-expression": [true, "ignore-arrow-function-shorthand"],
|
||||
|
||||
// Disallow duplicate imports in the same file.
|
||||
"no-duplicate-imports": true,
|
||||
|
||||
|
||||
// -- Strong Warnings --
|
||||
// These rules should almost never be needed, but may be included due to legacy code.
|
||||
// They are left as a warning to avoid frustration with blocked deploys when the developer
|
||||
// understand the warning and wants to deploy anyway.
|
||||
|
||||
// Warn when an empty interface is defined. These are generally not useful.
|
||||
"no-empty-interface": {"severity": "warning"},
|
||||
|
||||
// Warn when an import will have side effects.
|
||||
"no-import-side-effect": {"severity": "warning"},
|
||||
|
||||
// Warn when variables are defined with var. Var has subtle meaning that can lead to bugs. Strongly prefer const for
|
||||
// most values and let for values that will change.
|
||||
"no-var-keyword": {"severity": "warning"},
|
||||
|
||||
// Prefer === and !== over == and !=. The latter operators support overloads that are often accidental.
|
||||
"triple-equals": {"severity": "warning"},
|
||||
|
||||
// Warn when using deprecated APIs.
|
||||
"deprecation": {"severity": "warning"},
|
||||
|
||||
// -- Light Warnigns --
|
||||
// These rules are intended to help developers use better style. Simpler code has fewer bugs. These would be "info"
|
||||
// if TSLint supported such a level.
|
||||
|
||||
// prefer for( ... of ... ) to an index loop when the index is only used to fetch an object from an array.
|
||||
// (Even better: check out utils like .map if transforming an array!)
|
||||
"prefer-for-of": {"severity": "warning"},
|
||||
|
||||
// Warns if function overloads could be unified into a single function with optional or rest parameters.
|
||||
"unified-signatures": {"severity": "warning"},
|
||||
|
||||
// Prefer const for values that will not change. This better documents code.
|
||||
"prefer-const": {"severity": "warning"},
|
||||
|
||||
// Multi-line object liiterals and function calls should have a trailing comma. This helps avoid merge conflicts.
|
||||
"trailing-comma": {"severity": "warning"}
|
||||
},
|
||||
|
||||
"defaultSeverity": "error"
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import SessionManager, { SessionAlreadyValidatedError } from "./SessionManager"
|
||||
import { User } from "./User.entity"
|
||||
import { PostOffice } from "../post_office"
|
||||
import {GQL} from "../../../@types"
|
||||
import {GQL} from "../../@types"
|
||||
import {Context} from "../@types/graphql-utils"
|
||||
|
||||
interface Dependencies {
|
|
@ -4,7 +4,7 @@ import {
|
|||
EventOccurrence
|
||||
} from "../Calendar/Event.entity"
|
||||
import { Context, ResolverMap } from "../@types/graphql-utils"
|
||||
import {GQL} from "../../../@types"
|
||||
import {GQL} from "../../@types"
|
||||
const resolvers: ResolverMap = {
|
||||
Query: {
|
||||
event: (_, req: GQL.IEventOnQueryArguments) => {
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"typeRoots": ["src/@types"],
|
||||
"typeRoots": ["src/@types", "node_modules/@types"],
|
||||
"lib": ["es2015", "esnext.asynciterable"],
|
||||
"module": "commonjs",
|
||||
"noImplicitReturns": true,
|
||||
"outDir": "lib",
|
||||
"outDir": "../lib",
|
||||
"sourceMap": true,
|
||||
"target": "es6",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
|
@ -14,12 +14,8 @@
|
|||
"types": ["node", "jest"]
|
||||
},
|
||||
"compileOnSave": true,
|
||||
"include": [
|
||||
"src",
|
||||
"migrations"
|
||||
],
|
||||
"files": [
|
||||
"../@types/index.d.ts", "../node_modules/@types/node/ts3.2/index.d.ts"
|
||||
"../@types/index.d.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"**/*.spec.ts", "node_modules", "__tests__"
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"typeRoots": ["src/@types", "node_modules/@types"],
|
||||
"lib": ["es2015", "esnext.asynciterable"],
|
||||
"module": "commonjs",
|
||||
"noImplicitReturns": true,
|
||||
"outDir": "lib",
|
||||
"sourceMap": true,
|
||||
"target": "es6",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"types": ["node", "jest"]
|
||||
},
|
||||
"compileOnSave": true,
|
||||
"include": [
|
||||
"migrations"
|
||||
],
|
||||
"files": [
|
||||
"@types/index.d.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"**/*.spec.ts", "node_modules", "__tests__"
|
||||
]}
|
||||
|
||||
|
||||
rm ./Auth/authHttpHandlers.js ./Auth/authResolvers.js ./Auth/Session.entity.js ./Auth/SessionManager.js ./Auth/User.entity.js ./Auth/userResolvers.js ./Calendar/Event.entity.js ./config.js ./Events/eventsResolvers.js ./graphql.js ./index.js ./ormconfig.js ./post_office.js
|
134
tslint.json
134
tslint.json
|
@ -1,21 +1,119 @@
|
|||
{
|
||||
"extends": [
|
||||
"tslint:recommended"
|
||||
],
|
||||
"linterOptions": {
|
||||
"exclude": [
|
||||
"config/**/*.js",
|
||||
"node_modules/**/*.ts",
|
||||
"coverage/lcov-report/*.js"
|
||||
]
|
||||
},
|
||||
"rules": {
|
||||
"member-access": false,
|
||||
"object-literal-sort-keys":false,
|
||||
"no-console": false,
|
||||
"interface-name" : false,
|
||||
"no-empty-interface" : false,
|
||||
"max-classes-per-file" : false,
|
||||
"semicolon": [true, "never"]
|
||||
}
|
||||
// -- Strict errors --
|
||||
// These lint rules are likely always a good idea.
|
||||
|
||||
// Force function overloads to be declared together. This ensures readers understand APIs.
|
||||
"adjacent-overload-signatures": true,
|
||||
|
||||
// Do not allow the subtle/obscure comma operator.
|
||||
"ban-comma-operator": true,
|
||||
|
||||
// Do not allow internal modules or namespaces . These are deprecated in favor of ES6 modules.
|
||||
"no-namespace": true,
|
||||
|
||||
// Do not allow parameters to be reassigned. To avoid bugs, developers should instead assign new values to new vars.
|
||||
"no-parameter-reassignment": true,
|
||||
|
||||
// Force the use of ES6-style imports instead of /// <reference path=> imports.
|
||||
"no-reference": true,
|
||||
"semicolon": [true, "never"],
|
||||
|
||||
// Do not allow type assertions that do nothing. This is a big warning that the developer may not understand the
|
||||
// code currently being edited (they may be incorrectly handling a different type case that does not exist).
|
||||
"no-unnecessary-type-assertion": true,
|
||||
|
||||
// Disallow nonsensical label usage.
|
||||
"label-position": true,
|
||||
|
||||
// Disallows the (often typo) syntax if (var1 = var2). Replace with if (var2) { var1 = var2 }.
|
||||
"no-conditional-assignment": true,
|
||||
|
||||
// Disallows constructors for primitive types (e.g. new Number('123'), though Number('123') is still allowed).
|
||||
"no-construct": true,
|
||||
|
||||
// Do not allow super() to be called twice in a constructor.
|
||||
"no-duplicate-super": true,
|
||||
|
||||
// Do not allow the same case to appear more than once in a switch block.
|
||||
"no-duplicate-switch-case": true,
|
||||
|
||||
// Do not allow a variable to be declared more than once in the same block. Consider function parameters in this
|
||||
// rule.
|
||||
"no-duplicate-variable": [true, "check-parameters"],
|
||||
|
||||
// Disallows a variable definition in an inner scope from shadowing a variable in an outer scope. Developers should
|
||||
// instead use a separate variable name.
|
||||
"no-shadowed-variable": true,
|
||||
|
||||
// Empty blocks are almost never needed. Allow the one general exception: empty catch blocks.
|
||||
"no-empty": [true, "allow-empty-catch"],
|
||||
|
||||
// Functions must either be handled directly (e.g. with a catch() handler) or returned to another function.
|
||||
// This is a major source of errors in Cloud Functions and the team strongly recommends leaving this rule on.
|
||||
"no-floating-promises": true,
|
||||
|
||||
// Do not allow any imports for modules that are not in package.json. These will almost certainly fail when
|
||||
// deployed.
|
||||
"no-implicit-dependencies": true,
|
||||
|
||||
// The 'this' keyword can only be used inside of classes.
|
||||
"no-invalid-this": true,
|
||||
|
||||
// Do not allow strings to be thrown because they will not include stack traces. Throw Errors instead.
|
||||
"no-string-throw": true,
|
||||
|
||||
// Disallow control flow statements, such as return, continue, break, and throw in finally blocks.
|
||||
"no-unsafe-finally": true,
|
||||
|
||||
// Do not allow variables to be used before they are declared.
|
||||
"no-use-before-declare": true,
|
||||
|
||||
// Expressions must always return a value. Avoids common errors like const myValue = functionReturningVoid();
|
||||
"no-void-expression": [true, "ignore-arrow-function-shorthand"],
|
||||
|
||||
// Disallow duplicate imports in the same file.
|
||||
"no-duplicate-imports": true,
|
||||
|
||||
|
||||
// -- Strong Warnings --
|
||||
// These rules should almost never be needed, but may be included due to legacy code.
|
||||
// They are left as a warning to avoid frustration with blocked deploys when the developer
|
||||
// understand the warning and wants to deploy anyway.
|
||||
|
||||
// Warn when an empty interface is defined. These are generally not useful.
|
||||
"no-empty-interface": {"severity": "warning"},
|
||||
|
||||
// Warn when an import will have side effects.
|
||||
"no-import-side-effect": {"severity": "warning"},
|
||||
|
||||
// Warn when variables are defined with var. Var has subtle meaning that can lead to bugs. Strongly prefer const for
|
||||
// most values and let for values that will change.
|
||||
"no-var-keyword": {"severity": "warning"},
|
||||
|
||||
// Prefer === and !== over == and !=. The latter operators support overloads that are often accidental.
|
||||
"triple-equals": {"severity": "warning"},
|
||||
|
||||
// Warn when using deprecated APIs.
|
||||
"deprecation": {"severity": "warning"},
|
||||
|
||||
// -- Light Warnigns --
|
||||
// These rules are intended to help developers use better style. Simpler code has fewer bugs. These would be "info"
|
||||
// if TSLint supported such a level.
|
||||
|
||||
// prefer for( ... of ... ) to an index loop when the index is only used to fetch an object from an array.
|
||||
// (Even better: check out utils like .map if transforming an array!)
|
||||
"prefer-for-of": {"severity": "warning"},
|
||||
|
||||
// Warns if function overloads could be unified into a single function with optional or rest parameters.
|
||||
"unified-signatures": {"severity": "warning"},
|
||||
|
||||
// Prefer const for values that will not change. This better documents code.
|
||||
"prefer-const": {"severity": "warning"},
|
||||
|
||||
// Multi-line object liiterals and function calls should have a trailing comma. This helps avoid merge conflicts.
|
||||
"trailing-comma": {"severity": "warning"}
|
||||
},
|
||||
|
||||
"defaultSeverity": "error"
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
const path = require('path')
|
||||
const nodeExternals = require('webpack-node-externals');
|
||||
|
||||
|
||||
module.exports = {
|
||||
entry: './src/index.ts',
|
||||
target: 'node',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts']
|
||||
},
|
||||
output: {
|
||||
filename: 'index.js', // <-- Important
|
||||
libraryTarget: 'this' // <-- Important
|
||||
},
|
||||
externals: [nodeExternals()]
|
||||
};
|
Loading…
Reference in New Issue