Commit 5c81aaa3 authored by Sam Gluck's avatar Sam Gluck
Browse files

Mock pages for Communities, Collections, & Resources

parent 23339084
const communityBg = require('../static/img/styleguide/the-red-group-community.png');
const collectionBg = require('../static/img/styleguide/russian-revolution-collection.png');
const resourceBg = require('../static/img/styleguide/runaway-russia-resource.png');
const rand = () => Math.max(1, Math.floor(Math.random() * 20));
const description =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ornare pretium tellus ut laoreet. Donec nec pulvinar diam. Fusce sed est sed sem condimentum porttitor eget non turpis. Sed dictum pulvinar dui, iaculis ultrices orci scelerisque non. Integer a dignissim arcu. Nunc eu mi orci. Fusce ante sapien, elementum in gravida ut, porta ut erat. Suspendisse potenti.';
export const DUMMY_COMMUNITIES = [
{
id: 0,
title: 'The Red Group',
backgroundImage: communityBg,
description,
contentCounts: {
Members: rand(),
Collections: rand()
},
onButtonClick: () => alert('card btn clicked')
},
{
id: 1,
title: 'The Red Group',
backgroundImage: communityBg,
description,
contentCounts: {
Members: rand(),
Collections: rand()
},
onButtonClick: () => alert('card btn clicked')
},
{
id: 2,
title: 'The Red Group',
backgroundImage: communityBg,
description,
contentCounts: {
Members: rand(),
Collections: rand()
},
onButtonClick: () => alert('card btn clicked')
}
];
export const DUMMY_COLLECTIONS = [
{
id: 0,
title: 'The Russian Revolution',
description,
backgroundImage: collectionBg,
contentCounts: {
Followers: rand(),
Resources: rand()
},
community: DUMMY_COMMUNITIES[0],
onButtonClick: () => alert('card btn clicked')
},
{
id: 1,
title: 'The Russian Revolution',
description,
backgroundImage: collectionBg,
contentCounts: {
Followers: rand(),
Resources: rand()
},
community: DUMMY_COMMUNITIES[0],
onButtonClick: () => alert('card btn clicked')
},
{
id: 2,
title: 'The Russian Revolution',
description,
backgroundImage: collectionBg,
contentCounts: {
Followers: rand(),
Resources: rand()
},
community: DUMMY_COMMUNITIES[0],
onButtonClick: () => alert('card btn clicked')
}
];
export const DUMMY_RESOURCES = [
{
id: 0,
title:
'Runaway Russia: An American Woman Reports on the Russian Revolution',
description,
source: '#',
likesCount: rand(),
backgroundImage: resourceBg,
collection: DUMMY_COLLECTIONS[0],
onButtonClick: () => alert('card btn clicked')
},
{
id: 1,
title:
'Runaway Russia: An American Woman Reports on the Russian Revolution',
description,
source: '#',
likesCount: rand(),
backgroundImage: resourceBg,
collection: DUMMY_COLLECTIONS[0],
onButtonClick: () => alert('card btn clicked')
},
{
id: 2,
title:
'Runaway Russia: An American Woman Reports on the Russian Revolution',
description,
source: '#',
likesCount: rand(),
backgroundImage: resourceBg,
collection: DUMMY_COLLECTIONS[0],
onButtonClick: () => alert('card btn clicked')
},
{
id: 3,
title:
'Runaway Russia: An American Woman Reports on the Russian Revolution',
description,
source: '#',
likesCount: rand(),
backgroundImage: resourceBg,
collection: DUMMY_COLLECTIONS[0],
onButtonClick: () => alert('card btn clicked')
},
{
id: 4,
title:
'Runaway Russia: An American Woman Reports on the Russian Revolution',
description,
source: '#',
likesCount: rand(),
backgroundImage: resourceBg,
collection: DUMMY_COLLECTIONS[0],
onButtonClick: () => alert('card btn clicked')
},
{
id: 5,
title:
'Runaway Russia: An American Woman Reports on the Russian Revolution',
description,
source: '#',
likesCount: rand(),
backgroundImage: resourceBg,
collection: DUMMY_COLLECTIONS[0],
onButtonClick: () => alert('card btn clicked')
},
{
id: 6,
title:
'Runaway Russia: An American Woman Reports on the Russian Revolution',
description,
source: '#',
likesCount: rand(),
backgroundImage: resourceBg,
collection: DUMMY_COLLECTIONS[0],
onButtonClick: () => alert('card btn clicked')
},
{
id: 7,
title:
'Runaway Russia: An American Woman Reports on the Russian Revolution',
description,
source: '#',
likesCount: rand(),
backgroundImage: resourceBg,
collection: DUMMY_COLLECTIONS[0],
onButtonClick: () => alert('card btn clicked')
}
];
......@@ -63,7 +63,7 @@ export default new ApolloClient({
user: {
__typename: 'User',
// TODO reinstate after dev
// isAuthenticated: true,
// isAuthenticated: false,
// data: null,
isAuthenticated: true,
data: DUMMY_USER
......
import styled from '../../../themes/styled';
export default styled.div`
margin: 10px 15px;
width: 100%;
overflow: auto;
`;
......@@ -131,8 +131,14 @@ class Menu extends React.Component<MenuProps, MenuState> {
[MenuItems.notifications]: NotificationsMenuBody,
[MenuItems.search]: SearchMenuBody,
[MenuItems.user]: UserMenuBody
}[activeMenu] as React.ComponentType<{ user: User }>;
return <Component user={this.props.data.user.data as User} />;
//TODO lift prop type
}[activeMenu] as React.ComponentType<{ closeMenu: Function; user: User }>;
return (
<Component
closeMenu={this.closeMenu}
user={this.props.data.user.data as User}
/>
);
}
render() {
......
......@@ -6,6 +6,7 @@ import H6 from '../../typography/H6/H6';
import Button from '../../elements/Button/Button';
import Tag from '../../elements/Tag/Tag';
import Link from '../../elements/Link/Link';
import { withRouter } from 'react-router';
const SearchHeading = styled(H6)`
margin-block-start: 0.5em;
......@@ -37,7 +38,7 @@ neck`;
const links = ['The Russian Revolution', 'Joseph Stalin', 'Lenin'];
export default withTheme(({ theme }: any) => {
export default withRouter(withTheme(({ closeMenu, history, theme }: any) => {
return (
<div>
<SearchHeading>
......@@ -45,15 +46,18 @@ export default withTheme(({ theme }: any) => {
<span style={{ color: theme.styles.colour.primary }}>MoodleNet</span>
</SearchHeading>
<form
action="/search"
onSubmit={e => {
e.preventDefault();
const el: HTMLInputElement | null = document.getElementById(
const el: HTMLInputElement = document.getElementById(
'searchInput'
) as HTMLInputElement;
alert('search submitted: ' + (el ? el.value : ''));
history.push('/search?q=' + el.value);
closeMenu();
}}
>
<Text
name="q"
id="searchInput"
placeholder="e.g. history lessons"
button={<Button type="submit">Search</Button>}
......@@ -78,4 +82,4 @@ export default withTheme(({ theme }: any) => {
</div>
</div>
);
});
}) as any);
......@@ -99,8 +99,6 @@ class Nav extends React.Component<RouterProps, {}> {
constructor(props) {
super(props);
console.log(props);
for (const [re, item] of navMatch) {
if (re.test(props.location.pathname)) {
this.state.activeNav = item;
......
......@@ -2,6 +2,7 @@ import * as React from 'react';
import styled from '../../../themes/styled';
import Button from '../Button/Button';
import slugify from '../../../util/slugify';
import { Link } from 'react-router-dom';
export enum CardType {
......@@ -10,11 +11,11 @@ export enum CardType {
resource = 'resource'
}
const CardLink = styled(Link)`
text-decoration: none;
`;
const StyledCard = styled.div<any>`
width: 260px;
min-width: 260px;
max-width: 260px;
height: 195px;
background-color: white;
padding: 15px;
overflow: hidden;
......@@ -26,10 +27,24 @@ const StyledCard = styled.div<any>`
display: flex;
flex-direction: column;
margin: 0 15px 15px 0;
&.small {
width: 260px;
min-width: 260px;
max-width: 260px;
height: 195px;
}
&.large {
width: 450px;
min-width: 450px;
max-width: 450px;
height: 320px;
}
`;
const CardTitle = styled.div<{ small?: boolean }>`
font-size: ${props => (props.small ? 20 : 32)}px;
font-size: ${props => (props.small ? 18 : 32)}px;
font-weight: bold;
flex-grow: 1;
`;
......@@ -66,16 +81,25 @@ const ResourceBottom = styled.div`
}
`;
export type CardProps = {
const typeSlug = {
[CardType.collection]: 'collections',
[CardType.community]: 'communities',
[CardType.resource]: 'resources'
};
//TODO split this into separate props interfaces for each card type as it is becoming cumbersome and not useful for actual type checking anymore
export interface CardProps {
title: string;
backgroundImage?: string;
onButtonClick: Function;
contentCounts?: object;
type?: CardType;
type?: CardType | string;
joined?: boolean;
likesCount?: number;
source?: string;
};
large?: boolean;
link?: boolean | string;
}
export function CommunityCard({ ...props }: CardProps) {
return <Card type={CardType.community} {...props} />;
......@@ -85,72 +109,131 @@ export function CollectionCard({ ...props }: CardProps) {
return <Card type={CardType.collection} {...props} />;
}
/**
*
* @param title
* @param source
* @param link
* @param likesCount
* @param backgroundImage
* @constructor
*/
export function ResourceCard({
title,
source,
link = true,
likesCount,
backgroundImage
}: CardProps) {
//TODO lift this Outer stuff as it is shared with the Card component
const Outer = link ? CardLink : React.Fragment;
const outerProps = link
? {
to:
typeof link === 'string'
? link
: `/${typeSlug[CardType.resource]}/${slugify(title)}`
}
: {};
return (
<StyledCard backgroundImage={backgroundImage}>
<CardTitle small>{title}</CardTitle>
<ResourceBottom>
<Link to={source as any}>
<div>Source</div>
</Link>
<div>{likesCount} likes</div>
</ResourceBottom>
</StyledCard>
<Outer {...outerProps}>
<StyledCard className="small" backgroundImage={backgroundImage}>
<CardTitle small>{title}</CardTitle>
<ResourceBottom>
<Link to={source as any}>
<div>Source</div>
</Link>
<div>{likesCount} likes</div>
</ResourceBottom>
</StyledCard>
</Outer>
);
}
/**
*
* @param type
* @param joined
* @param large
* @param link
* @param title
* @param source
* @param likesCount
* @param backgroundImage
* @param onButtonClick
* @param contentCounts
* @constructor
*/
export default function Card({
title,
type = CardType.collection,
type,
joined = false,
large = false,
link = true,
title,
source,
likesCount,
backgroundImage,
onButtonClick,
contentCounts
}: CardProps) {
type = String(type);
const countKeys = contentCounts ? Object.keys(contentCounts) : [];
const buttonText = {
[CardType.collection]: ['Follow', 'Following'],
[CardType.community]: ['Join', 'Member']
}[type][+joined];
//TODO ^^^ how to not do String() on type when using as index?
const Outer = link ? CardLink : React.Fragment;
const outerProps = link
? {
to:
typeof link === 'string'
? link
: `/${typeSlug[type]}/${slugify(title)}`
}
: {};
if (title.length > 50) {
title = title.substr(0, 50) + '...';
}
return (
<StyledCard backgroundImage={backgroundImage}>
<CardTitle>{title}</CardTitle>
{contentCounts ? (
<ContentCounts>
{countKeys.map((key, i) => {
let separator: JSX.Element | null = null;
if (i < countKeys.length - 1 && countKeys.length > 1) {
separator = <span> &bull; </span>;
}
return (
<span key={i}>
{contentCounts[key]} {key}
{separator}
</span>
);
})}
</ContentCounts>
) : null}
<div>
<CardButton
type={type}
hovered={type === CardType.community}
secondary={type === CardType.collection}
onClick={onButtonClick as any /*TODO don't use any type*/}
>
{buttonText}
</CardButton>
</div>
</StyledCard>
<Outer {...outerProps}>
<StyledCard
title={title}
className={large ? 'large' : 'small'}
backgroundImage={backgroundImage}
>
<CardTitle>{title}</CardTitle>
{contentCounts ? (
<ContentCounts>
{countKeys.map((key, i) => {
let separator: JSX.Element | null = null;
if (i < countKeys.length - 1 && countKeys.length > 1) {
separator = <span> &bull; </span>;
}
return (
<span key={i}>
{contentCounts[key]} {key}
{separator}
</span>
);
})}
</ContentCounts>
) : null}
<div>
<CardButton
type={type}
hovered={type === CardType.community}
secondary={type === CardType.collection}
onClick={onButtonClick as any /*TODO don't use any type*/}
>
{buttonText}
</CardButton>
</div>
</StyledCard>
</Outer>
);
}
......@@ -7,10 +7,14 @@ import Menu from '../../components/chrome/Menu/Menu';
import Nav from '../../components/chrome/Nav/Nav';
import CommunitiesFeatured from '../../pages/communities.featured/CommunitiesFeatured';
import CommunitiesYours from '../../pages/communities.yours/CommunitiesYours';
import CommunitiesCommunity from '../../pages/communities.community/CommunitiesCommunity';
import CollectionsCollection from '../../pages/collections.collection/CollectionsCollection';
import ResourcesResource from '../../pages/resources.resource/ResourcesResource';
import Login from '../../pages/login/Login';
import NotFound from '../../pages/not-found/NotFound';
import ProtectedRoute from './ProtectedRoute';
import Loader from '../../components/elements/Loader/Loader';
import Search from '../../pages/search/Search';
const AppInner = styled.div`
display: flex;
......@@ -40,75 +44,68 @@ export default () => (
<Router>
<AppInner>
<Switch>
<ProtectedRoute
path="/"
component={() => {
return (
<>
<Nav />
<Switch>
<Route exact path="/" component={CommunitiesFeatured} />
<Route
exact
path="/communities"
component={CommunitiesFeatured}
/>
<Route
exact
path="/communities/featured"
component={CommunitiesFeatured}
/>
<Route
path="/communities/yours"
component={CommunitiesYours}
/>
<Route
exact
path="/communities/:community"
component={({ match }) => {
return (
<div>single community: {match.params.community}</div>
);
}}
/>
<Route
exact
path="/collections"
component={() => <div>collections</div>}
/>
<Route
exact
path="/collections/featured"
component={() => <div>collections featured</div>}
/>
<Route
exact
path="/collections/yours"
component={() => <div>collections yours</div>}
/>
<Route
exact
path="/collections/following"
component={() => <div>collections following</div>}
/>
<Route
exact
path="/collections/:collection"
component={({ match }) => {
return (
<div>single collection: {match.params.community}</div>
);
}}
/>
</Switch>
<Menu />
</>
);
}}
/>
<Route exact path="/login" component={Login} />
<Route exact path="/sign-up" component={SignUp} />
<Route exact path="/sign-up/:step([12])" component={SignUp} />
<ProtectedRoute
path="/"
component={() => (
<>
<Nav />
<Switch>
<Route exact path="/" component={CommunitiesFeatured} />
<Route exact path="/search" component={Search} />
<Route
exact
path="/communities"