This commit is contained in:
Sukchan Lee 2017-06-21 00:09:55 +09:00
parent c4477edfaa
commit 0f15061a23
11 changed files with 308 additions and 26 deletions

View File

@ -79,7 +79,7 @@ co(function* () {
next();
})
server.use(csrf);
// server.use(csrf);
server.use(passport.initialize());
server.use(passport.session());

View File

@ -50,18 +50,10 @@ Layout.Container = ({visible, children}) => visible ? (
/* this is a temporal CSS */
Layout.Content = styled.div`
display: flex;
align-items: center;
justify-content: center;
font-size: 4rem;
line-height: 8rem;
color: ${oc.gray[5]};
border-top: 1px solid ${oc.gray[4]};
box-shadow: 3px 3px 6px rgba(0,0,0,0.10), 3px 3px 6px rgba(0,0,0,0.20);
background-color: white;
width: 100%;
height: 100%;
background: ${p => p.background ? p.background : `white`};
`;
export default Layout;

View File

@ -0,0 +1,47 @@
import React from 'react';
import styled from 'styled-components';
import oc from 'open-color';
import PropTypes from 'prop-types';
const Wrapper = styled.div`
padding : 8rem;
text-align: center;
`;
const propTypes = {
visible: PropTypes.bool,
color: PropTypes.string,
size: PropTypes.string,
}
const defaultProps = {
visible: true,
color: "blue",
size: "64"
}
const Spinner = ({visible, color, size}) => visible ? (
<Wrapper>
<svg width={size} height={size} viewBox="0 0 38 38" xmlns="http://www.w3.org/2000/svg" stroke={color}>
<g fill="none" fillRule="evenodd">
<g transform="translate(1 1)" strokeWidth="2">
<circle strokeOpacity=".5" cx="18" cy="18" r="18"/>
<path d="M36 18c0-9.94-8.06-18-18-18">
<animateTransform
attributeName="transform"
type="rotate"
from="0 18 18"
to="360 18 18"
dur="1s"
repeatCount="indefinite"/>
</path>
</g>
</g>
</svg>
</Wrapper>
) : null;
Spinner.propTypes = propTypes;
Spinner.defaultProps = defaultProps;
export default Spinner;

View File

@ -0,0 +1,164 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import oc from 'open-color';
import { media } from 'helpers/style-utils';
import StarIcon from 'react-icons/lib/md/star';
import EditIcon from 'react-icons/lib/md/edit';
const Wrapper = styled.div`
padding: 1rem;
position: relative;
overflow: hidden;
display: flex;
background: ${oc.gray[0]};
border: 1px solid ${oc.gray[2]};
transition: all .25s;
& + & {
margin-top: 1rem;
}
.actions {
position: absolute;
top: 0;
right: -3rem;
width: 3rem;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
background: ${oc.gray[1]};
border-left: 1px solid ${oc.gray[2]};
opacity: 0;
transition: all .4s;
}
&:hover {
border: 1px solid ${oc.gray[4]};
background: white;
.actions {
opacity: 1;
right: 0rem;
}
}
`;
const CircleButton = styled.div`
height: 2rem;
width: 2rem;
display: flex;
align-items: center;
justify-content: center;
margin: 0.25rem;
background: white;
border: 1px solid ${oc.gray[4]};
color: ${oc.gray[4]};
border-radius: 1rem;
font-size: 1.15rem;
&:hover {
border: 1px solid ${oc.gray[7]};
color: ${oc.gray[9]};
}
&.favorite {
${props => props.active ? `
border: 1px solid ${oc.yellow[6]};
color: ${oc.yellow[6]};
`: ''}
&:active {
border: 1px solid ${oc.yellow[6]};
color: ${oc.yellow[6]};
}
}
`;
CircleButton.propTypes = {
active: PropTypes.bool
};
const Info = styled.div`
margin-left: 1rem;
flex: 1;
display: flex;
justify-content: center;
flex-direction: column;
`;
/*
const Imsi = styled.div`
font-size: 1.25rem;
color: ${oc.gray[9]};
font-weight: 500;
`;
*/
const Sizer = styled.div`
display: inline-block;
width: 33.3%;
padding: 0.5rem;
${media.desktop`
width: 50%;
`}
${media.tablet`
width: 100%;
`}
${media.mobile`
width: 100%;
padding: 0.25rem;
`}
`;
const Card = styled.div`
position: relative;
background: white;
cursor: pointer;
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
&:hover {
box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
}
`;
const Imsi = styled.div`
padding-left: 1rem;
font-size: 1.25rem;
font-weight: 500;
line-height : 3rem;
`;
const propTypes = {
subscriber: PropTypes.shape({
imsi: PropTypes.string
}),
onOpenModify: PropTypes.func,
onOpenDelete: PropTypes.func
}
const Item = ({ subscriber, onOpenModify, onOpenDelete }) => (
<Sizer>
<Card onClick={onOpenModify}>
<Imsi>{subscriber.imsi}</Imsi>
</Card>
</Sizer>
)
Item.propTypes = propTypes;
export default Item;

View File

@ -0,0 +1,32 @@
import PropTypes from 'prop-types';
import styled from 'styled-components';
import oc from 'open-color';
import { media, transitions } from 'helpers/style-utils';
import { Layout } from 'components';
import Item from './Item';
const Wrapper = styled.div`
display: block;
${media.mobile`
margin-top: 0.25rem;
`}
`
const propTypes = {
subscribers: PropTypes.arrayOf(PropTypes.object)
}
const List = ({ subscribers }) => (
<Wrapper>
{subscribers.map(subscriber =>
<Item subscriber={subscriber} />
)}
</Wrapper>
)
List.propTypes = propTypes;
export default List;

View File

@ -0,0 +1,33 @@
import styled from 'styled-components';
import oc from 'open-color';
import PropTypes from 'prop-types';
const Search = styled.input`
width: 100%;
padding: 0.5rem;
border: 1px solid ${oc.gray[2]};
font-size: 1.5rem;
line-height: 2rem;
transition: all .25s;
&:focus {
outline: none;
border: 1px solid ${oc.pink[3]};
color: ${oc.pink[6]};
}
& + & {
margin-top: 1rem;
}
`;
Search.propTypes = {
name: PropTypes.string,
value: PropTypes.string,
placeholder: PropTypes.string,
onChange: PropTypes.func
};
export default Search;

View File

@ -0,0 +1,7 @@
import List from './List';
import Search from './Search';
export {
List,
Search
};

View File

@ -7,7 +7,9 @@ import Logout from './Base/Logout';
import Modal from './Shared/Modal';
import ThumbnailIcon from './Shared/ThumbnailIcon';
import Dimmed from './Shared/Dimmed';
import Spinner from './Shared/Spinner';
import * as Subscriber from './Subscriber';
import * as User from './User';
export {
@ -20,6 +22,8 @@ export {
Modal,
ThumbnailIcon,
Dimmed,
Spinner,
Subscriber,
User
}

View File

@ -37,13 +37,15 @@ class App extends Component {
return (
<Layout>
<Layout.Container visible={view === "subscriber"}>
<SubscriberContainer/>
<Layout.Content background="#e9ecef">
<SubscriberContainer/>
</Layout.Content>
</Layout.Container>
<Layout.Container visible={view === "pdn"}>
<PdnContainer/>
<Layout.Content><PdnContainer/></Layout.Content>
</Layout.Container>
<Layout.Container visible={view === "user"}>
<UserContainer/>
<Layout.Content><UserContainer/></Layout.Content>
</Layout.Container>
<Layout.Container visible={view === "test1"}>
<Layout.Content>{view}</Layout.Content>

View File

@ -5,6 +5,8 @@ import { connect } from 'react-redux';
import { fetchSubscribers } from 'modules/crud/subscriber';
import { select } from 'modules/crud/selectors';
import { Spinner, Subscriber } from 'components';
class SubscriberContainer extends Component {
componentWillMount() {
const { subscribers, dispatch } = this.props
@ -23,17 +25,16 @@ class SubscriberContainer extends Component {
render() {
const { subscribers } = this.props
if (subscribers.isLoading) {
return <div>
<p>loading...</p>
</div>
return <Spinner/>
} else {
return <div>
{ subscribers.data.map(subscriber =>
<li key={subscriber.imsi}>{subscriber.imsi}{subscriber.pdn[0].apn}</li>
)
}
</div>
return (
<div>
<Subscriber.Search />
<Subscriber.List subscribers={subscribers.data} />
</div>
)
}
}
}

View File

@ -20,7 +20,7 @@ html, body {
-ms-font-smoothing: subpixel-antialiased;
font-family: 'Roboto', sans-serif;
background: #f8f9fa;
background: white;
}
* {