Skip to content

Commit

Permalink
Meetup demo (keystonejs#1082)
Browse files Browse the repository at this point in the history
* Adding Meetup demo project
  • Loading branch information
JedWatson authored and timleslie committed May 3, 2019
1 parent c79cd7e commit 061aedf
Show file tree
Hide file tree
Showing 10 changed files with 341 additions and 0 deletions.
12 changes: 12 additions & 0 deletions demo-projects/meetup/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Demo Project: Meetup

// TODO

## Running the Project.

To run this project, open your terminal and run `bolt` within the Keystone project root to install all required packages, then run `bolt start meetup` to begin running Keystone.

The Keystone Admin UI is reachable from `localhost:3000/admin`. To log in, use the following credentials:

Username: `admin@keystonejs.com`
Password: `password`
41 changes: 41 additions & 0 deletions demo-projects/meetup/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//imports for Keystone app core
const { AdminUI } = require('@keystone-alpha/admin-ui');
const { Keystone, PasswordAuthStrategy } = require('@keystone-alpha/keystone');
const { MongooseAdapter } = require('@keystone-alpha/adapter-mongoose');

const { Event, Talk, User, Rsvp } = require('./schema');

const keystone = new Keystone({
name: 'Keystone Meetup',
adapter: new MongooseAdapter(),
});

const authStrategy = keystone.createAuthStrategy({
type: PasswordAuthStrategy,
list: 'User',
});

keystone.createList('Event', Event);
keystone.createList('Rsvp', Rsvp);
keystone.createList('Talk', Talk);
keystone.createList('User', User);

const admin = new AdminUI(keystone, {
adminPath: '/admin',
authStrategy,
pages: [
{
label: 'Meetup',
children: ['Event', 'Talk'],
},
{
label: 'People',
children: ['User'],
},
],
});

module.exports = {
keystone,
admin,
};
20 changes: 20 additions & 0 deletions demo-projects/meetup/initialData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module.exports = {
User: [
{
name: 'Admin User',
email: 'admin@keystonejs.com',
isAdmin: true,
password: 'password',
},
],
Event: [
{
name: 'Keystone Launch',
},
],
Talk: [
{
name: 'Introducing Keystone 5 🎉',
},
],
};
31 changes: 31 additions & 0 deletions demo-projects/meetup/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "@keystone-alpha/demo-project-meetup",
"description": "An example KeystoneJS project showcasing a Meetup Site.",
"private": true,
"version": "0.0.0",
"author": "The KeystoneJS Development Team",
"license": "MIT",
"engines": {
"node": ">=8.4.0"
},
"scripts": {
"start": "DISABLE_LOGGING=true keystone",
"build": "next build"
},
"dependencies": {
"@keystone-alpha/adapter-mongoose": "^1.0.6",
"@keystone-alpha/admin-ui": "^3.1.0",
"@keystone-alpha/core": "^2.0.3",
"@keystone-alpha/fields": "^5.0.0",
"@keystone-alpha/fields-wysiwyg-tinymce": "^1.0.2",
"@keystone-alpha/keystone": "^3.1.0",
"@keystone-alpha/server": "^4.0.1",
"apollo-boost": "^0.3.1",
"apollo-client": "^2.5.1",
"graphql-tag": "^2.10.1",
"isomorphic-unfetch": "^3.0.0",
"next": "^8.0.3",
"react": "^16.8.6",
"react-apollo": "2.4.0"
}
}
50 changes: 50 additions & 0 deletions demo-projects/meetup/schema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const {
Checkbox,
DateTime,
Integer,
Password,
Relationship,
Select,
Text,
} = require('@keystone-alpha/fields');
const { Wysiwyg } = require('@keystone-alpha/fields-wysiwyg-tinymce');

exports.User = {
fields: {
name: { type: Text },
email: { type: Text, isUnique: true },
password: { type: Password },
isAdmin: { type: Checkbox },
talks: { type: Relationship, ref: 'Talk.speakers', many: true },
},
};

exports.Event = {
fields: {
name: { type: Text },
status: { type: Select, options: 'draft, active' },
startDate: { type: DateTime },
durationMins: { type: Integer },
description: { type: Wysiwyg },
talks: { type: Relationship, ref: 'Talk.event', many: true },
maxRSVPs: { type: Integer },
},
};

exports.Talk = {
fields: {
name: { type: Text },
event: { type: Relationship, ref: 'Event.talks' },
speakers: { type: Relationship, ref: 'User.talks', many: true },
isLightningTalk: { type: Checkbox },
description: { type: Wysiwyg },
},
};

exports.Rsvp = {
fields: {
event: { type: Relationship, ref: 'Event' },
user: { type: Relationship, ref: 'User' },
status: { type: Select, options: 'yes, no' },
},
};
33 changes: 33 additions & 0 deletions demo-projects/meetup/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const keystone = require('@keystone-alpha/core');
const { Wysiwyg } = require('@keystone-alpha/fields-wysiwyg-tinymce');
const next = require('next');

const initialData = require('./initialData');

const port = process.env.PORT || 3000;

const nextApp = next({
dir: 'site',
distDir: 'build',
dev: process.env.NODE_ENV !== 'production',
});

Promise.all([keystone.prepare({ port }), nextApp.prepare()])
.then(async ([{ server, keystone: keystoneApp }]) => {
await keystoneApp.connect();

// Initialise some data.
// NOTE: This is only for demo purposes and should not be used in production
const users = await keystoneApp.lists.User.adapter.findAll();
if (!users.length) {
await keystoneApp.createItems(initialData);
}

Wysiwyg.bindStaticMiddleware(server);
server.app.use(nextApp.getRequestHandler());
await server.start();
})
.catch(error => {
console.error(error);
process.exit(1);
});
34 changes: 34 additions & 0 deletions demo-projects/meetup/site/lib/initApollo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ApolloClient, InMemoryCache, HttpLink } from 'apollo-boost';
import fetch from 'isomorphic-unfetch';

let apolloClient = null;

function create(initialState) {
// Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient
return new ApolloClient({
connectToDevTools: process.browser,
ssrMode: !process.browser, // Disables forceFetch on the server (so queries are only run once)
link: new HttpLink({
uri: 'http://localhost:3000/admin/api', // Server URL (must be absolute)
credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers`
// Use fetch() polyfill on the server
fetch: !process.browser && fetch,
}),
cache: new InMemoryCache().restore(initialState || {}),
});
}

export default function initApollo(initialState) {
// Make sure to create a new client for every server-side request so that data
// isn't shared between connections (which would be bad)
if (!process.browser) {
return create(initialState);
}

// Reuse client on the client-side
if (!apolloClient) {
apolloClient = create(initialState);
}

return apolloClient;
}
59 changes: 59 additions & 0 deletions demo-projects/meetup/site/lib/withApollo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/* This code is lifted from the Next.js Apollo example. See
https://github.com/zeit/next.js/blob/canary/examples/with-apollo */

import React from 'react';
import initApollo from './initApollo';
import Head from 'next/head';
import { getDataFromTree } from 'react-apollo';

export default App => {
return class Apollo extends React.Component {
static displayName = 'withApollo(App)';
static async getInitialProps(ctx) {
const { Component, router } = ctx;

let appProps = {};
if (App.getInitialProps) {
appProps = await App.getInitialProps(ctx);
}

// Run all GraphQL queries in the component tree
// and extract the resulting data
const apollo = initApollo();
if (!process.browser) {
try {
// Run all GraphQL queries
await getDataFromTree(
<App {...appProps} Component={Component} router={router} apolloClient={apollo} />
);
} catch (error) {
// Prevent Apollo Client GraphQL errors from crashing SSR.
// Handle them in components via the data.error prop:
// https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-query-data-error
console.error('Error while running `getDataFromTree`', error);
}

// getDataFromTree does not call componentWillUnmount
// head side effect therefore need to be cleared manually
Head.rewind();
}

// Extract query data from the Apollo store
const apolloState = apollo.cache.extract();

return {
...appProps,
apolloState,
};
}

constructor(props) {
super(props);
this.apolloClient = initApollo(props.apolloState);
}

render() {
return <App {...this.props} apolloClient={this.apolloClient} />;
}
};
};
19 changes: 19 additions & 0 deletions demo-projects/meetup/site/pages/_app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import App, { Container } from 'next/app';
import React from 'react';
import withApollo from '../lib/withApollo';
import { ApolloProvider } from 'react-apollo';

class MyApp extends App {
render() {
const { Component, pageProps, apolloClient } = this.props;
return (
<Container>
<ApolloProvider client={apolloClient}>
<Component {...pageProps} />
</ApolloProvider>
</Container>
);
}
}

export default withApollo(MyApp);
42 changes: 42 additions & 0 deletions demo-projects/meetup/site/pages/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react';

import gql from 'graphql-tag';
import { Query } from 'react-apollo';

export default () => {
return (
<Query
query={gql`
{
allEvents {
id
name
startDate
description
talks {
name
speakers {
name
}
}
}
}
`}
>
{({ data, loading, error }) => {
if (loading) return <p>loading...</p>;
if (error) {
console.log(error);
return <p>Error!</p>;
}
return (
<ul>
{data.allEvents.map(event => (
<li key={event.id}>{event.name}</li>
))}
</ul>
);
}}
</Query>
);
};

0 comments on commit 061aedf

Please sign in to comment.