From e13e630cd6e4fc0b1ff92098a28a770794c7bb9a Mon Sep 17 00:00:00 2001
From: gabrhr <73925454+gabrhr@users.noreply.github.com>
Date: Wed, 20 Apr 2022 10:19:29 -0500
Subject: =?UTF-8?q?A=C3=B1adir=20plantilla?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Base para front
---
front/odiparpack/app/components/.DS_Store | Bin 0 -> 8196 bytes
.../app/components/Badges/LimitedBadges.js | 27 +
.../app/components/BreadCrumb/BreadCrumb.js | 55 +
.../app/components/BreadCrumb/breadCrumb-jss.js | 29 +
.../odiparpack/app/components/Calendar/AddEvent.js | 49 +
.../app/components/Calendar/AddEventForm.js | 178 ++
.../app/components/Calendar/DetailEvent.js | 167 ++
.../app/components/Calendar/EventCalendar.js | 70 +
.../app/components/Calendar/calendar-jss.js | 118 ++
.../app/components/CardPaper/GeneralCard.js | 52 +
.../app/components/CardPaper/IdentityCard.js | 70 +
.../app/components/CardPaper/NewsCard.js | 46 +
.../app/components/CardPaper/PlayerCard.js | 66 +
.../app/components/CardPaper/PostCard.js | 142 ++
.../app/components/CardPaper/ProductCard.js | 134 ++
.../app/components/CardPaper/ProfileCard.js | 99 ++
.../app/components/CardPaper/VideoCard.js | 90 +
.../app/components/CardPaper/cardStyle-jss.js | 188 ++
front/odiparpack/app/components/Cart/Cart.js | 137 ++
front/odiparpack/app/components/Cart/cart-jss.js | 38 +
front/odiparpack/app/components/Chat/ChatHeader.js | 122 ++
front/odiparpack/app/components/Chat/ChatRoom.js | 106 ++
.../odiparpack/app/components/Chat/MessageField.js | 69 +
.../app/components/Chat/chatStyle-jss.js | 148 ++
.../app/components/Chat/svg/trigger-opaque.svg | 66 +
.../components/Chat/svg/trigger-transparent.svg | 68 +
.../app/components/Contact/AddContact.js | 82 +
.../app/components/Contact/AddContactForm.js | 273 +++
.../app/components/Contact/ContactDetail.js | 195 +++
.../app/components/Contact/ContactHeader.js | 62 +
.../app/components/Contact/ContactList.js | 112 ++
.../app/components/Contact/contact-jss.js | 282 +++
.../app/components/Counter/CounterWidget.js | 80 +
.../app/components/Divider/divider-jss.js | 70 +
front/odiparpack/app/components/Divider/index.js | 148 ++
.../app/components/Email/ComposeEmail.js | 65 +
.../app/components/Email/ComposeEmailForm.js | 262 +++
.../odiparpack/app/components/Email/EmailHeader.js | 49 +
front/odiparpack/app/components/Email/EmailList.js | 314 ++++
.../app/components/Email/EmailSidebar.js | 162 ++
front/odiparpack/app/components/Email/email-jss.js | 238 +++
front/odiparpack/app/components/Error/ErrorWrap.js | 75 +
front/odiparpack/app/components/Forms/LockForm.js | 123 ++
front/odiparpack/app/components/Forms/LoginForm.js | 147 ++
.../app/components/Forms/MaterialDropZone.js | 202 +++
.../app/components/Forms/ReduxFormMUI.js | 69 +
.../app/components/Forms/RegisterForm.js | 174 ++
front/odiparpack/app/components/Forms/ResetForm.js | 71 +
.../app/components/Forms/helpers/helpers.js | 8 +
front/odiparpack/app/components/Forms/user-jss.js | 179 ++
.../app/components/Gallery/PhotoGallery.js | 83 +
.../app/components/Gallery/ProductDetail.js | 195 +++
.../app/components/Gallery/ProductGallery.js | 152 ++
.../odiparpack/app/components/Gallery/photo-jss.js | 79 +
.../app/components/Gallery/product-jss.js | 87 +
front/odiparpack/app/components/Header/Header.js | 63 +
front/odiparpack/app/components/Header/UserMenu.js | 177 ++
.../odiparpack/app/components/Header/header-jss.js | 166 ++
.../app/components/ImageLightbox/ImageLightbox.js | 1794 ++++++++++++++++++++
.../app/components/ImageLightbox/constant.js | 39 +
.../app/components/ImageLightbox/util.js | 46 +
front/odiparpack/app/components/Loading/index.js | 20 +
.../app/components/Notification/Notification.js | 59 +
.../app/components/Pagination/Pagination.js | 189 +++
.../app/components/Panel/FloatingPanel.js | 90 +
front/odiparpack/app/components/Panel/panel-jss.js | 95 ++
.../app/components/PapperBlock/PapperBlock.js | 55 +
.../app/components/PapperBlock/papperStyle-jss.js | 58 +
front/odiparpack/app/components/Profile/About.js | 243 +++
front/odiparpack/app/components/Profile/Albums.js | 152 ++
.../app/components/Profile/Connection.js | 45 +
.../odiparpack/app/components/Profile/Favorites.js | 130 ++
.../app/components/Profile/profile-jss.js | 155 ++
front/odiparpack/app/components/Quote/Quote.js | 81 +
front/odiparpack/app/components/Rating/Rating.js | 144 ++
.../app/components/Search/SearchProduct.js | 84 +
.../odiparpack/app/components/Search/search-jss.js | 48 +
.../odiparpack/app/components/Sidebar/MainMenu.js | 130 ++
.../odiparpack/app/components/Sidebar/OtherMenu.js | 44 +
front/odiparpack/app/components/Sidebar/Sidebar.js | 122 ++
.../app/components/Sidebar/sidebar-jss.js | 205 +++
.../app/components/SocialMedia/Comment.js | 135 ++
.../odiparpack/app/components/SocialMedia/Cover.js | 103 ++
.../app/components/SocialMedia/SideSection.js | 209 +++
.../app/components/SocialMedia/Timeline.js | 177 ++
.../app/components/SocialMedia/WritePost.js | 169 ++
.../app/components/SocialMedia/jss/cover-jss.js | 55 +
.../components/SocialMedia/jss/socialMedia-jss.js | 89 +
.../app/components/SocialMedia/jss/timeline-jss.js | 95 ++
.../components/SocialMedia/jss/writePost-jss.js | 73 +
.../app/components/SourceReader/SourceReader.js | 114 ++
front/odiparpack/app/components/Tables/AdvTable.js | 206 +++
.../odiparpack/app/components/Tables/CrudTable.js | 52 +
.../app/components/Tables/CrudTableForm.js | 70 +
.../odiparpack/app/components/Tables/EmptyData.js | 14 +
.../odiparpack/app/components/Tables/TreeTable.js | 190 +++
.../components/Tables/tableParts/DatePickerCell.js | 59 +
.../components/Tables/tableParts/EditableCell.js | 86 +
.../app/components/Tables/tableParts/Form.js | 71 +
.../app/components/Tables/tableParts/MainTable.js | 104 ++
.../components/Tables/tableParts/MainTableForm.js | 97 ++
.../app/components/Tables/tableParts/Row.js | 167 ++
.../components/Tables/tableParts/RowReadOnly.js | 76 +
.../components/Tables/tableParts/SelectableCell.js | 46 +
.../components/Tables/tableParts/TableHeader.js | 74 +
.../components/Tables/tableParts/TableToolbar.js | 123 ++
.../components/Tables/tableParts/TimePickerCell.js | 66 +
.../app/components/Tables/tableParts/ToggleCell.js | 50 +
.../components/Tables/tableParts/tableStyle-jss.js | 63 +
.../app/components/TemplateSettings/ThemeThumb.js | 74 +
.../app/components/TemplateSettings/index.js | 93 +
.../components/TemplateSettings/themeStyles-jss.js | 106 ++
.../app/components/Widget/AlbumWidget.js | 55 +
.../app/components/Widget/AreaChartWidget.js | 147 ++
.../app/components/Widget/BigChartWidget.js | 142 ++
.../app/components/Widget/CarouselWidget.js | 117 ++
.../app/components/Widget/CounterGroupWidget.js | 101 ++
.../app/components/Widget/CounterIconsWidget.js | 74 +
.../odiparpack/app/components/Widget/MapWidget.js | 60 +
.../app/components/Widget/ProfileWidget.js | 54 +
.../app/components/Widget/ProgressWidget.js | 39 +
.../app/components/Widget/SliderWidget.js | 69 +
.../app/components/Widget/TableWidget.js | 124 ++
.../odiparpack/app/components/Widget/TaskWidget.js | 83 +
.../odiparpack/app/components/Widget/widget-jss.js | 294 ++++
front/odiparpack/app/components/index.js | 87 +
126 files changed, 15358 insertions(+)
create mode 100644 front/odiparpack/app/components/.DS_Store
create mode 100644 front/odiparpack/app/components/Badges/LimitedBadges.js
create mode 100644 front/odiparpack/app/components/BreadCrumb/BreadCrumb.js
create mode 100644 front/odiparpack/app/components/BreadCrumb/breadCrumb-jss.js
create mode 100644 front/odiparpack/app/components/Calendar/AddEvent.js
create mode 100644 front/odiparpack/app/components/Calendar/AddEventForm.js
create mode 100644 front/odiparpack/app/components/Calendar/DetailEvent.js
create mode 100644 front/odiparpack/app/components/Calendar/EventCalendar.js
create mode 100644 front/odiparpack/app/components/Calendar/calendar-jss.js
create mode 100644 front/odiparpack/app/components/CardPaper/GeneralCard.js
create mode 100644 front/odiparpack/app/components/CardPaper/IdentityCard.js
create mode 100644 front/odiparpack/app/components/CardPaper/NewsCard.js
create mode 100644 front/odiparpack/app/components/CardPaper/PlayerCard.js
create mode 100644 front/odiparpack/app/components/CardPaper/PostCard.js
create mode 100644 front/odiparpack/app/components/CardPaper/ProductCard.js
create mode 100644 front/odiparpack/app/components/CardPaper/ProfileCard.js
create mode 100644 front/odiparpack/app/components/CardPaper/VideoCard.js
create mode 100644 front/odiparpack/app/components/CardPaper/cardStyle-jss.js
create mode 100644 front/odiparpack/app/components/Cart/Cart.js
create mode 100644 front/odiparpack/app/components/Cart/cart-jss.js
create mode 100644 front/odiparpack/app/components/Chat/ChatHeader.js
create mode 100644 front/odiparpack/app/components/Chat/ChatRoom.js
create mode 100644 front/odiparpack/app/components/Chat/MessageField.js
create mode 100644 front/odiparpack/app/components/Chat/chatStyle-jss.js
create mode 100644 front/odiparpack/app/components/Chat/svg/trigger-opaque.svg
create mode 100644 front/odiparpack/app/components/Chat/svg/trigger-transparent.svg
create mode 100644 front/odiparpack/app/components/Contact/AddContact.js
create mode 100644 front/odiparpack/app/components/Contact/AddContactForm.js
create mode 100644 front/odiparpack/app/components/Contact/ContactDetail.js
create mode 100644 front/odiparpack/app/components/Contact/ContactHeader.js
create mode 100644 front/odiparpack/app/components/Contact/ContactList.js
create mode 100644 front/odiparpack/app/components/Contact/contact-jss.js
create mode 100644 front/odiparpack/app/components/Counter/CounterWidget.js
create mode 100644 front/odiparpack/app/components/Divider/divider-jss.js
create mode 100644 front/odiparpack/app/components/Divider/index.js
create mode 100644 front/odiparpack/app/components/Email/ComposeEmail.js
create mode 100644 front/odiparpack/app/components/Email/ComposeEmailForm.js
create mode 100644 front/odiparpack/app/components/Email/EmailHeader.js
create mode 100644 front/odiparpack/app/components/Email/EmailList.js
create mode 100644 front/odiparpack/app/components/Email/EmailSidebar.js
create mode 100644 front/odiparpack/app/components/Email/email-jss.js
create mode 100644 front/odiparpack/app/components/Error/ErrorWrap.js
create mode 100644 front/odiparpack/app/components/Forms/LockForm.js
create mode 100644 front/odiparpack/app/components/Forms/LoginForm.js
create mode 100644 front/odiparpack/app/components/Forms/MaterialDropZone.js
create mode 100644 front/odiparpack/app/components/Forms/ReduxFormMUI.js
create mode 100644 front/odiparpack/app/components/Forms/RegisterForm.js
create mode 100644 front/odiparpack/app/components/Forms/ResetForm.js
create mode 100644 front/odiparpack/app/components/Forms/helpers/helpers.js
create mode 100644 front/odiparpack/app/components/Forms/user-jss.js
create mode 100644 front/odiparpack/app/components/Gallery/PhotoGallery.js
create mode 100644 front/odiparpack/app/components/Gallery/ProductDetail.js
create mode 100644 front/odiparpack/app/components/Gallery/ProductGallery.js
create mode 100644 front/odiparpack/app/components/Gallery/photo-jss.js
create mode 100644 front/odiparpack/app/components/Gallery/product-jss.js
create mode 100644 front/odiparpack/app/components/Header/Header.js
create mode 100644 front/odiparpack/app/components/Header/UserMenu.js
create mode 100644 front/odiparpack/app/components/Header/header-jss.js
create mode 100644 front/odiparpack/app/components/ImageLightbox/ImageLightbox.js
create mode 100644 front/odiparpack/app/components/ImageLightbox/constant.js
create mode 100644 front/odiparpack/app/components/ImageLightbox/util.js
create mode 100644 front/odiparpack/app/components/Loading/index.js
create mode 100644 front/odiparpack/app/components/Notification/Notification.js
create mode 100644 front/odiparpack/app/components/Pagination/Pagination.js
create mode 100644 front/odiparpack/app/components/Panel/FloatingPanel.js
create mode 100644 front/odiparpack/app/components/Panel/panel-jss.js
create mode 100644 front/odiparpack/app/components/PapperBlock/PapperBlock.js
create mode 100644 front/odiparpack/app/components/PapperBlock/papperStyle-jss.js
create mode 100644 front/odiparpack/app/components/Profile/About.js
create mode 100644 front/odiparpack/app/components/Profile/Albums.js
create mode 100644 front/odiparpack/app/components/Profile/Connection.js
create mode 100644 front/odiparpack/app/components/Profile/Favorites.js
create mode 100644 front/odiparpack/app/components/Profile/profile-jss.js
create mode 100644 front/odiparpack/app/components/Quote/Quote.js
create mode 100644 front/odiparpack/app/components/Rating/Rating.js
create mode 100644 front/odiparpack/app/components/Search/SearchProduct.js
create mode 100644 front/odiparpack/app/components/Search/search-jss.js
create mode 100644 front/odiparpack/app/components/Sidebar/MainMenu.js
create mode 100644 front/odiparpack/app/components/Sidebar/OtherMenu.js
create mode 100644 front/odiparpack/app/components/Sidebar/Sidebar.js
create mode 100644 front/odiparpack/app/components/Sidebar/sidebar-jss.js
create mode 100644 front/odiparpack/app/components/SocialMedia/Comment.js
create mode 100644 front/odiparpack/app/components/SocialMedia/Cover.js
create mode 100644 front/odiparpack/app/components/SocialMedia/SideSection.js
create mode 100644 front/odiparpack/app/components/SocialMedia/Timeline.js
create mode 100644 front/odiparpack/app/components/SocialMedia/WritePost.js
create mode 100644 front/odiparpack/app/components/SocialMedia/jss/cover-jss.js
create mode 100644 front/odiparpack/app/components/SocialMedia/jss/socialMedia-jss.js
create mode 100644 front/odiparpack/app/components/SocialMedia/jss/timeline-jss.js
create mode 100644 front/odiparpack/app/components/SocialMedia/jss/writePost-jss.js
create mode 100644 front/odiparpack/app/components/SourceReader/SourceReader.js
create mode 100644 front/odiparpack/app/components/Tables/AdvTable.js
create mode 100644 front/odiparpack/app/components/Tables/CrudTable.js
create mode 100644 front/odiparpack/app/components/Tables/CrudTableForm.js
create mode 100644 front/odiparpack/app/components/Tables/EmptyData.js
create mode 100644 front/odiparpack/app/components/Tables/TreeTable.js
create mode 100644 front/odiparpack/app/components/Tables/tableParts/DatePickerCell.js
create mode 100644 front/odiparpack/app/components/Tables/tableParts/EditableCell.js
create mode 100644 front/odiparpack/app/components/Tables/tableParts/Form.js
create mode 100644 front/odiparpack/app/components/Tables/tableParts/MainTable.js
create mode 100644 front/odiparpack/app/components/Tables/tableParts/MainTableForm.js
create mode 100644 front/odiparpack/app/components/Tables/tableParts/Row.js
create mode 100644 front/odiparpack/app/components/Tables/tableParts/RowReadOnly.js
create mode 100644 front/odiparpack/app/components/Tables/tableParts/SelectableCell.js
create mode 100644 front/odiparpack/app/components/Tables/tableParts/TableHeader.js
create mode 100644 front/odiparpack/app/components/Tables/tableParts/TableToolbar.js
create mode 100644 front/odiparpack/app/components/Tables/tableParts/TimePickerCell.js
create mode 100644 front/odiparpack/app/components/Tables/tableParts/ToggleCell.js
create mode 100644 front/odiparpack/app/components/Tables/tableParts/tableStyle-jss.js
create mode 100644 front/odiparpack/app/components/TemplateSettings/ThemeThumb.js
create mode 100644 front/odiparpack/app/components/TemplateSettings/index.js
create mode 100644 front/odiparpack/app/components/TemplateSettings/themeStyles-jss.js
create mode 100644 front/odiparpack/app/components/Widget/AlbumWidget.js
create mode 100644 front/odiparpack/app/components/Widget/AreaChartWidget.js
create mode 100644 front/odiparpack/app/components/Widget/BigChartWidget.js
create mode 100644 front/odiparpack/app/components/Widget/CarouselWidget.js
create mode 100644 front/odiparpack/app/components/Widget/CounterGroupWidget.js
create mode 100644 front/odiparpack/app/components/Widget/CounterIconsWidget.js
create mode 100644 front/odiparpack/app/components/Widget/MapWidget.js
create mode 100644 front/odiparpack/app/components/Widget/ProfileWidget.js
create mode 100644 front/odiparpack/app/components/Widget/ProgressWidget.js
create mode 100644 front/odiparpack/app/components/Widget/SliderWidget.js
create mode 100644 front/odiparpack/app/components/Widget/TableWidget.js
create mode 100644 front/odiparpack/app/components/Widget/TaskWidget.js
create mode 100644 front/odiparpack/app/components/Widget/widget-jss.js
create mode 100644 front/odiparpack/app/components/index.js
(limited to 'front/odiparpack/app/components')
diff --git a/front/odiparpack/app/components/.DS_Store b/front/odiparpack/app/components/.DS_Store
new file mode 100644
index 0000000..a82440f
Binary files /dev/null and b/front/odiparpack/app/components/.DS_Store differ
diff --git a/front/odiparpack/app/components/Badges/LimitedBadges.js b/front/odiparpack/app/components/Badges/LimitedBadges.js
new file mode 100644
index 0000000..a0e0ecd
--- /dev/null
+++ b/front/odiparpack/app/components/Badges/LimitedBadges.js
@@ -0,0 +1,27 @@
+import React, { PureComponent } from 'react';
+import PropTypes from 'prop-types';
+import { Badge } from '@material-ui/core';
+
+class LimitedBadges extends PureComponent {
+ render() {
+ const {
+ children,
+ limit,
+ value,
+ ...rest
+ } = this.props;
+ return (
+ limit ? limit + '+' : value} {...rest}>
+ { children }
+
+ );
+ }
+}
+
+LimitedBadges.propTypes = {
+ children: PropTypes.node.isRequired,
+ value: PropTypes.number.isRequired,
+ limit: PropTypes.number.isRequired,
+};
+
+export default LimitedBadges;
diff --git a/front/odiparpack/app/components/BreadCrumb/BreadCrumb.js b/front/odiparpack/app/components/BreadCrumb/BreadCrumb.js
new file mode 100644
index 0000000..0d11753
--- /dev/null
+++ b/front/odiparpack/app/components/BreadCrumb/BreadCrumb.js
@@ -0,0 +1,55 @@
+import React, { Fragment } from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import { Link, Route } from 'react-router-dom';
+import styles from './breadCrumb-jss';
+
+const Breadcrumbs = (props) => {
+ const {
+ classes,
+ theme,
+ separator,
+ location
+ } = props;
+ return (
+
+ {
+ let parts = location.pathname.split('/');
+ const place = parts[parts.length - 1];
+ parts = parts.slice(1, parts.length - 1);
+ return (
+
+ You are here:
+
+ {
+ parts.map((part, partIndex) => {
+ const path = ['', ...parts.slice(0, partIndex + 1)].join('/');
+ return (
+
+ {part}
+ { separator }
+
+ );
+ })
+ }
+ {place}
+
+
+ );
+ }}
+ />
+
+ );
+};
+
+Breadcrumbs.propTypes = {
+ classes: PropTypes.object.isRequired,
+ location: PropTypes.object.isRequired,
+ theme: PropTypes.string.isRequired,
+ separator: PropTypes.string.isRequired,
+};
+
+export default withStyles(styles)(Breadcrumbs);
diff --git a/front/odiparpack/app/components/BreadCrumb/breadCrumb-jss.js b/front/odiparpack/app/components/BreadCrumb/breadCrumb-jss.js
new file mode 100644
index 0000000..fe2dc47
--- /dev/null
+++ b/front/odiparpack/app/components/BreadCrumb/breadCrumb-jss.js
@@ -0,0 +1,29 @@
+const styles = theme => ({
+ dark: {},
+ breadcrumbs: {
+ position: 'relative',
+ display: 'block',
+ fontSize: 12,
+ color: 'rgba(255, 255, 255, 0.5)',
+ '& p': {
+ display: 'block',
+ '& span': {
+ textTransform: 'capitalize',
+ marginLeft: 5,
+ },
+ '& a': {
+ color: theme.palette.common.white,
+ textDecoration: 'none',
+ margin: '0 5px'
+ }
+ },
+ '&$dark': {
+ color: theme.palette.grey[900],
+ '& a': {
+ color: theme.palette.grey[900]
+ }
+ }
+ }
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/Calendar/AddEvent.js b/front/odiparpack/app/components/Calendar/AddEvent.js
new file mode 100644
index 0000000..ef1f5a5
--- /dev/null
+++ b/front/odiparpack/app/components/Calendar/AddEvent.js
@@ -0,0 +1,49 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import Add from '@material-ui/icons/Add';
+import { Fab, Tooltip } from '@material-ui/core';
+import FloatingPanel from '../Panel/FloatingPanel';
+import AddEventForm from './AddEventForm';
+import styles from './calendar-jss.js';
+
+
+class AddEvent extends React.Component {
+ showResult(values) {
+ setTimeout(() => {
+ this.props.submit(values);
+ }, 500); // simulate server latency
+ }
+
+ render() {
+ const {
+ classes,
+ openForm,
+ closeForm,
+ addEvent
+ } = this.props;
+ const branch = '';
+ return (
+
+
+ addEvent()} className={classes.addBtn}>
+
+
+
+
closeForm()}>
+ this.showResult(values)} />
+
+
+ );
+ }
+}
+
+AddEvent.propTypes = {
+ classes: PropTypes.object.isRequired,
+ openForm: PropTypes.bool.isRequired,
+ addEvent: PropTypes.func.isRequired,
+ closeForm: PropTypes.func.isRequired,
+ submit: PropTypes.func.isRequired,
+};
+
+export default withStyles(styles)(AddEvent);
diff --git a/front/odiparpack/app/components/Calendar/AddEventForm.js b/front/odiparpack/app/components/Calendar/AddEventForm.js
new file mode 100644
index 0000000..9edccca
--- /dev/null
+++ b/front/odiparpack/app/components/Calendar/AddEventForm.js
@@ -0,0 +1,178 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import { KeyboardDatePicker, MuiPickersUtilsProvider } from '@material-ui/pickers';
+import MomentUtils from '@date-io/moment';
+import { reduxForm, Field } from 'redux-form/immutable';
+import { connect } from 'react-redux';
+import css from 'ba-styles/Form.scss';
+import { Button, Radio, RadioGroup, FormLabel, FormControlLabel } from '@material-ui/core';
+import { TextFieldRedux } from '../Forms/ReduxFormMUI';
+import styles from './calendar-jss';
+
+
+// validation functions
+const required = value => (value == null ? 'Required' : undefined);
+
+const DateTimePickerRow = props => {
+ const {
+ showErrorsInline,
+ dispatch,
+ input: { onChange, value },
+ meta: { touched, error },
+ ...other
+ } = props;
+
+ const showError = showErrorsInline || touched;
+ return (
+
+
+
+ );
+};
+
+DateTimePickerRow.propTypes = {
+ showErrorsInline: PropTypes.bool,
+ dispatch: PropTypes.func,
+ input: PropTypes.object.isRequired,
+ meta: PropTypes.object.isRequired,
+};
+
+const renderRadioGroup = ({ input, ...rest }) => (
+ input.onChange(value)}
+ />
+);
+
+renderRadioGroup.propTypes = {
+ input: PropTypes.object.isRequired,
+};
+
+DateTimePickerRow.defaultProps = {
+ showErrorsInline: false,
+ dispatch: () => {},
+};
+
+class AddEventForm extends React.Component {
+ state = {
+ selectedDate: new Date(),
+ }
+
+ onChangeDate = date => {
+ this.setState({ selectedDate: date });
+ }
+
+ saveRef = ref => {
+ this.ref = ref;
+ return this.ref;
+ };
+
+ render() {
+ const {
+ classes,
+ reset,
+ pristine,
+ submitting,
+ handleSubmit,
+ } = this.props;
+ const { selectedDate } = this.state;
+ return (
+
+ );
+ }
+}
+
+AddEventForm.propTypes = {
+ classes: PropTypes.object.isRequired,
+ handleSubmit: PropTypes.func.isRequired,
+ reset: PropTypes.func.isRequired,
+ pristine: PropTypes.bool.isRequired,
+ submitting: PropTypes.bool.isRequired,
+};
+
+const AddEventFormRedux = reduxForm({
+ form: 'immutableAddCalendar',
+ enableReinitialize: true,
+})(AddEventForm);
+
+const reducer = 'calendar';
+const AddEventInit = connect(
+ state => ({
+ force: state,
+ initialValues: state.getIn([reducer, 'formValues'])
+ }),
+)(AddEventFormRedux);
+
+export default withStyles(styles)(AddEventInit);
diff --git a/front/odiparpack/app/components/Calendar/DetailEvent.js b/front/odiparpack/app/components/Calendar/DetailEvent.js
new file mode 100644
index 0000000..b25d8b9
--- /dev/null
+++ b/front/odiparpack/app/components/Calendar/DetailEvent.js
@@ -0,0 +1,167 @@
+import React, { Fragment } from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import MoreVertIcon from '@material-ui/icons/MoreVert';
+import Today from '@material-ui/icons/Today';
+import { Typography, IconButton, Menu, MenuItem, Divider, Popover } from '@material-ui/core';
+import styles from './calendar-jss';
+
+
+const ITEM_HEIGHT = 48;
+
+class DetailEvent extends React.Component {
+ state = {
+ anchorElOpt: null,
+ };
+
+ handleClickOpt = event => {
+ this.setState({ anchorElOpt: event.currentTarget });
+ };
+
+ handleCloseOpt = () => {
+ this.setState({ anchorElOpt: null });
+ };
+
+ handleDeleteEvent = (event) => {
+ this.setState({ anchorElOpt: null });
+ this.props.remove(event);
+ this.props.close();
+ };
+
+ render() {
+ const getDate = date => {
+ if (date._isAMomentObject) {
+ return date.format('MMMM Do YYYY');
+ }
+ let dd = date.getDate();
+ const monthNames = [
+ 'January', 'February', 'March', 'April', 'May', 'June',
+ 'July', 'August', 'September', 'October', 'November', 'December'
+ ];
+ const mm = monthNames[date.getMonth()]; // January is 0!
+ const yyyy = date.getFullYear();
+
+ if (dd < 10) {
+ dd = '0' + dd;
+ }
+
+ const convertedDate = mm + ', ' + dd + ' ' + yyyy;
+
+ return convertedDate;
+ };
+
+ const getTime = time => {
+ if (time._isAMomentObject) {
+ return time.format('LT');
+ }
+ let h = time.getHours();
+ let m = time.getMinutes();
+
+ if (h < 10) {
+ h = '0' + h;
+ }
+
+ if (m < 10) {
+ m = '0' + m;
+ }
+
+ const convertedTime = h + ':' + m;
+ return convertedTime;
+ };
+
+ const {
+ classes,
+ anchorEl,
+ event,
+ close,
+ anchorPos
+ } = this.props;
+ const { anchorElOpt } = this.state;
+ return (
+
+
+
+
+ {event !== null
+ && (
+
+
+
+
+ {' '}
+ {event.title}
+
+
+
+Start:
+ {getDate(event.start)}
+ {' '}
+-
+ {getTime(event.start)}
+
+
+
+End:
+ {getDate(event.end)}
+ {' '}
+-
+ {getTime(event.end)}
+
+
+
+ )
+ }
+
+ );
+ }
+}
+
+DetailEvent.propTypes = {
+ classes: PropTypes.object.isRequired,
+ anchorEl: PropTypes.bool.isRequired,
+ anchorPos: PropTypes.object.isRequired,
+ event: PropTypes.object,
+ close: PropTypes.func.isRequired,
+ remove: PropTypes.func.isRequired,
+};
+
+DetailEvent.defaultProps = {
+ event: null,
+};
+
+export default withStyles(styles)(DetailEvent);
diff --git a/front/odiparpack/app/components/Calendar/EventCalendar.js b/front/odiparpack/app/components/Calendar/EventCalendar.js
new file mode 100644
index 0000000..fe7b76e
--- /dev/null
+++ b/front/odiparpack/app/components/Calendar/EventCalendar.js
@@ -0,0 +1,70 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import BigCalendar from 'react-big-calendar';
+import moment from 'moment';
+import { Paper } from '@material-ui/core';
+import styles from './calendar-jss';
+
+BigCalendar.setLocalizer(BigCalendar.momentLocalizer(moment));
+
+function Event(event) {
+ return (
+ {event.title}
+ );
+}
+
+class EventCalendar extends React.Component {
+ eventStyleGetter = event => {
+ const backgroundColor = '#' + event.hexColor;
+ const style = {
+ backgroundColor,
+ };
+ return {
+ style
+ };
+ }
+
+ render() {
+ const allViews = Object.keys(BigCalendar.Views).map(k => BigCalendar.Views[k]);
+ const {
+ classes,
+ events,
+ handleEventClick
+ } = this.props;
+ return (
+
+ handleEventClick(selectedEvent)}
+ eventPropGetter={(this.eventStyleGetter)}
+ onSelectSlot={slotInfo => console.log(
+ `selected slot: \n\nstart ${slotInfo.start.toLocaleString()} `
+ + `\nend: ${slotInfo.end.toLocaleString()}`
+ + `\naction: ${slotInfo.action}`
+ )
+ }
+ components={{
+ event: Event
+ }}
+ />
+
+ );
+ }
+}
+
+EventCalendar.propTypes = {
+ classes: PropTypes.object.isRequired,
+ events: PropTypes.array.isRequired,
+ handleEventClick: PropTypes.func.isRequired,
+};
+
+export default withStyles(styles)(EventCalendar);
diff --git a/front/odiparpack/app/components/Calendar/calendar-jss.js b/front/odiparpack/app/components/Calendar/calendar-jss.js
new file mode 100644
index 0000000..36ed0a4
--- /dev/null
+++ b/front/odiparpack/app/components/Calendar/calendar-jss.js
@@ -0,0 +1,118 @@
+import {
+ pink as red,
+ lightGreen as green,
+ lightBlue as blue,
+ deepPurple as violet,
+ orange,
+} from '@material-ui/core/colors';
+
+const styles = theme => ({
+ root: {
+ padding: 20,
+ [theme.breakpoints.down('sm')]: {
+ padding: '20px 8px'
+ },
+ },
+ calendarWrap: {
+ minHeight: 600
+ },
+ addBtn: {
+ position: 'fixed',
+ bottom: 30,
+ right: 30,
+ zIndex: 100
+ },
+ typography: {
+ margin: theme.spacing(2),
+ },
+ divider: {
+ margin: '5px 0',
+ textAlign: 'center'
+ },
+ button: {
+ margin: theme.spacing(1),
+ },
+ eventName: {
+ padding: '50px 20px 10px 30px',
+ minWidth: 400,
+ color: 'rgba(0, 0, 0, 0.7)',
+ '& svg': {
+ top: -2,
+ position: 'relative'
+ }
+ },
+ time: {
+ padding: 20
+ },
+ moreOpt: {
+ position: 'absolute',
+ top: 10,
+ right: 10
+ },
+ field: {
+ width: '100%',
+ marginBottom: 20
+ },
+ fieldBasic: {
+ width: '100%',
+ marginBottom: 20,
+ marginTop: 10
+ },
+ inlineWrap: {
+ display: 'flex',
+ flexDirection: 'row'
+ },
+ redRadio: {
+ color: red[600],
+ '& svg': {
+ borderRadius: '50%',
+ background: red[100],
+ },
+ '&$checked': {
+ color: red[500],
+ },
+ },
+ greenRadio: {
+ color: green[600],
+ '& svg': {
+ borderRadius: '50%',
+ background: green[100],
+ },
+ '&$checked': {
+ color: green[500],
+ },
+ },
+ blueRadio: {
+ color: blue[600],
+ '& svg': {
+ borderRadius: '50%',
+ background: blue[100],
+ },
+ '&$checked': {
+ color: blue[500],
+ },
+ },
+ violetRadio: {
+ color: violet[600],
+ '& svg': {
+ borderRadius: '50%',
+ background: violet[100],
+ },
+ '&$checked': {
+ color: violet[500],
+ },
+ },
+ orangeRadio: {
+ color: orange[600],
+ '& svg': {
+ borderRadius: '50%',
+ background: orange[100],
+ },
+ '&$checked': {
+ color: orange[500],
+ },
+ },
+ checked: {},
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/CardPaper/GeneralCard.js b/front/odiparpack/app/components/CardPaper/GeneralCard.js
new file mode 100644
index 0000000..85f7787
--- /dev/null
+++ b/front/odiparpack/app/components/CardPaper/GeneralCard.js
@@ -0,0 +1,52 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import FavoriteIcon from '@material-ui/icons/Favorite';
+import ShareIcon from '@material-ui/icons/Share';
+import Comment from '@material-ui/icons/Comment';
+import { Card, CardActions, CardContent, IconButton } from '@material-ui/core';
+import styles from './cardStyle-jss';
+
+
+class GeneralCard extends React.Component {
+ render() {
+ const {
+ classes,
+ children,
+ liked,
+ shared,
+ commented
+ } = this.props;
+ return (
+
+
+ {children}
+
+
+
+ 0 && classes.liked} />
+ {liked}
+
+
+ 0 && classes.shared} />
+ {shared}
+
+
+
+ {commented}
+
+
+
+ );
+ }
+}
+
+GeneralCard.propTypes = {
+ classes: PropTypes.object.isRequired,
+ children: PropTypes.node.isRequired,
+ liked: PropTypes.number.isRequired,
+ shared: PropTypes.number.isRequired,
+ commented: PropTypes.number.isRequired,
+};
+
+export default withStyles(styles)(GeneralCard);
diff --git a/front/odiparpack/app/components/CardPaper/IdentityCard.js b/front/odiparpack/app/components/CardPaper/IdentityCard.js
new file mode 100644
index 0000000..4f4bd6c
--- /dev/null
+++ b/front/odiparpack/app/components/CardPaper/IdentityCard.js
@@ -0,0 +1,70 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import LocalPhone from '@material-ui/icons/LocalPhone';
+import LocationOn from '@material-ui/icons/LocationOn';
+import {
+ Card, Typography, CardContent,
+ ListItem, ListItemText, ListItemAvatar,
+ Avatar, Divider
+} from '@material-ui/core';
+import styles from './cardStyle-jss';
+
+
+class IdentityCard extends React.Component {
+ render() {
+ const {
+ classes,
+ title,
+ name,
+ avatar,
+ phone,
+ address,
+ } = this.props;
+ return (
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+IdentityCard.propTypes = {
+ classes: PropTypes.object.isRequired,
+ title: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ avatar: PropTypes.string.isRequired,
+ phone: PropTypes.string.isRequired,
+ address: PropTypes.string.isRequired,
+};
+
+export default withStyles(styles)(IdentityCard);
diff --git a/front/odiparpack/app/components/CardPaper/NewsCard.js b/front/odiparpack/app/components/CardPaper/NewsCard.js
new file mode 100644
index 0000000..f9994d8
--- /dev/null
+++ b/front/odiparpack/app/components/CardPaper/NewsCard.js
@@ -0,0 +1,46 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import { Card, CardMedia, CardActions, CardContent, Button } from '@material-ui/core';
+import styles from './cardStyle-jss';
+
+
+class NewsCard extends React.Component {
+ render() {
+ const {
+ classes,
+ children,
+ title,
+ image,
+ } = this.props;
+ return (
+
+
+
+ {children}
+
+
+
+
+
+
+ );
+ }
+}
+
+NewsCard.propTypes = {
+ classes: PropTypes.object.isRequired,
+ children: PropTypes.node.isRequired,
+ title: PropTypes.string.isRequired,
+ image: PropTypes.string.isRequired,
+};
+
+export default withStyles(styles)(NewsCard);
diff --git a/front/odiparpack/app/components/CardPaper/PlayerCard.js b/front/odiparpack/app/components/CardPaper/PlayerCard.js
new file mode 100644
index 0000000..eabbb3a
--- /dev/null
+++ b/front/odiparpack/app/components/CardPaper/PlayerCard.js
@@ -0,0 +1,66 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import SkipPreviousIcon from '@material-ui/icons/SkipPrevious';
+import PlayArrowIcon from '@material-ui/icons/PlayArrow';
+import SkipNextIcon from '@material-ui/icons/SkipNext';
+import { Typography, Card, CardMedia, CardContent, IconButton } from '@material-ui/core';
+import styles from './cardStyle-jss';
+
+
+class PlayerCard extends React.Component {
+ state = { expanded: false };
+
+ handleExpandClick = () => {
+ this.setState({ expanded: !this.state.expanded });
+ };
+
+ render() {
+ const {
+ classes,
+ theme,
+ title,
+ artist,
+ cover,
+ } = this.props;
+
+ return (
+
+
+
+ {title}
+
+ {artist}
+
+
+
+
+ {theme.direction === 'rtl' ? : }
+
+
+
+
+
+ {theme.direction === 'rtl' ? : }
+
+
+
+
+
+ );
+ }
+}
+
+PlayerCard.propTypes = {
+ classes: PropTypes.object.isRequired,
+ theme: PropTypes.object.isRequired,
+ title: PropTypes.string.isRequired,
+ artist: PropTypes.string.isRequired,
+ cover: PropTypes.string.isRequired,
+};
+
+export default withStyles(styles, { withTheme: true })(PlayerCard);
diff --git a/front/odiparpack/app/components/CardPaper/PostCard.js b/front/odiparpack/app/components/CardPaper/PostCard.js
new file mode 100644
index 0000000..9c8f05b
--- /dev/null
+++ b/front/odiparpack/app/components/CardPaper/PostCard.js
@@ -0,0 +1,142 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import FavoriteIcon from '@material-ui/icons/Favorite';
+import ShareIcon from '@material-ui/icons/Share';
+import Comment from '@material-ui/icons/Comment';
+import MoreVertIcon from '@material-ui/icons/MoreVert';
+import {
+ Typography,
+ Card,
+ Menu,
+ MenuItem,
+ CardHeader,
+ CardMedia,
+ CardContent,
+ CardActions,
+ IconButton,
+ Avatar,
+} from '@material-ui/core';
+import styles from './cardStyle-jss';
+
+
+const optionsOpt = [
+ 'Report this post',
+ 'Hide this post',
+ 'Copy link',
+];
+
+const ITEM_HEIGHT = 48;
+
+class PostCard extends React.Component {
+ state = { anchorElOpt: null };
+
+ handleClickOpt = event => {
+ this.setState({ anchorElOpt: event.currentTarget });
+ };
+
+ handleCloseOpt = () => {
+ this.setState({ anchorElOpt: null });
+ };
+
+ render() {
+ const {
+ classes,
+ avatar,
+ name,
+ date,
+ image,
+ content,
+ liked,
+ shared,
+ commented
+ } = this.props;
+ const { anchorElOpt } = this.state;
+ return (
+
+
+ }
+ action={(
+
+
+
+ )}
+ title={name}
+ subheader={date}
+ />
+
+ { image !== ''
+ && (
+
+ )
+ }
+
+
+ {content}
+
+
+
+
+ 0 && classes.liked} />
+ {liked}
+
+
+ 0 && classes.shared} />
+ {shared}
+
+
+
+ {commented}
+
+
+
+ );
+ }
+}
+
+PostCard.propTypes = {
+ classes: PropTypes.object.isRequired,
+ avatar: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ date: PropTypes.string.isRequired,
+ image: PropTypes.string,
+ content: PropTypes.string.isRequired,
+ liked: PropTypes.number.isRequired,
+ shared: PropTypes.number.isRequired,
+ commented: PropTypes.number.isRequired,
+};
+
+PostCard.defaultProps = {
+ image: ''
+};
+
+export default withStyles(styles)(PostCard);
diff --git a/front/odiparpack/app/components/CardPaper/ProductCard.js b/front/odiparpack/app/components/CardPaper/ProductCard.js
new file mode 100644
index 0000000..967e2fd
--- /dev/null
+++ b/front/odiparpack/app/components/CardPaper/ProductCard.js
@@ -0,0 +1,134 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import { isWidthUp } from '@material-ui/core/withWidth';
+import classNames from 'classnames';
+import AddShoppingCart from '@material-ui/icons/AddShoppingCart';
+import Type from 'ba-styles/Typography.scss';
+import {
+ Typography,
+ withWidth,
+ Card,
+ IconButton,
+ Tooltip,
+ CardMedia,
+ CardActions,
+ CardContent,
+ Chip,
+ Fab,
+ Button,
+} from '@material-ui/core';
+import Rating from '../Rating/Rating';
+import styles from './cardStyle-jss';
+
+
+class ProductCard extends React.Component {
+ render() {
+ const {
+ classes,
+ discount,
+ soldout,
+ thumbnail,
+ name,
+ desc,
+ ratting,
+ price,
+ prevPrice,
+ list,
+ detailOpen,
+ addToCart,
+ width,
+ } = this.props;
+ return (
+
+
+ {discount !== '' && (
+
+ )}
+ {soldout && (
+
+ )}
+
+
+
+ {!soldout && (
+
+
+
+
+
+ )}
+
+ {name}
+
+
+ {desc}
+
+
+
+
+
+
+
+
+$
+ {price}
+
+
+ {prevPrice > 0 && (
+
+
+$
+ {prevPrice}
+
+
+ )}
+
+
+ {!soldout && (
+
+
+
+
+
+ )}
+
+
+
+ );
+ }
+}
+
+ProductCard.propTypes = {
+ classes: PropTypes.object.isRequired,
+ discount: PropTypes.string,
+ width: PropTypes.string.isRequired,
+ soldout: PropTypes.bool,
+ thumbnail: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ desc: PropTypes.string.isRequired,
+ ratting: PropTypes.number.isRequired,
+ price: PropTypes.number.isRequired,
+ prevPrice: PropTypes.number,
+ list: PropTypes.bool,
+ detailOpen: PropTypes.func,
+ addToCart: PropTypes.func,
+};
+
+ProductCard.defaultProps = {
+ discount: '',
+ soldout: false,
+ prevPrice: 0,
+ list: false,
+ detailOpen: () => (false),
+ addToCart: () => (false),
+};
+
+const ProductCardResponsive = withWidth()(ProductCard);
+export default withStyles(styles)(ProductCardResponsive);
diff --git a/front/odiparpack/app/components/CardPaper/ProfileCard.js b/front/odiparpack/app/components/CardPaper/ProfileCard.js
new file mode 100644
index 0000000..7827941
--- /dev/null
+++ b/front/odiparpack/app/components/CardPaper/ProfileCard.js
@@ -0,0 +1,99 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import Type from 'ba-styles/Typography.scss';
+import VerifiedUser from '@material-ui/icons/VerifiedUser';
+import SupervisorAccount from '@material-ui/icons/SupervisorAccount';
+import Favorite from '@material-ui/icons/Favorite';
+import PhotoLibrary from '@material-ui/icons/PhotoLibrary';
+import {
+ Typography,
+ Card,
+ CardMedia,
+ CardContent,
+ CardActions,
+ Button,
+ Avatar,
+ BottomNavigation,
+ BottomNavigationAction,
+ Divider,
+} from '@material-ui/core';
+import styles from './cardStyle-jss';
+
+
+class ProfileCard extends React.Component {
+ state = { expanded: false };
+
+ handleExpandClick = () => {
+ this.setState({ expanded: !this.state.expanded });
+ };
+
+ render() {
+ const {
+ classes,
+ cover,
+ avatar,
+ name,
+ title,
+ connection,
+ isVerified,
+ btnText
+ } = this.props;
+
+ return (
+
+
+
+
+
+ {name}
+ {isVerified && }
+
+
+ {title}
+
+
+ {connection}
+ {' '}
+connection
+
+
+
+
+
+
+ } />
+ } />
+ } />
+
+
+
+ );
+ }
+}
+
+ProfileCard.propTypes = {
+ classes: PropTypes.object.isRequired,
+ cover: PropTypes.string.isRequired,
+ avatar: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ title: PropTypes.string.isRequired,
+ connection: PropTypes.number.isRequired,
+ btnText: PropTypes.string.isRequired,
+ isVerified: PropTypes.bool
+};
+
+ProfileCard.defaultProps = {
+ isVerified: false
+};
+
+export default withStyles(styles)(ProfileCard);
diff --git a/front/odiparpack/app/components/CardPaper/VideoCard.js b/front/odiparpack/app/components/CardPaper/VideoCard.js
new file mode 100644
index 0000000..69aa60c
--- /dev/null
+++ b/front/odiparpack/app/components/CardPaper/VideoCard.js
@@ -0,0 +1,90 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import PlayArrowIcon from '@material-ui/icons/PlayArrow';
+import MoreVertIcon from '@material-ui/icons/MoreVert';
+import { red } from '@material-ui/core/colors';
+
+import { Card, CardHeader, CardMedia, IconButton, Avatar } from '@material-ui/core';
+
+const styles = theme => ({
+ playIcon: {
+ height: 38,
+ width: 38,
+ },
+ cardSocmed: {
+ maxWidth: 400,
+ },
+ media: {
+ height: 0,
+ paddingTop: '56.25%', // 16:9
+ position: 'relative',
+ },
+ avatar: {
+ backgroundColor: red[500],
+ },
+ playBtn: {
+ position: 'absolute',
+ top: '50%',
+ left: '50%',
+ width: 64,
+ height: 64,
+ transform: 'translate(-50%, -50%)',
+ '& svg': {
+ color: theme.palette.common.white,
+ fontSize: 64
+ }
+ }
+});
+
+class VideoCard extends React.Component {
+ state = { expanded: false };
+
+ handleExpandClick = () => {
+ this.setState({ expanded: !this.state.expanded });
+ };
+
+ render() {
+ const {
+ classes,
+ title,
+ cover,
+ date
+ } = this.props;
+
+ return (
+
+
+
+
+
+ R
+
+ )}
+ action={(
+
+
+
+ )}
+ title={title}
+ subheader={date}
+ />
+
+ );
+ }
+}
+
+VideoCard.propTypes = {
+ classes: PropTypes.object.isRequired,
+ title: PropTypes.string.isRequired,
+ cover: PropTypes.string.isRequired,
+ date: PropTypes.string.isRequired,
+};
+
+export default withStyles(styles)(VideoCard);
diff --git a/front/odiparpack/app/components/CardPaper/cardStyle-jss.js b/front/odiparpack/app/components/CardPaper/cardStyle-jss.js
new file mode 100644
index 0000000..df367a0
--- /dev/null
+++ b/front/odiparpack/app/components/CardPaper/cardStyle-jss.js
@@ -0,0 +1,188 @@
+import { pink, lightGreen, blueGrey as dark } from '@material-ui/core/colors';
+
+const styles = theme => ({
+ divider: {
+ margin: `${theme.spacing(3)}px 0`
+ },
+ card: {
+ minWidth: 275,
+ },
+ liked: {
+ color: pink[500]
+ },
+ shared: {
+ color: lightGreen[500]
+ },
+ num: {
+ fontSize: 14,
+ marginLeft: 5
+ },
+ rightIcon: {
+ marginLeft: 'auto',
+ display: 'flex',
+ alignItems: 'center'
+ },
+ button: {
+ marginRight: theme.spacing(1)
+ },
+ media: {
+ height: 0,
+ paddingTop: '56.25%', // 16:9
+ },
+ cardPlayer: {
+ display: 'flex',
+ justifyContent: 'space-between'
+ },
+ details: {
+ display: 'flex',
+ flexDirection: 'column',
+ },
+ content: {
+ flex: '1 0 auto',
+ },
+ cover: {
+ width: 150,
+ height: 150,
+ },
+ controls: {
+ display: 'flex',
+ alignItems: 'center',
+ paddingLeft: theme.spacing(1),
+ paddingBottom: theme.spacing(1),
+ },
+ playIcon: {
+ height: 38,
+ width: 38,
+ },
+ cardSocmed: {
+ minWidth: 275,
+ },
+ cardProduct: {
+ position: 'relative'
+ },
+ mediaProduct: {
+ height: 0,
+ paddingTop: '60.25%', // 16:9
+ },
+ rightAction: {
+ '&:not(:first-child)': {
+ marginLeft: 'auto',
+ display: 'flex',
+ alignItems: 'center'
+ }
+ },
+ floatingButtonWrap: {
+ position: 'relative',
+ paddingTop: 50
+ },
+ buttonAdd: {
+ position: 'absolute',
+ right: 20,
+ top: -20,
+ },
+ buttonAddList: {
+ display: 'none',
+ marginLeft: 10
+ },
+ title: {
+ fontSize: 20,
+ height: 30,
+ },
+ ratting: {
+ margin: '10px 0',
+ '& button': {
+ width: 24,
+ height: 24
+ }
+ },
+ status: {
+ position: 'absolute',
+ right: 0,
+ top: 0,
+ padding: 10,
+ '& > *': {
+ margin: 5
+ }
+ },
+ desc: {
+ height: 45,
+ overflow: 'hidden'
+ },
+ chipDiscount: {
+ background: theme.palette.primary.light,
+ color: theme.palette.primary.dark,
+ },
+ chipSold: {
+ background: dark[500],
+ color: theme.palette.getContrastText(dark[500]),
+ },
+ contentProfle: {
+ flex: '1 0 auto',
+ textAlign: 'center'
+ },
+ mediaProfile: {
+ height: 0,
+ paddingTop: '36.25%', // 16:9
+ },
+ actions: {
+ display: 'flex',
+ },
+ avatarBig: {
+ width: 80,
+ height: 80,
+ margin: '-56px auto 10px',
+ background: theme.palette.secondary.dark
+ },
+ name: {
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center'
+ },
+ buttonProfile: {
+ margin: 20
+ },
+ bottomLink: {
+ width: '100%',
+ },
+ price: {
+ padding: theme.spacing(2),
+ paddingBottom: 20
+ },
+ contentProfile: {
+ textAlign: 'center'
+ },
+ verified: {
+ fontSize: 16,
+ color: theme.palette.primary.main
+ },
+ cardList: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ '& $buttonAddList': {
+ display: 'inline-block'
+ },
+ '& $floatingButtonWrap': {
+ flex: 1,
+ },
+ '& $buttonAdd': {
+ display: 'none'
+ },
+ '& $status': {
+ right: 'auto',
+ left: 0,
+ },
+ '& $mediaProduct': {
+ width: 300,
+ paddingTop: '21.25%'
+ },
+ '& $price': {
+ flexDirection: 'column',
+ justifyContent: 'center',
+ '& button': {
+ marginTop: 20
+ }
+ }
+ },
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/Cart/Cart.js b/front/odiparpack/app/components/Cart/Cart.js
new file mode 100644
index 0000000..d477a51
--- /dev/null
+++ b/front/odiparpack/app/components/Cart/Cart.js
@@ -0,0 +1,137 @@
+import React, { Fragment } from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import DeleteIcon from '@material-ui/icons/Delete';
+import ShoppingCartIcon from '@material-ui/icons/ShoppingCart';
+import Type from 'ba-styles/Typography.scss';
+import {
+ Menu,
+ Typography,
+ Button,
+ ListSubheader,
+ List,
+ ListItem,
+ ListItemText,
+ ListItemSecondaryAction,
+ IconButton,
+ Divider,
+} from '@material-ui/core';
+import styles from './cart-jss';
+
+
+class Cart extends React.Component {
+ render() {
+ const {
+ classes,
+ anchorEl,
+ close,
+ dataCart,
+ removeItem,
+ totalPrice,
+ checkout
+ } = this.props;
+
+ const getCartItem = dataArray => dataArray.map((item, index) => (
+
+
+
+
+
+
+
+ removeItem(item)}>
+
+
+
+
+
+
+
+
+ ));
+ return (
+
+ );
+ }
+}
+
+Cart.propTypes = {
+ classes: PropTypes.object.isRequired,
+ dataCart: PropTypes.object.isRequired,
+ anchorEl: PropTypes.object,
+ close: PropTypes.func.isRequired,
+ removeItem: PropTypes.func.isRequired,
+ checkout: PropTypes.func.isRequired,
+ totalPrice: PropTypes.number.isRequired,
+};
+
+Cart.defaultProps = {
+ anchorEl: null,
+};
+
+export default withStyles(styles)(Cart);
diff --git a/front/odiparpack/app/components/Cart/cart-jss.js b/front/odiparpack/app/components/Cart/cart-jss.js
new file mode 100644
index 0000000..aef71cd
--- /dev/null
+++ b/front/odiparpack/app/components/Cart/cart-jss.js
@@ -0,0 +1,38 @@
+const styles = theme => ({
+ totalPrice: {
+ background: theme.palette.grey[200],
+ textAlign: 'right',
+ display: 'block'
+ },
+ cartWrap: {
+ [theme.breakpoints.up('sm')]: {
+ width: 400,
+ },
+ '&:focus': {
+ outline: 'none'
+ }
+ },
+ itemText: {
+ marginRight: 30,
+ overflow: 'hidden',
+ whiteSpace: 'nowrap',
+ textOverflow: 'ellipsis',
+ width: 220
+ },
+ cartPanel: {
+ '& figure': {
+ width: 120,
+ height: 70,
+ overflow: 'hidden',
+ '& img': {
+ maxWidth: '100%'
+ }
+ }
+ },
+ empty: {
+ textAlign: 'center',
+ padding: 20
+ }
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/Chat/ChatHeader.js b/front/odiparpack/app/components/Chat/ChatHeader.js
new file mode 100644
index 0000000..b3456e9
--- /dev/null
+++ b/front/odiparpack/app/components/Chat/ChatHeader.js
@@ -0,0 +1,122 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import MoreVertIcon from '@material-ui/icons/MoreVert';
+import ArrowBack from '@material-ui/icons/ArrowBack';
+import { Typography, AppBar, Menu, MenuItem, Avatar, IconButton, Toolbar } from '@material-ui/core';
+import styles from '../Contact/contact-jss';
+
+
+const optionsOpt = [
+ 'Delete Conversation',
+ 'Option 1',
+ 'Option 2',
+ 'Option 3',
+];
+
+const ITEM_HEIGHT = 48;
+
+class ChatHeader extends React.Component {
+ state = {
+ anchorElOpt: null,
+ };
+
+ handleClickOpt = event => {
+ this.setState({ anchorElOpt: event.currentTarget });
+ };
+
+ handleCloseOpt = () => {
+ this.setState({ anchorElOpt: null });
+ };
+
+ handleRemove = (person) => {
+ this.props.remove(person);
+ }
+
+ render() {
+ const {
+ classes,
+ chatSelected,
+ dataContact,
+ showMobileDetail,
+ hideDetail,
+ } = this.props;
+ const { anchorElOpt } = this.state;
+ return (
+
+
+ {showMobileDetail && (
+ hideDetail()}
+ className={classes.navIconHide}
+ >
+
+
+ )}
+
+
+ {dataContact.getIn([chatSelected, 'name'])}
+
+
+ {' '}
+Online
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+ChatHeader.propTypes = {
+ classes: PropTypes.object.isRequired,
+ dataContact: PropTypes.object.isRequired,
+ showMobileDetail: PropTypes.bool.isRequired,
+ hideDetail: PropTypes.func.isRequired,
+ remove: PropTypes.func.isRequired,
+ chatSelected: PropTypes.number.isRequired,
+};
+
+export default withStyles(styles)(ChatHeader);
diff --git a/front/odiparpack/app/components/Chat/ChatRoom.js b/front/odiparpack/app/components/Chat/ChatRoom.js
new file mode 100644
index 0000000..9abef89
--- /dev/null
+++ b/front/odiparpack/app/components/Chat/ChatRoom.js
@@ -0,0 +1,106 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import Send from '@material-ui/icons/Send';
+import dummyContents from 'ba-api/dummyContents';
+import Type from 'ba-styles/Typography.scss';
+import { Avatar, Typography, Paper, Tooltip, IconButton } from '@material-ui/core';
+import MessageField from './MessageField';
+import styles from './chatStyle-jss';
+
+
+class ChatRoom extends React.Component {
+ constructor() {
+ super();
+ this.state = { message: '' };
+ this.handleWrite = this.handleWrite.bind(this);
+ }
+
+ handleWrite = (e, value) => {
+ this.setState({ message: value });
+ };
+
+ resetInput = () => {
+ const ctn = document.getElementById('roomContainer');
+ this.setState({ message: '' });
+ this._field.setState({ value: '' });
+ setTimeout(() => {
+ ctn.scrollTo(0, ctn.scrollHeight);
+ }, 300);
+ }
+
+ sendMessageByEnter = (event, message) => {
+ if (event.key === 'Enter' && event.target.value !== '') {
+ this.props.sendMessage(message.__html);
+ this.resetInput();
+ }
+ }
+
+ sendMessage = message => {
+ this.props.sendMessage(message.__html);
+ this.resetInput();
+ }
+
+ render() {
+ const html = { __html: this.state.message };
+ const {
+ classes,
+ dataChat,
+ chatSelected,
+ dataContact,
+ showMobileDetail,
+ } = this.props;
+ const { message } = this.state;
+ const getChat = dataArray => dataArray.map(data => {
+ const renderHTML = { __html: data.get('message') };
+ return (
+
+
+ {data.get('from') === 'contact'
+ ?
+ :
+ }
+
+
+ );
+ });
+ return (
+
+
+ {dataChat.size > 0 ? getChat(dataChat) : ({'You haven\'t made any conversation yet'})}
+
+
+ { this._field = _field; return this._field; }}
+ placeholder="Type a message"
+ fieldType="input"
+ value={message}
+ onKeyPress={(event) => this.sendMessageByEnter(event, html)}
+ />
+
+
+ this.sendMessage(html)} aria-label="send" className={classes.sendBtn}>
+
+
+
+
+
+
+ );
+ }
+}
+
+ChatRoom.propTypes = {
+ classes: PropTypes.object.isRequired,
+ dataChat: PropTypes.object.isRequired,
+ showMobileDetail: PropTypes.bool.isRequired,
+ chatSelected: PropTypes.number.isRequired,
+ dataContact: PropTypes.object.isRequired,
+ sendMessage: PropTypes.func.isRequired,
+};
+
+export default withStyles(styles)(ChatRoom);
diff --git a/front/odiparpack/app/components/Chat/MessageField.js b/front/odiparpack/app/components/Chat/MessageField.js
new file mode 100644
index 0000000..947bed4
--- /dev/null
+++ b/front/odiparpack/app/components/Chat/MessageField.js
@@ -0,0 +1,69 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import 'ba-styles/vendors/emoji-picker-react/emoji-picker-react.css';
+
+class MessageField extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ value: props.value || '',
+ };
+
+ this.onChange = this.onChange.bind(this);
+ }
+
+ onChange(e) {
+ const { val } = this.state;
+ const { onChange } = this.props;
+ const value = e ? e.target.value : val;
+
+ this.setState({ value }, () => {
+ if (typeof onChange === 'function') {
+ onChange(e, value);
+ }
+ });
+ }
+
+ onPickerkeypress(e) {
+ if (e.keyCode === 27 || e.which === 27 || e.key === 'Escape' || e.code === 'Escape') {
+ this.closePicker();
+ }
+ }
+
+ render() {
+ const {
+ onChange,
+ fieldType,
+ ...rest
+ } = this.props;
+
+ const className = `emoji-text-field emoji-${fieldType}`;
+ const { value } = this.state;
+ const isInput = fieldType === 'input';
+ const ref = (_field) => {
+ this._field = _field;
+ return this._field;
+ };
+
+ return (
+
+ { (isInput) && () }
+ { (!isInput) && () }
+
+ );
+ }
+}
+
+MessageField.propTypes = {
+ value: PropTypes.string,
+ onChange: PropTypes.func,
+ fieldType: PropTypes.string.isRequired
+};
+
+MessageField.defaultProps = {
+ value: '',
+ onChange: () => {},
+};
+
+export default MessageField;
diff --git a/front/odiparpack/app/components/Chat/chatStyle-jss.js b/front/odiparpack/app/components/Chat/chatStyle-jss.js
new file mode 100644
index 0000000..360b170
--- /dev/null
+++ b/front/odiparpack/app/components/Chat/chatStyle-jss.js
@@ -0,0 +1,148 @@
+import { lighten } from '@material-ui/core/styles/colorManipulator';
+const styles = theme => ({
+ root: {
+ flexGrow: 1,
+ display: 'flex',
+ flexDirection: 'column',
+ position: 'relative',
+ backgroundColor: lighten(theme.palette.secondary.light, 0.9),
+ },
+ chatList: {
+ padding: 24,
+ paddingTop: 110,
+ overflow: 'auto',
+ height: 580,
+ '& li': {
+ marginBottom: theme.spacing(6),
+ display: 'flex',
+ position: 'relative',
+ '& time': {
+ position: 'absolute',
+ top: -20,
+ color: theme.palette.grey[500],
+ fontSize: 11
+ }
+ },
+ },
+ detailPopup: {
+ [theme.breakpoints.down('xs')]: {
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ zIndex: 1200,
+ width: '100%',
+ overflow: 'auto',
+ height: 575
+ }
+ },
+ talk: {
+ flex: 1,
+ '& p': {
+ marginBottom: 10,
+ position: 'relative',
+ '& span': {
+ padding: 10,
+ borderRadius: 10,
+ display: 'inline-block'
+ }
+ }
+ },
+ avatar: {},
+ from: {
+ '& time': {
+ left: 60,
+ },
+ '& $avatar': {
+ marginRight: 20
+ },
+ '& $talk': {
+ '& > p': {
+ '& span': {
+ background: theme.palette.secondary.light,
+ border: `1px solid ${lighten(theme.palette.secondary.main, 0.5)}`,
+ },
+ '&:first-child': {
+ '& span': {
+ borderTopLeftRadius: 0,
+ },
+ '&:before': {
+ content: '""',
+ borderRight: `11px solid ${lighten(theme.palette.secondary.main, 0.5)}`,
+ borderBottom: '17px solid transparent',
+ position: 'absolute',
+ left: -11,
+ top: 0
+ },
+ '&:after': {
+ content: '""',
+ borderRight: `10px solid ${theme.palette.secondary.light}`,
+ borderBottom: '15px solid transparent',
+ position: 'absolute',
+ left: -9,
+ top: 1
+ },
+ }
+ }
+ }
+ },
+ to: {
+ flexDirection: 'row-reverse',
+ '& time': {
+ right: 60,
+ },
+ '& $avatar': {
+ marginLeft: 20
+ },
+ '& $talk': {
+ textAlign: 'right',
+ '& > p': {
+ '& span': {
+ textAlign: 'left',
+ background: theme.palette.primary.light,
+ border: `1px solid ${lighten(theme.palette.primary.main, 0.5)}`,
+ },
+ '&:first-child': {
+ '& span': {
+ borderTopRightRadius: 0,
+ },
+ '&:before': {
+ content: '""',
+ borderLeft: `11px solid ${lighten(theme.palette.primary.main, 0.5)}`,
+ borderBottom: '17px solid transparent',
+ position: 'absolute',
+ right: -11,
+ top: 0
+ },
+ '&:after': {
+ content: '""',
+ borderLeft: `10px solid ${theme.palette.primary.light}`,
+ borderBottom: '15px solid transparent',
+ position: 'absolute',
+ right: -9,
+ top: 1
+ },
+ }
+ }
+ }
+ },
+ messageBox: {
+ border: 'none',
+ padding: 0,
+ outline: 'none',
+ width: '100%',
+ '&:after, &:before': {
+ display: 'none'
+ }
+ },
+ writeMessage: {
+ position: 'relative',
+ bottom: 16,
+ display: 'flex',
+ minHeight: 55,
+ margin: '0 16px',
+ alignItems: 'center',
+ padding: '0 10px',
+ }
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/Chat/svg/trigger-opaque.svg b/front/odiparpack/app/components/Chat/svg/trigger-opaque.svg
new file mode 100644
index 0000000..1540f5b
--- /dev/null
+++ b/front/odiparpack/app/components/Chat/svg/trigger-opaque.svg
@@ -0,0 +1,66 @@
+
+
+
diff --git a/front/odiparpack/app/components/Chat/svg/trigger-transparent.svg b/front/odiparpack/app/components/Chat/svg/trigger-transparent.svg
new file mode 100644
index 0000000..8555ab9
--- /dev/null
+++ b/front/odiparpack/app/components/Chat/svg/trigger-transparent.svg
@@ -0,0 +1,68 @@
+
+
+
diff --git a/front/odiparpack/app/components/Contact/AddContact.js b/front/odiparpack/app/components/Contact/AddContact.js
new file mode 100644
index 0000000..2690f1d
--- /dev/null
+++ b/front/odiparpack/app/components/Contact/AddContact.js
@@ -0,0 +1,82 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import Add from '@material-ui/icons/Add';
+import { Tooltip, Fab } from '@material-ui/core';
+import AddContactForm from './AddContactForm';
+import FloatingPanel from '../Panel/FloatingPanel';
+import styles from './contact-jss';
+
+
+class AddContact extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ img: '',
+ files: []
+ };
+ this.onDrop = this.onDrop.bind(this);
+ }
+
+ onDrop(filesVal) {
+ const { files } = this.state;
+ const filesLimit = 1;
+ let oldFiles = files;
+ oldFiles = oldFiles.concat(filesVal);
+ if (oldFiles.length > filesLimit) {
+ console.log('Cannot upload more than ' + filesLimit + ' items.');
+ } else {
+ this.setState({ img: filesVal[0] });
+ }
+ }
+
+ sendValues = (values) => {
+ const { submit } = this.props;
+ const { img } = this.state;
+ const { avatarInit } = this.props;
+ const avatar = img === null ? avatarInit : img;
+ setTimeout(() => {
+ submit(values, avatar);
+ this.setState({ img: null });
+ }, 500);
+ }
+
+ render() {
+ const {
+ classes,
+ openForm,
+ closeForm,
+ avatarInit,
+ addContact
+ } = this.props;
+ const { img } = this.state;
+ const branch = '';
+ return (
+
+
+ addContact()} className={classes.addBtn}>
+
+
+
+
+
+
+
+ );
+ }
+}
+
+AddContact.propTypes = {
+ classes: PropTypes.object.isRequired,
+ submit: PropTypes.func.isRequired,
+ addContact: PropTypes.func.isRequired,
+ openForm: PropTypes.bool.isRequired,
+ avatarInit: PropTypes.string.isRequired,
+ closeForm: PropTypes.func.isRequired,
+};
+
+export default withStyles(styles)(AddContact);
diff --git a/front/odiparpack/app/components/Contact/AddContactForm.js b/front/odiparpack/app/components/Contact/AddContactForm.js
new file mode 100644
index 0000000..0b51684
--- /dev/null
+++ b/front/odiparpack/app/components/Contact/AddContactForm.js
@@ -0,0 +1,273 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Dropzone from 'react-dropzone';
+import { withStyles } from '@material-ui/core/styles';
+import Type from 'ba-styles/Typography.scss';
+import PhotoCamera from '@material-ui/icons/PhotoCamera';
+import { connect } from 'react-redux';
+import { reduxForm, Field } from 'redux-form/immutable';
+import PermContactCalendar from '@material-ui/icons/PermContactCalendar';
+import Bookmark from '@material-ui/icons/Bookmark';
+import LocalPhone from '@material-ui/icons/LocalPhone';
+import Email from '@material-ui/icons/Email';
+import Smartphone from '@material-ui/icons/Smartphone';
+import LocationOn from '@material-ui/icons/LocationOn';
+import Work from '@material-ui/icons/Work';
+import Language from '@material-ui/icons/Language';
+import css from 'ba-styles/Form.scss';
+import { Button, Avatar, IconButton, Typography, Tooltip, InputAdornment } from '@material-ui/core';
+import { TextFieldRedux } from '../Forms/ReduxFormMUI';
+import styles from './contact-jss';
+
+
+// validation functions
+const required = value => (value == null ? 'Required' : undefined);
+const email = value => (
+ value && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)
+ ? 'Invalid email'
+ : undefined
+);
+
+class AddContactForm extends React.Component {
+ saveRef = ref => {
+ this.ref = ref;
+ return this.ref;
+ };
+
+ render() {
+ const {
+ classes,
+ reset,
+ pristine,
+ submitting,
+ handleSubmit,
+ onDrop,
+ imgAvatar
+ } = this.props;
+ let dropzoneRef;
+ const acceptedFiles = ['image/jpeg', 'image/png', 'image/bmp'];
+ const fileSizeLimit = 300000;
+ const imgPreview = img => {
+ if (typeof img !== 'string' && img !== '') {
+ return URL.createObjectURL(imgAvatar);
+ }
+ return img;
+ };
+ return (
+
+ );
+ }
+}
+
+AddContactForm.propTypes = {
+ classes: PropTypes.object.isRequired,
+ handleSubmit: PropTypes.func.isRequired,
+ reset: PropTypes.func.isRequired,
+ onDrop: PropTypes.func.isRequired,
+ pristine: PropTypes.bool.isRequired,
+ submitting: PropTypes.bool.isRequired,
+ imgAvatar: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
+};
+
+const AddContactFormRedux = reduxForm({
+ form: 'immutableAddContact',
+ enableReinitialize: true,
+})(AddContactForm);
+
+const AddContactInit = connect(
+ state => ({
+ initialValues: state.getIn(['contact', 'formValues'])
+ })
+)(AddContactFormRedux);
+
+export default withStyles(styles)(AddContactInit);
diff --git a/front/odiparpack/app/components/Contact/ContactDetail.js b/front/odiparpack/app/components/Contact/ContactDetail.js
new file mode 100644
index 0000000..f6d2dfc
--- /dev/null
+++ b/front/odiparpack/app/components/Contact/ContactDetail.js
@@ -0,0 +1,195 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import Edit from '@material-ui/icons/Edit';
+import Star from '@material-ui/icons/Star';
+import StarBorder from '@material-ui/icons/StarBorder';
+import MoreVertIcon from '@material-ui/icons/MoreVert';
+import LocalPhone from '@material-ui/icons/LocalPhone';
+import Email from '@material-ui/icons/Email';
+import Smartphone from '@material-ui/icons/Smartphone';
+import LocationOn from '@material-ui/icons/LocationOn';
+import Work from '@material-ui/icons/Work';
+import Language from '@material-ui/icons/Language';
+import {
+ List,
+ ListItem,
+ ListItemText,
+ ListItemAvatar,
+ Avatar,
+ Menu,
+ MenuItem,
+ IconButton,
+ Typography,
+ Divider,
+} from '@material-ui/core';
+import styles from './contact-jss';
+
+
+const optionsOpt = [
+ 'Block Contact',
+ 'Delete Contact',
+ 'Option 1',
+ 'Option 2',
+ 'Option 3',
+];
+
+const ITEM_HEIGHT = 48;
+
+class ContactDetail extends React.Component {
+ state = {
+ anchorElOpt: null,
+ };
+
+ handleClickOpt = event => {
+ this.setState({ anchorElOpt: event.currentTarget });
+ };
+
+ handleCloseOpt = () => {
+ this.setState({ anchorElOpt: null });
+ };
+
+ deleteContact = (item) => {
+ this.props.remove(item);
+ this.setState({ anchorElOpt: null });
+ }
+
+ render() {
+ const {
+ classes,
+ dataContact,
+ itemSelected,
+ edit,
+ favorite,
+ showMobileDetail
+ } = this.props;
+ const { anchorElOpt } = this.state;
+ return (
+
+
+
+ favorite(dataContact.get(itemSelected))}>
+ {dataContact.getIn([itemSelected, 'favorited']) ? () : }
+
+ edit(dataContact.get(itemSelected))}>
+
+
+
+
+
+
+
+
+
+ {dataContact.getIn([itemSelected, 'name'])}
+
+ {dataContact.getIn([itemSelected, 'title'])}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+ContactDetail.propTypes = {
+ classes: PropTypes.object.isRequired,
+ showMobileDetail: PropTypes.bool.isRequired,
+ dataContact: PropTypes.object.isRequired,
+ itemSelected: PropTypes.number.isRequired,
+ edit: PropTypes.func.isRequired,
+ remove: PropTypes.func.isRequired,
+ favorite: PropTypes.func.isRequired,
+};
+
+export default withStyles(styles)(ContactDetail);
diff --git a/front/odiparpack/app/components/Contact/ContactHeader.js b/front/odiparpack/app/components/Contact/ContactHeader.js
new file mode 100644
index 0000000..f17a1a4
--- /dev/null
+++ b/front/odiparpack/app/components/Contact/ContactHeader.js
@@ -0,0 +1,62 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import ArrowBack from '@material-ui/icons/ArrowBack';
+import PermContactCalendar from '@material-ui/icons/PermContactCalendar';
+import Add from '@material-ui/icons/Add';
+import { AppBar, Toolbar, Typography, Button, IconButton } from '@material-ui/core';
+import styles from './contact-jss';
+
+
+class ContactHeader extends React.Component {
+ render() {
+ const {
+ classes,
+ addContact,
+ total,
+ hideDetail,
+ showMobileDetail
+ } = this.props;
+ return (
+
+
+ {showMobileDetail && (
+ hideDetail()}
+ className={classes.navIconHide}
+ >
+
+
+ )}
+
+
+ {' '}
+Contacts (
+ {total}
+)
+
+
+
+
+ );
+ }
+}
+
+ContactHeader.propTypes = {
+ classes: PropTypes.object.isRequired,
+ showMobileDetail: PropTypes.bool.isRequired,
+ addContact: PropTypes.func.isRequired,
+ hideDetail: PropTypes.func.isRequired,
+ total: PropTypes.number.isRequired,
+};
+
+export default withStyles(styles)(ContactHeader);
diff --git a/front/odiparpack/app/components/Contact/ContactList.js b/front/odiparpack/app/components/Contact/ContactList.js
new file mode 100644
index 0000000..545687d
--- /dev/null
+++ b/front/odiparpack/app/components/Contact/ContactList.js
@@ -0,0 +1,112 @@
+import React, { Fragment } from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import SearchIcon from '@material-ui/icons/Search';
+import PermContactCalendar from '@material-ui/icons/PermContactCalendar';
+import Star from '@material-ui/icons/Star';
+import {
+ Drawer,
+ Divider,
+ List,
+ ListItem,
+ ListItemText,
+ ListItemAvatar,
+ Avatar,
+ BottomNavigation,
+ BottomNavigationAction,
+} from '@material-ui/core';
+import styles from './contact-jss';
+
+
+class ContactList extends React.Component {
+ state = {
+ filter: 'all',
+ };
+
+ handleChange = (event, value) => {
+ this.setState({ filter: value });
+ };
+
+ render() {
+ const {
+ classes,
+ dataContact,
+ itemSelected,
+ showDetail,
+ search,
+ keyword,
+ clippedRight
+ } = this.props;
+ const { filter } = this.state;
+ const favoriteData = dataContact.filter(item => item.get('favorited') === true);
+ const getItem = dataArray => dataArray.map(data => {
+ const index = dataContact.indexOf(data);
+ if (data.get('name').toLowerCase().indexOf(keyword) === -1) {
+ return false;
+ }
+ return (
+ showDetail(data)}
+ >
+
+
+
+
+
+ );
+ });
+ return (
+
+
+
+
+
+
+
+
+
+
search(event)} placeholder="Search Contact" />
+
+
+
+
+
+ {filter === 'all' ? getItem(dataContact) : getItem(favoriteData)}
+
+
+
+
+ } />
+ } />
+
+
+ );
+ }
+}
+
+ContactList.propTypes = {
+ classes: PropTypes.object.isRequired,
+ dataContact: PropTypes.object.isRequired,
+ keyword: PropTypes.string.isRequired,
+ itemSelected: PropTypes.number.isRequired,
+ showDetail: PropTypes.func.isRequired,
+ search: PropTypes.func.isRequired,
+ clippedRight: PropTypes.bool,
+};
+
+ContactList.defaultProps = {
+ clippedRight: false
+};
+
+export default withStyles(styles)(ContactList);
diff --git a/front/odiparpack/app/components/Contact/contact-jss.js b/front/odiparpack/app/components/Contact/contact-jss.js
new file mode 100644
index 0000000..6745527
--- /dev/null
+++ b/front/odiparpack/app/components/Contact/contact-jss.js
@@ -0,0 +1,282 @@
+import { amber, blue, deepPurple as purple, teal, brown, red } from '@material-ui/core/colors';
+
+const drawerWidth = 240;
+const drawerHeight = 630;
+
+const styles = theme => ({
+ root: {
+ flexGrow: 1,
+ height: drawerHeight,
+ zIndex: 1,
+ overflow: 'hidden',
+ position: 'relative',
+ [theme.breakpoints.up('sm')]: {
+ display: 'flex',
+ },
+ borderRadius: 2,
+ boxShadow: theme.shadows[2]
+ },
+ addBtn: {
+ position: 'fixed',
+ bottom: 30,
+ right: 30,
+ zIndex: 100
+ },
+ appBar: {
+ zIndex: theme.zIndex.drawer + 1,
+ background: theme.palette.secondary.main,
+ height: 64,
+ display: 'flex',
+ justifyContent: 'center',
+ '& $avatar': {
+ marginRight: 10
+ },
+ '& h2': {
+ flex: 1
+ },
+ '& $button': {
+ color: theme.palette.common.white
+ }
+ },
+ button: {
+ [theme.breakpoints.down('sm')]: {
+ display: 'none'
+ },
+ },
+ online: {
+ background: '#CDDC39'
+ },
+ bussy: {
+ background: '#EF5350'
+ },
+ idle: {
+ background: '#FFC107'
+ },
+ offline: {
+ background: '#9E9E9E'
+ },
+ status: {
+ padding: '2px 6px',
+ '& span': {
+ borderRadius: '50%',
+ display: 'inline-block',
+ marginRight: 2,
+ width: 10,
+ height: 10,
+ border: `1px solid ${theme.palette.common.white}`
+ }
+ },
+ appBarShift: {
+ marginLeft: 0,
+ width: '100%',
+ transition: theme.transitions.create(['width', 'margin'], {
+ easing: theme.transitions.easing.sharp,
+ duration: theme.transitions.duration.enteringScreen,
+ }),
+ [theme.breakpoints.up('md')]: {
+ marginLeft: drawerWidth,
+ width: `calc(100% - ${drawerWidth}px)`,
+ },
+ },
+ drawerPaper: {
+ [theme.breakpoints.up('sm')]: {
+ width: drawerWidth,
+ },
+ position: 'relative',
+ paddingBottom: 65,
+ height: drawerHeight,
+ },
+ clippedRight: {},
+ toolbar: {
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ padding: 8,
+ position: 'relative',
+ '&$clippedRight': {
+ marginTop: 66
+ }
+ },
+ content: {
+ flexGrow: 1,
+ paddingTop: 64,
+ backgroundColor: theme.palette.background.paper,
+ },
+ detailPopup: {
+ [theme.breakpoints.down('xs')]: {
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ zIndex: 1200,
+ width: '100%',
+ overflow: 'auto',
+ height: 'calc(100% - 50px)'
+ }
+ },
+ title: {
+ display: 'flex',
+ flex: 1,
+ '& svg': {
+ marginRight: 5
+ }
+ },
+ flex: {
+ flex: 1,
+ },
+ searchWrapper: {
+ fontFamily: theme.typography.fontFamily,
+ position: 'relative',
+ borderRadius: 2,
+ display: 'block',
+ background: theme.palette.grey[100]
+ },
+ search: {
+ width: 'auto',
+ height: '100%',
+ top: 0,
+ left: 20,
+ position: 'absolute',
+ pointerEvents: 'none',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ input: {
+ font: 'inherit',
+ padding: `${theme.spacing(1)}px ${theme.spacing(1)}px ${theme.spacing(1)}px ${theme.spacing(6)}px`,
+ border: 0,
+ display: 'block',
+ verticalAlign: 'middle',
+ whiteSpace: 'normal',
+ background: 'none',
+ margin: 0, // Reset for Safari
+ color: 'inherit',
+ width: '100%',
+ '&:focus': {
+ outline: 0,
+ },
+ },
+ bottomFilter: {
+ position: 'absolute',
+ width: '100%',
+ [theme.breakpoints.up('sm')]: {
+ width: 240,
+ },
+ zIndex: 2000,
+ bottom: 0,
+ left: 0,
+ background: theme.palette.grey[100],
+ borderTop: `1px solid ${theme.palette.grey[300]}`,
+ borderRight: `1px solid ${theme.palette.grey[300]}`,
+ },
+ avatar: {},
+ userName: {
+ textAlign: 'left'
+ },
+ cover: {
+ padding: 20,
+ height: 130,
+ position: 'relative',
+ background: theme.palette.primary.light,
+ width: '100%',
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'flex-start',
+ '& $avatar': {
+ boxShadow: theme.shadows[4],
+ width: 100,
+ height: 100,
+ marginRight: 20
+ },
+ },
+ opt: {
+ position: 'absolute',
+ top: 10,
+ right: 10,
+ },
+ favorite: {
+ color: amber[500]
+ },
+ redIcon: {
+ background: red[50],
+ '& svg': {
+ color: red[500]
+ }
+ },
+ brownIcon: {
+ background: brown[50],
+ '& svg': {
+ color: brown[500]
+ }
+ },
+ tealIcon: {
+ background: teal[50],
+ '& svg': {
+ color: teal[500]
+ }
+ },
+ blueIcon: {
+ background: blue[50],
+ '& svg': {
+ color: blue[500]
+ }
+ },
+ amberIcon: {
+ background: amber[50],
+ '& svg': {
+ color: amber[500]
+ }
+ },
+ purpleIcon: {
+ background: purple[50],
+ '& svg': {
+ color: purple[500]
+ }
+ },
+ field: {
+ width: '100%',
+ marginBottom: 20,
+ '& svg': {
+ color: theme.palette.grey[400],
+ fontSize: 18,
+ }
+ },
+ uploadAvatar: {
+ width: '100%',
+ height: '100%',
+ background: theme.palette.grey[200],
+ boxShadow: theme.shadows[4],
+ },
+ selected: {
+ background: theme.palette.secondary.light,
+ borderLeft: `2px solid ${theme.palette.secondary.main}`,
+ paddingLeft: 22,
+ '& h3': {
+ color: theme.palette.secondary.dark
+ }
+ },
+ hiddenDropzone: {
+ display: 'none'
+ },
+ avatarWrap: {
+ width: 100,
+ height: 100,
+ margin: '10px auto 30px',
+ position: 'relative'
+ },
+ buttonUpload: {
+ position: 'absolute',
+ top: '50%',
+ left: '50%',
+ transform: 'translate(-50%, -50%)'
+ },
+ navIconHide: {
+ marginRight: theme.spacing(1),
+ [theme.breakpoints.up('sm')]: {
+ display: 'none'
+ }
+ }
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/Counter/CounterWidget.js b/front/odiparpack/app/components/Counter/CounterWidget.js
new file mode 100644
index 0000000..f22bfb8
--- /dev/null
+++ b/front/odiparpack/app/components/Counter/CounterWidget.js
@@ -0,0 +1,80 @@
+import React, { PureComponent } from 'react';
+import PropTypes from 'prop-types';
+import CountUp from 'react-countup';
+import { withStyles } from '@material-ui/core/styles';
+import { Typography, Paper } from '@material-ui/core';
+
+const styles = theme => ({
+ root: {
+ flexGrow: 1,
+ justifyContent: 'space-between',
+ alignItems: 'flex-start',
+ padding: 10,
+ height: 190,
+ marginBottom: 6,
+ display: 'flex',
+ [theme.breakpoints.up('sm')]: {
+ height: 120,
+ marginBottom: -1,
+ alignItems: 'flex-end',
+ },
+ [theme.breakpoints.down('xs')]: {
+ flexDirection: 'column',
+ },
+ '& > *': {
+ padding: '0 5px'
+ }
+ },
+ title: {
+ color: theme.palette.common.white,
+ fontSize: 16,
+ fontWeight: 400
+ },
+ counter: {
+ color: theme.palette.common.white,
+ fontSize: 28,
+ fontWeight: 500
+ },
+ customContent: {
+ textAlign: 'right'
+ }
+});
+
+class CounterWidget extends PureComponent {
+ render() {
+ const {
+ classes,
+ color,
+ start,
+ end,
+ duration,
+ title,
+ children
+ } = this.props;
+ return (
+
+
+
+
+
+ {title}
+
+
+ {children}
+
+
+ );
+ }
+}
+
+CounterWidget.propTypes = {
+ classes: PropTypes.object.isRequired,
+ color: PropTypes.string.isRequired,
+ start: PropTypes.number.isRequired,
+ end: PropTypes.number.isRequired,
+ duration: PropTypes.number.isRequired,
+ title: PropTypes.string.isRequired,
+ children: PropTypes.node.isRequired,
+};
+
+export default withStyles(styles)(CounterWidget);
diff --git a/front/odiparpack/app/components/Divider/divider-jss.js b/front/odiparpack/app/components/Divider/divider-jss.js
new file mode 100644
index 0000000..e813b8d
--- /dev/null
+++ b/front/odiparpack/app/components/Divider/divider-jss.js
@@ -0,0 +1,70 @@
+const space = {
+ margin: '40px 0'
+};
+const styles = theme => ({
+ gradient: {
+ extend: space,
+ border: 0,
+ height: 1,
+ background: '#333',
+ backgroundImage: 'linear-gradient(to right, #fff, #8c8c8c, #fff)'
+ },
+ colorDash: {
+ border: 0,
+ extend: space,
+ borderBottom: `1px dashed ${theme.palette.grey[100]}`,
+ background: '#999'
+ },
+ shadow: {
+ height: 12,
+ extend: space,
+ border: 0,
+ boxShadow: 'inset 0 12px 12px -12px rgba(0, 0, 0, 0.5)'
+ },
+ inset: {
+ border: 0,
+ extend: space,
+ height: 0,
+ borderTop: '1px solid rgba(0, 0, 0, 0.1)',
+ borderBottom: '1px solid rgba(255, 255, 255, 0.3)'
+ },
+ flairedEdges: {
+ overflow: 'visible', /* For IE */
+ extend: space,
+ height: 30,
+ borderStyle: 'solid',
+ borderColor: theme.palette.grey[400],
+ borderWidth: '1px 0 0 0',
+ borderRadius: 20,
+ '&:before': {
+ display: 'block',
+ content: '""',
+ height: 30,
+ marginTop: -31,
+ borderStyle: 'solid',
+ borderColor: theme.palette.grey[400],
+ borderWidth: '0 0 1px 0',
+ borderRadius: 20
+ }
+ },
+ content: {
+ overflow: 'visible', /* For IE */
+ extend: space,
+ padding: 0,
+ border: 'none',
+ borderTop: `1px solid ${theme.palette.grey[400]}`,
+ color: '#333',
+ textAlign: 'center',
+ '&:after': {
+ content: 'attr(data-content)',
+ display: 'inline-block',
+ position: 'relative',
+ top: -15,
+ fontSize: 14,
+ padding: '0 0.25em',
+ background: '#FFF'
+ }
+ }
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/Divider/index.js b/front/odiparpack/app/components/Divider/index.js
new file mode 100644
index 0000000..8e5c010
--- /dev/null
+++ b/front/odiparpack/app/components/Divider/index.js
@@ -0,0 +1,148 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import styles from './divider-jss';
+
+/* Gradient Divider */
+const Gradient = props => {
+ const {
+ thin,
+ classes,
+ ...rest
+ } = props;
+ return (
+
+ );
+};
+
+Gradient.propTypes = {
+ thin: PropTypes.number,
+ classes: PropTypes.object.isRequired,
+};
+
+Gradient.defaultProps = {
+ thin: 1
+};
+
+export const GradientDivider = withStyles(styles)(Gradient);
+
+/* Dash Divider */
+
+const Dash = props => {
+ const {
+ thin,
+ classes,
+ ...rest
+ } = props;
+ return (
+
+ );
+};
+
+Dash.propTypes = {
+ classes: PropTypes.object.isRequired,
+ thin: PropTypes.number,
+};
+
+Dash.defaultProps = {
+ thin: 1
+};
+
+export const DashDivider = withStyles(styles)(Dash);
+
+/* Shadow Divider */
+
+const Shadow = props => {
+ const {
+ classes,
+ thin,
+ ...rest
+ } = props;
+ return (
+
+ );
+};
+
+Shadow.propTypes = {
+ classes: PropTypes.object.isRequired,
+ thin: PropTypes.number,
+};
+
+Shadow.defaultProps = {
+ thin: 1
+};
+
+export const ShadowDivider = withStyles(styles)(Shadow);
+
+/* Shadow Inset */
+
+const Inset = props => {
+ const {
+ classes,
+ thin,
+ ...rest
+ } = props;
+ return (
+
+ );
+};
+
+Inset.propTypes = {
+ classes: PropTypes.object.isRequired,
+ thin: PropTypes.number,
+};
+
+Inset.defaultProps = {
+ thin: 1
+};
+
+export const InsetDivider = withStyles(styles)(Inset);
+
+/* Shadow FlairedEdges */
+
+export const FlairedEdges = props => {
+ const {
+ classes,
+ thin,
+ ...rest
+ } = props;
+ return (
+
+ );
+};
+
+FlairedEdges.propTypes = {
+ classes: PropTypes.object.isRequired,
+ thin: PropTypes.number,
+};
+
+FlairedEdges.defaultProps = {
+ thin: 1
+};
+
+export const FlairedEdgesDivider = withStyles(styles)(FlairedEdges);
+
+
+export const Content = props => {
+ const {
+ classes,
+ thin,
+ content,
+ ...rest
+ } = props;
+ return (
+
+ );
+};
+
+Content.propTypes = {
+ classes: PropTypes.object.isRequired,
+ thin: PropTypes.number,
+ content: PropTypes.string.isRequired,
+};
+
+Content.defaultProps = {
+ thin: 1
+};
+
+export const ContentDivider = withStyles(styles)(Content);
diff --git a/front/odiparpack/app/components/Email/ComposeEmail.js b/front/odiparpack/app/components/Email/ComposeEmail.js
new file mode 100644
index 0000000..8d06ebd
--- /dev/null
+++ b/front/odiparpack/app/components/Email/ComposeEmail.js
@@ -0,0 +1,65 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import Add from '@material-ui/icons/Add';
+import { Fab, Tooltip } from '@material-ui/core';
+import ComposeEmailForm from './ComposeEmailForm';
+import FloatingPanel from '../Panel/FloatingPanel';
+import styles from './email-jss';
+
+
+class ComposeEmail extends React.Component {
+ render() {
+ const {
+ classes,
+ open,
+ closeForm,
+ sendEmail,
+ to,
+ subject,
+ validMail,
+ inputChange,
+ compose
+ } = this.props;
+ const branch = '';
+ return (
+
+
+ compose()} className={classes.addBtn}>
+
+
+
+
+
+
+
+ );
+ }
+}
+
+ComposeEmail.propTypes = {
+ classes: PropTypes.object.isRequired,
+ open: PropTypes.bool.isRequired,
+ to: PropTypes.string.isRequired,
+ subject: PropTypes.string.isRequired,
+ validMail: PropTypes.string.isRequired,
+ compose: PropTypes.func.isRequired,
+ closeForm: PropTypes.func.isRequired,
+ sendEmail: PropTypes.func.isRequired,
+ inputChange: PropTypes.func.isRequired,
+};
+
+export default withStyles(styles)(ComposeEmail);
diff --git a/front/odiparpack/app/components/Email/ComposeEmailForm.js b/front/odiparpack/app/components/Email/ComposeEmailForm.js
new file mode 100644
index 0000000..3620d4d
--- /dev/null
+++ b/front/odiparpack/app/components/Email/ComposeEmailForm.js
@@ -0,0 +1,262 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Editor } from 'react-draft-wysiwyg';
+import { convertFromRaw, EditorState, convertToRaw } from 'draft-js';
+import draftToHtml from 'draftjs-to-html';
+import Dropzone from 'react-dropzone';
+import { withStyles } from '@material-ui/core/styles';
+import TextField from '@material-ui/core/TextField';
+import Attachment from '@material-ui/icons/Attachment';
+import FileIcon from '@material-ui/icons/Description';
+import ActionDelete from '@material-ui/icons/Delete';
+import Send from '@material-ui/icons/Send';
+import EditorStyle from 'ba-styles/TextEditor.scss';
+import css from 'ba-styles/Form.scss';
+import 'ba-styles/vendors/react-draft-wysiwyg/react-draft-wysiwyg.css';
+import { Button, Grid, Typography, Snackbar, IconButton } from '@material-ui/core';
+import isImage from '../Forms/helpers/helpers.js';
+import styles from './email-jss';
+
+
+const content = {
+ blocks: [{
+ key: '637gr',
+ text: 'Lorem ipsum dolor sit amet 😀',
+ type: 'unstyled',
+ depth: 0,
+ inlineStyleRanges: [],
+ entityRanges: [],
+ data: {}
+ }],
+ entityMap: {}
+};
+
+class ComposeEmailForm extends React.Component {
+ constructor(props) {
+ super(props);
+ const contentBlock = convertFromRaw(content);
+ if (contentBlock) {
+ const editorState = EditorState.createWithContent(contentBlock);
+ this.state = {
+ openSnackBar: false,
+ errorMessage: '',
+ files: [],
+ editorState,
+ emailContent: draftToHtml(convertToRaw(editorState.getCurrentContent())),
+ };
+ }
+ this.onDrop = this.onDrop.bind(this);
+ }
+
+ onDrop(filesVal) {
+ const { files } = this.state;
+ let oldFiles = files;
+ const filesLimit = 3;
+ oldFiles = oldFiles.concat(filesVal);
+ if (oldFiles.length > filesLimit) {
+ console.log('Cannot upload more than ' + filesLimit + ' items.');
+ } else {
+ this.setState({ files: oldFiles });
+ }
+ }
+
+ onEditorStateChange = editorState => {
+ this.setState({
+ editorState,
+ emailContent: draftToHtml(convertToRaw(editorState.getCurrentContent()))
+ });
+ };
+
+ onDropRejected() {
+ this.setState({
+ openSnackBar: true,
+ errorMessage: 'File too big, max size is 3MB',
+ });
+ }
+
+ handleRequestCloseSnackBar = () => {
+ this.setState({
+ openSnackBar: false,
+ });
+ };
+
+ handleRemove(file, fileIndex) {
+ const thisFiles = this.state.files;
+ // This is to prevent memory leaks.
+ window.URL.revokeObjectURL(file.preview);
+
+ thisFiles.splice(fileIndex, 1);
+ this.setState({ files: thisFiles });
+ }
+
+ handleSend = (to, subject, emailContent, files) => {
+ this.props.sendEmail(to, subject, emailContent, files);
+ this.setState({ emailContent: '', files: [] });
+ };
+
+ render() {
+ const {
+ classes,
+ closeForm,
+ to,
+ subject,
+ validMail,
+ inputChange
+ } = this.props;
+ const {
+ editorState,
+ emailContent,
+ files,
+ openSnackBar,
+ errorMessage,
+ } = this.state;
+ let dropzoneRef;
+ const deleteBtn = (file, index) => (
+
+
this.handleRemove(file, index)}>
+
+
+
+ );
+ const previews = filesArray => filesArray.map((file, index) => {
+ if (isImage(file)) {
+ const base64Img = URL.createObjectURL(file);
+ return (
+
+
+

+ {deleteBtn(file, index)}
+
+
{file.name}
+
+ );
+ }
+ return (
+
+
+
+
+ {deleteBtn(file, index)}
+
+
+
{file.name}
+
+ );
+ });
+ const fileSizeLimit = 3000000;
+ return (
+
+ );
+ }
+}
+
+ComposeEmailForm.propTypes = {
+ classes: PropTypes.object.isRequired,
+ to: PropTypes.string.isRequired,
+ subject: PropTypes.string.isRequired,
+ validMail: PropTypes.string.isRequired,
+ sendEmail: PropTypes.func.isRequired,
+ closeForm: PropTypes.func.isRequired,
+ inputChange: PropTypes.func.isRequired,
+};
+
+export default withStyles(styles)(ComposeEmailForm);
diff --git a/front/odiparpack/app/components/Email/EmailHeader.js b/front/odiparpack/app/components/Email/EmailHeader.js
new file mode 100644
index 0000000..eceb757
--- /dev/null
+++ b/front/odiparpack/app/components/Email/EmailHeader.js
@@ -0,0 +1,49 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import SearchIcon from '@material-ui/icons/Search';
+import MenuIcon from '@material-ui/icons/Menu';
+import { AppBar, Hidden, Toolbar, Typography, IconButton } from '@material-ui/core';
+import styles from './email-jss';
+
+
+class EmailHeader extends React.Component {
+ render() {
+ const { classes, search, handleDrawerToggle } = this.props;
+ return (
+
+
+
+
+ Email
+
+
+ handleDrawerToggle()}
+ className={classes.navIconHide}
+ >
+
+
+
+
+
+
+
+
search(event)} placeholder="Search Email" />
+
+
+
+
+ );
+ }
+}
+
+EmailHeader.propTypes = {
+ classes: PropTypes.object.isRequired,
+ search: PropTypes.func.isRequired,
+ handleDrawerToggle: PropTypes.func.isRequired,
+};
+
+export default withStyles(styles)(EmailHeader);
diff --git a/front/odiparpack/app/components/Email/EmailList.js b/front/odiparpack/app/components/Email/EmailList.js
new file mode 100644
index 0000000..1fc3507
--- /dev/null
+++ b/front/odiparpack/app/components/Email/EmailList.js
@@ -0,0 +1,314 @@
+import React, { Fragment } from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import Bookmark from '@material-ui/icons/Bookmark';
+import Delete from '@material-ui/icons/Delete';
+import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
+import classNames from 'classnames';
+import Flag from '@material-ui/icons/Flag';
+import People from '@material-ui/icons/People';
+import QuestionAnswer from '@material-ui/icons/QuestionAnswer';
+import ReportIcon from '@material-ui/icons/Report';
+import LabelIcon from '@material-ui/icons/Label';
+import FileIcon from '@material-ui/icons/Description';
+import Download from '@material-ui/icons/CloudDownload';
+import StarBorder from '@material-ui/icons/StarBorder';
+import Star from '@material-ui/icons/Star';
+import {
+ List,
+ Typography,
+ ExpansionPanel,
+ ExpansionPanelDetails,
+ ExpansionPanelSummary,
+ ExpansionPanelActions,
+ Tooltip,
+ IconButton,
+ Avatar,
+ Button,
+ ListSubheader,
+ Menu,
+ MenuItem,
+ Divider,
+} from '@material-ui/core';
+import isImage from '../Forms/helpers/helpers.js';
+import styles from './email-jss';
+
+
+const ITEM_HEIGHT = 80;
+class EmailList extends React.Component {
+ state = {
+ anchorElOpt: null,
+ itemToMove: null
+ };
+
+ handleClickOpt = (event, item) => {
+ this.setState({
+ anchorElOpt: event.currentTarget,
+ itemToMove: item
+ });
+ };
+
+ handleCloseOpt = () => {
+ this.setState({ anchorElOpt: null });
+ };
+
+ handleMoveTo = (item, category) => {
+ this.props.moveTo(item, category);
+ this.setState({ anchorElOpt: null });
+ }
+
+ render() {
+ const {
+ classes,
+ emailData,
+ openMail,
+ filterPage,
+ keyword,
+ remove,
+ toggleStar,
+ reply
+ } = this.props;
+ const { anchorElOpt, itemToMove } = this.state;
+ /* Basic Filter */
+ const inbox = emailData.filter(item => item.get('category') !== 'sent' && item.get('category') !== 'spam');
+ const stared = emailData.filter(item => item.get('stared'));
+ const sent = emailData.filter(item => item.get('category') === 'sent');
+ const spam = emailData.filter(item => item.get('category') === 'spam');
+ /* Category Filter */
+ const updates = emailData.filter(item => item.get('category') === 'updates');
+ const social = emailData.filter(item => item.get('category') === 'social');
+ const forums = emailData.filter(item => item.get('category') === 'forums');
+ const promos = emailData.filter(item => item.get('category') === 'promos');
+ const getCategory = cat => {
+ switch (cat) {
+ case 'updates':
+ return (
+
+
+ {' '}
+Updates
+
+ );
+ case 'social':
+ return (
+
+
+ {' '}
+Social
+
+ );
+ case 'promos':
+ return (
+
+
+ {' '}
+Promos
+
+ );
+ case 'forums':
+ return (
+
+
+ {' '}
+Forums
+
+ );
+ default:
+ return false;
+ }
+ };
+ const attachmentPreview = filesArray => filesArray.map((file, index) => {
+ const base64File = URL.createObjectURL(file);
+ if (isImage(file)) {
+ return (
+
+
+
+
+
+
+
+

+
+
{file.name}
+
+ );
+ }
+ return (
+
+ );
+ });
+ const getEmail = dataArray => dataArray.map(mail => {
+ const renderHTML = { __html: mail.get('content') };
+ if (mail.get('subject').toLowerCase().indexOf(keyword) === -1) {
+ return false;
+ }
+ return (
+ openMail(mail)}>
+ }>
+
+
+ toggleStar(mail)} className={classes.starBtn}>{mail.get('stared') ? () : () }
+
+ {mail.get('category') !== 'spam'
+ ? (
)
+ : (
)
+ }
+
+ {mail.get('category') === 'sent' && ('To ')}
+ {mail.get('name')}
+ {mail.get('date')}
+
+
+
+ {mail.get('subject')}
+ {getCategory(mail.get('category'))}
+
+
+
+
+
+
+ {mail.get('category') !== 'sent' && (
+
+From
+ {mail.get('name')}
+ {' '}
+to me
+
+ )}
+
+
+
+ toggleStar(mail)}>{mail.get('stared') ? () : () }
+
+
+ this.handleClickOpt(event, mail)}
+ >
+
+
+
+
+ remove(mail)}>
+
+
+
+
+
{mail.get('subject')}
+
+
+
+ {attachmentPreview(mail.get('attachment'))}
+
+
+
+
+
+
+
+
+
+
+
+ );
+ });
+ const showEmail = category => {
+ switch (category) {
+ case 'inbox':
+ return getEmail(inbox);
+ case 'stared':
+ return getEmail(stared);
+ case 'sent':
+ return getEmail(sent);
+ case 'spam':
+ return getEmail(spam);
+ case 'updates':
+ return getEmail(updates);
+ case 'social':
+ return getEmail(social);
+ case 'promos':
+ return getEmail(promos);
+ case 'forums':
+ return getEmail(forums);
+ default:
+ return getEmail(inbox);
+ }
+ };
+ return (
+
+
+
+ {showEmail(filterPage)}
+
+ );
+ }
+}
+
+EmailList.propTypes = {
+ classes: PropTypes.object.isRequired,
+ emailData: PropTypes.object.isRequired,
+ openMail: PropTypes.func.isRequired,
+ moveTo: PropTypes.func.isRequired,
+ remove: PropTypes.func.isRequired,
+ toggleStar: PropTypes.func.isRequired,
+ reply: PropTypes.func.isRequired,
+ filterPage: PropTypes.string.isRequired,
+ keyword: PropTypes.string.isRequired,
+};
+
+export default withStyles(styles)(EmailList);
diff --git a/front/odiparpack/app/components/Email/EmailSidebar.js b/front/odiparpack/app/components/Email/EmailSidebar.js
new file mode 100644
index 0000000..1951de2
--- /dev/null
+++ b/front/odiparpack/app/components/Email/EmailSidebar.js
@@ -0,0 +1,162 @@
+import React, { Fragment } from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import InboxIcon from '@material-ui/icons/MoveToInbox';
+import SendIcon from '@material-ui/icons/Send';
+import ReportIcon from '@material-ui/icons/Report';
+import StarIcon from '@material-ui/icons/Star';
+import Flag from '@material-ui/icons/Flag';
+import People from '@material-ui/icons/People';
+import QuestionAnswer from '@material-ui/icons/QuestionAnswer';
+import LabelIcon from '@material-ui/icons/Label';
+import Add from '@material-ui/icons/Add';
+import {
+ Drawer,
+ List,
+ ListItem,
+ ListItemIcon,
+ ListItemText,
+ Hidden,
+ Button,
+ Divider,
+} from '@material-ui/core';
+import styles from './email-jss';
+
+
+const MenuList = props => {
+ const {
+ classes,
+ compose,
+ goto,
+ selected,
+ } = props;
+ return (
+
+
+
+
+
+ goto('inbox')}>
+
+
+
+
+
+ goto('stared')}>
+
+
+
+
+
+ goto('sent')}>
+
+
+
+
+
+ goto('spam')}>
+
+
+
+
+
+
+
+
+ goto('updates')}>
+
+
+
+
+
+ goto('social')}>
+
+
+
+
+
+ goto('promos')}>
+
+
+
+
+
+ goto('forums')}>
+
+
+
+
+
+
+
+ );
+};
+
+MenuList.propTypes = {
+ classes: PropTypes.object.isRequired,
+ compose: PropTypes.func.isRequired,
+ goto: PropTypes.func.isRequired,
+ selected: PropTypes.string.isRequired,
+};
+
+const MenuEmail = withStyles(styles)(MenuList);
+
+class EmailSidebar extends React.Component {
+ render() {
+ const {
+ classes,
+ compose,
+ goto,
+ selected,
+ handleDrawerToggle,
+ mobileOpen
+ } = this.props;
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+EmailSidebar.propTypes = {
+ classes: PropTypes.object.isRequired,
+ compose: PropTypes.func.isRequired,
+ goto: PropTypes.func.isRequired,
+ handleDrawerToggle: PropTypes.func.isRequired,
+ selected: PropTypes.string.isRequired,
+ mobileOpen: PropTypes.bool.isRequired,
+};
+
+export default withStyles(styles)(EmailSidebar);
diff --git a/front/odiparpack/app/components/Email/email-jss.js b/front/odiparpack/app/components/Email/email-jss.js
new file mode 100644
index 0000000..6775966
--- /dev/null
+++ b/front/odiparpack/app/components/Email/email-jss.js
@@ -0,0 +1,238 @@
+import { fade } from '@material-ui/core/styles/colorManipulator';
+import { red, orange, indigo as blue, cyan } from '@material-ui/core/colors';
+const drawerWidth = 240;
+const styles = theme => ({
+ iconRed: {
+ color: red[500]
+ },
+ iconOrange: {
+ color: orange[500]
+ },
+ iconBlue: {
+ color: blue[500]
+ },
+ iconCyan: {
+ color: cyan[500]
+ },
+ appBar: {
+ zIndex: 130,
+ background: theme.palette.secondary.main,
+ '& ::-webkit-input-placeholder': {
+ color: theme.palette.common.white
+ },
+ '& ::-moz-placeholder': {
+ color: theme.palette.common.white
+ },
+ '& :-ms-input-placeholder': {
+ color: theme.palette.common.white
+ },
+ '& :-moz-placeholder': {
+ color: theme.palette.common.white
+ }
+ },
+ flex: {
+ flex: 1,
+ },
+ wrapper: {
+ fontFamily: theme.typography.fontFamily,
+ position: 'relative',
+ marginLeft: theme.spacing(1),
+ borderRadius: 2,
+ background: fade(theme.palette.common.white, 0.15),
+ '&:hover': {
+ background: fade(theme.palette.common.white, 0.25),
+ },
+ '& $input': {
+ transition: theme.transitions.create('width'),
+ },
+ },
+ addBtn: {
+ position: 'fixed',
+ bottom: 30,
+ right: 30,
+ zIndex: 1000
+ },
+ sidebar: {
+ zIndex: 120
+ },
+ search: {
+ width: theme.spacing(9),
+ height: '100%',
+ position: 'absolute',
+ pointerEvents: 'none',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ input: {
+ font: 'inherit',
+ padding: `${theme.spacing(1)}px ${theme.spacing(1)}px ${theme.spacing(1)}px ${theme.spacing(9)}px`,
+ border: 0,
+ display: 'block',
+ verticalAlign: 'middle',
+ whiteSpace: 'normal',
+ background: 'none',
+ margin: 0, // Reset for Safari
+ color: 'inherit',
+ width: '100%',
+ '&:focus': {
+ outline: 0,
+ },
+ },
+ drawerPaper: {
+ [theme.breakpoints.up('md')]: {
+ position: 'relative',
+ },
+ width: drawerWidth,
+ background: theme.palette.grey[50],
+ border: 'none',
+ padding: 10
+ },
+ selected: {
+ background: theme.palette.secondary.light,
+ borderLeft: `2px solid ${theme.palette.secondary.main}`,
+ paddingLeft: 22,
+ '& h3': {
+ color: theme.palette.secondary.dark
+ }
+ },
+ content: {
+ flexGrow: 1,
+ backgroundColor: theme.palette.background.default,
+ zIndex: 120,
+ marginBottom: theme.spacing(8),
+ [theme.breakpoints.up('md')]: {
+ padding: theme.spacing(3),
+ marginBottom: theme.spacing(4),
+ paddingLeft: 0,
+ },
+ position: 'relative',
+ minWidth: 0, // So the Typography noWrap works
+ },
+ toolbar: {
+ minHeight: 32
+ },
+ title: {
+ width: 205
+ },
+ divider: {
+ margin: '0 20px 0 10px'
+ },
+ /* Email List */
+ column: {
+ flexBasis: '33.33%',
+ overflow: 'hidden',
+ paddingRight: '0 !important',
+ paddingTop: 5,
+ marginLeft: 20
+ },
+ secondaryHeading: {
+ fontSize: 14,
+ color: theme.palette.text.secondary,
+ },
+ icon: {
+ verticalAlign: 'bottom',
+ height: 20,
+ width: 20,
+ },
+ details: {
+ alignItems: 'center',
+ [theme.breakpoints.down('sm')]: {
+ padding: `${theme.spacing(1)}px ${theme.spacing(1)}px ${theme.spacing(3)}px`
+ },
+ '& section': {
+ width: '100%'
+ }
+ },
+ link: {
+ color: theme.palette.secondary.main,
+ textDecoration: 'none',
+ '&:hover': {
+ textDecoration: 'underline',
+ },
+ },
+ avatar: {},
+ fromHeading: {
+ overflow: 'hidden',
+ display: 'flex',
+ alignItems: 'center',
+ '& $avatar': {
+ width: 30,
+ height: 30,
+ marginRight: 20
+ }
+ },
+ topAction: {
+ display: 'flex',
+ background: theme.palette.grey[100],
+ marginBottom: 20,
+ alignItems: 'center',
+ padding: '0 20px',
+ borderRadius: 2,
+ },
+ category: {
+ fontSize: 12,
+ textTransform: 'uppercase',
+ display: 'flex',
+ '& svg': {
+ fontSize: 16,
+ marginRight: 5
+ }
+ },
+ markMenu: {
+ '& svg': {
+ marginRight: 10
+ }
+ },
+ headMail: {
+ flex: 1
+ },
+ field: {
+ width: '100%',
+ marginBottom: 20,
+ '& svg': {
+ color: theme.palette.grey[400],
+ fontSize: 18,
+ }
+ },
+ hiddenDropzone: {
+ display: 'none'
+ },
+ sendIcon: {
+ marginLeft: 10
+ },
+ item: {},
+ preview: {
+ display: 'flex',
+ marginBottom: 20,
+ '& $item': {
+ maxWidth: 160,
+ marginBottom: 5,
+ marginRight: 5
+ }
+ },
+ emailSummary: {
+ paddingLeft: 0,
+ '& > div': {
+ [theme.breakpoints.down('sm')]: {
+ flexDirection: 'column'
+ },
+ }
+ },
+ emailContent: {
+ padding: theme.spacing(2),
+ [theme.breakpoints.down('sm')]: {
+ padding: `${theme.spacing(2)}px ${theme.spacing(2)}px`,
+ },
+ },
+ starBtn: {
+ marginRight: 10
+ },
+ navIconHide: {
+ [theme.breakpoints.up('md')]: {
+ display: 'none',
+ },
+ },
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/Error/ErrorWrap.js b/front/odiparpack/app/components/Error/ErrorWrap.js
new file mode 100644
index 0000000..5b57ab3
--- /dev/null
+++ b/front/odiparpack/app/components/Error/ErrorWrap.js
@@ -0,0 +1,75 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import { Route, Link } from 'react-router-dom';
+
+import { Typography, Button } from '@material-ui/core';
+
+const styles = theme => ({
+ errorWrap: {
+ background: theme.palette.common.white,
+ boxShadow: theme.shadows[2],
+ borderRadius: '50%',
+ width: 500,
+ height: 500,
+ [theme.breakpoints.down('sm')]: {
+ width: 300,
+ height: 300,
+ },
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ flexDirection: 'column',
+ position: 'relative',
+ margin: `${theme.spacing(3)}px auto`,
+ },
+ title: {
+ color: theme.palette.primary.main,
+ textShadow: `10px 6px 50px ${theme.palette.primary.main}`,
+ [theme.breakpoints.down('sm')]: {
+ fontSize: '4rem'
+ },
+ },
+ deco: {
+ boxShadow: theme.shadows[2],
+ position: 'absolute',
+ borderRadius: 2,
+ },
+ button: {
+ marginTop: 50
+ }
+});
+
+const ErrorWrap = (props) => (
+ {
+ if (staticContext) {
+ staticContext.status = 404; // eslint-disable-line
+ }
+ const { classes, title, desc } = props;
+ return (
+
+ {title}
+ {desc}
+
+
+ );
+ }}
+ />
+);
+
+ErrorWrap.propTypes = {
+ classes: PropTypes.object.isRequired,
+ desc: PropTypes.string.isRequired,
+ title: PropTypes.string.isRequired,
+};
+
+export default withStyles(styles)(ErrorWrap);
diff --git a/front/odiparpack/app/components/Forms/LockForm.js b/front/odiparpack/app/components/Forms/LockForm.js
new file mode 100644
index 0000000..c667809
--- /dev/null
+++ b/front/odiparpack/app/components/Forms/LockForm.js
@@ -0,0 +1,123 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import { Field, reduxForm } from 'redux-form/immutable';
+import ArrowForward from '@material-ui/icons/ArrowForward';
+import Help from '@material-ui/icons/Help';
+import dummy from 'ba-api/dummyContents';
+import avatarApi from 'ba-api/avatars';
+import {
+ Button,
+ Popover,
+ FormControl,
+ IconButton,
+ Typography,
+ InputAdornment,
+ Paper,
+ Avatar,
+} from '@material-ui/core';
+import { TextFieldRedux } from './ReduxFormMUI';
+import styles from './user-jss';
+
+
+// validation functions
+const required = value => (value == null ? 'Required' : undefined);
+
+class LockForm extends React.Component {
+ state = {
+ anchorEl: null,
+ };
+
+ handleShowHint = event => {
+ this.setState({
+ anchorEl: event.currentTarget,
+ });
+ };
+
+ handleClose = () => {
+ this.setState({
+ anchorEl: null,
+ });
+ };
+
+ render() {
+ const {
+ classes,
+ handleSubmit,
+ pristine,
+ submitting
+ } = this.props;
+ const { anchorEl } = this.state;
+ return (
+
+ );
+ }
+}
+
+LockForm.propTypes = {
+ classes: PropTypes.object.isRequired,
+ handleSubmit: PropTypes.func.isRequired,
+ pristine: PropTypes.bool.isRequired,
+ submitting: PropTypes.bool.isRequired,
+};
+
+const LockFormReduxed = reduxForm({
+ form: 'immutableELockFrm',
+ enableReinitialize: true,
+})(LockForm);
+
+export default withStyles(styles)(LockFormReduxed);
diff --git a/front/odiparpack/app/components/Forms/LoginForm.js b/front/odiparpack/app/components/Forms/LoginForm.js
new file mode 100644
index 0000000..d652480
--- /dev/null
+++ b/front/odiparpack/app/components/Forms/LoginForm.js
@@ -0,0 +1,147 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import { Field, reduxForm } from 'redux-form/immutable';
+import { connect } from 'react-redux';
+import Visibility from '@material-ui/icons/Visibility';
+import VisibilityOff from '@material-ui/icons/VisibilityOff';
+import AllInclusive from '@material-ui/icons/AllInclusive';
+import Brightness5 from '@material-ui/icons/Brightness5';
+import People from '@material-ui/icons/People';
+import ArrowForward from '@material-ui/icons/ArrowForward';
+import { Button, IconButton, InputAdornment, FormControl, FormControlLabel } from '@material-ui/core';
+import styles from './user-jss';
+import { TextFieldRedux, CheckboxRedux } from './ReduxFormMUI';
+import { ContentDivider } from '../Divider';
+import PapperBlock from '../PapperBlock/PapperBlock';
+
+
+// validation functions
+const required = value => (value == null ? 'Required' : undefined);
+const email = value => (
+ value && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)
+ ? 'Invalid email'
+ : undefined
+);
+
+class LoginForm extends React.Component {
+ state = {
+ showPassword: false
+ }
+
+ handleClickShowPassword = () => {
+ this.setState({ showPassword: !this.state.showPassword });
+ };
+
+ handleMouseDownPassword = event => {
+ event.preventDefault();
+ };
+
+ render() {
+ const {
+ classes,
+ handleSubmit,
+ pristine,
+ submitting
+ } = this.props;
+ return (
+
+ );
+ }
+}
+
+LoginForm.propTypes = {
+ classes: PropTypes.object.isRequired,
+ handleSubmit: PropTypes.func.isRequired,
+ pristine: PropTypes.bool.isRequired,
+ submitting: PropTypes.bool.isRequired,
+};
+
+const LoginFormReduxed = reduxForm({
+ form: 'immutableExample',
+ enableReinitialize: true,
+})(LoginForm);
+
+const reducer = 'login';
+const FormInit = connect(
+ state => ({
+ force: state,
+ initialValues: state.getIn([reducer, 'usersLogin'])
+ }),
+)(LoginFormReduxed);
+
+export default withStyles(styles)(FormInit);
diff --git a/front/odiparpack/app/components/Forms/MaterialDropZone.js b/front/odiparpack/app/components/Forms/MaterialDropZone.js
new file mode 100644
index 0000000..c62b3fd
--- /dev/null
+++ b/front/odiparpack/app/components/Forms/MaterialDropZone.js
@@ -0,0 +1,202 @@
+import React from 'react';
+import Dropzone from 'react-dropzone';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import { withStyles } from '@material-ui/core/styles';
+import Button from '@material-ui/core/Button';
+import FileIcon from '@material-ui/icons/Description';
+import ActionDelete from '@material-ui/icons/Delete';
+import IconButton from '@material-ui/core/IconButton';
+import Snackbar from '@material-ui/core/Snackbar';
+import CloudUpload from '@material-ui/icons/CloudUpload';
+import { lighten } from '@material-ui/core/styles/colorManipulator';
+import 'ba-styles/vendors/react-dropzone/react-dropzone.css';
+import isImage from './helpers/helpers.js';
+
+const styles = theme => ({
+ dropItem: {
+ borderColor: theme.palette.secondary.main,
+ background: lighten(theme.palette.secondary.light, 0.9),
+ borderRadius: 2
+ },
+ uploadIconSize: {
+ width: 51,
+ height: 51,
+ color: theme.palette.secondary.main,
+ margin: '0 auto'
+ },
+ rightIcon: {
+ marginLeft: theme.spacing(1),
+ },
+ button: {
+ marginTop: 20
+ }
+});
+
+class MaterialDropZone extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ openSnackBar: false,
+ errorMessage: '',
+ files: this.props.files, // eslint-disable-line
+ acceptedFiles: this.props.acceptedFiles // eslint-disable-line
+ };
+ this.onDrop = this.onDrop.bind(this);
+ }
+
+ onDrop(filesVal) {
+ const { files } = this.state;
+ const { filesLimit } = this.props;
+ let oldFiles = files;
+ const filesLimitVal = filesLimit || '3';
+ oldFiles = oldFiles.concat(filesVal);
+ if (oldFiles.length > filesLimit) {
+ this.setState({
+ openSnackBar: true,
+ errorMessage: 'Cannot upload more than ' + filesLimitVal + ' items.',
+ });
+ } else {
+ this.setState({ files: oldFiles });
+ }
+ }
+
+ onDropRejected() {
+ this.setState({
+ openSnackBar: true,
+ errorMessage: 'File too big, max size is 3MB',
+ });
+ }
+
+ handleRequestCloseSnackBar = () => {
+ this.setState({
+ openSnackBar: false,
+ });
+ };
+
+ handleRemove(file, fileIndex) {
+ const thisFiles = this.state.files; // eslint-disable-line
+ // This is to prevent memory leaks.
+ window.URL.revokeObjectURL(file.preview);
+
+ thisFiles.splice(fileIndex, 1);
+ this.setState({ files: thisFiles });
+ }
+
+ render() {
+ const {
+ classes,
+ showPreviews,
+ maxSize,
+ text,
+ showButton,
+ filesLimit,
+ ...rest
+ } = this.props;
+
+ const {
+ acceptedFiles,
+ files,
+ openSnackBar,
+ errorMessage
+ } = this.state;
+ const fileSizeLimit = maxSize || 3000000;
+ const deleteBtn = (file, index) => (
+
+
this.handleRemove(file, index)}>
+
+
+
+ );
+ const previews = filesArray => filesArray.map((file, index) => {
+ const base64Img = URL.createObjectURL(file);
+ if (isImage(file)) {
+ return (
+
+
+

+ {deleteBtn(file, index)}
+
+
+ );
+ }
+ return (
+
+
+
+ {deleteBtn(file, index)}
+
+
+ );
+ });
+ let dropzoneRef;
+ return (
+
+
{ dropzoneRef = node; }}
+ {...rest}
+ >
+ {({ getRootProps, getInputProps }) => (
+
+ )}
+ {/* end */}
+
+ {showButton && (
+
+ )}
+
+ {showPreviews && previews(files)}
+
+
+
+ );
+ }
+}
+
+MaterialDropZone.propTypes = {
+ files: PropTypes.array.isRequired,
+ text: PropTypes.string.isRequired,
+ acceptedFiles: PropTypes.array,
+ showPreviews: PropTypes.bool.isRequired,
+ showButton: PropTypes.bool,
+ maxSize: PropTypes.number.isRequired,
+ filesLimit: PropTypes.number.isRequired,
+ classes: PropTypes.object.isRequired,
+};
+
+MaterialDropZone.defaultProps = {
+ acceptedFiles: [],
+ showButton: false,
+};
+
+export default withStyles(styles)(MaterialDropZone);
diff --git a/front/odiparpack/app/components/Forms/ReduxFormMUI.js b/front/odiparpack/app/components/Forms/ReduxFormMUI.js
new file mode 100644
index 0000000..383a717
--- /dev/null
+++ b/front/odiparpack/app/components/Forms/ReduxFormMUI.js
@@ -0,0 +1,69 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import TextField from '@material-ui/core/TextField';
+import Select from '@material-ui/core/Select';
+import Checkbox from '@material-ui/core/Checkbox';
+import Switch from '@material-ui/core/Switch';
+
+/* Textfield */
+export const TextFieldRedux = ({ meta: { touched, error }, input, ...rest }) => (
+
+);
+
+TextFieldRedux.propTypes = {
+ input: PropTypes.object.isRequired,
+ meta: PropTypes.object,
+};
+
+TextFieldRedux.defaultProps = {
+ meta: null,
+};
+/* End */
+
+/* Select */
+export const SelectRedux = ({ input, children, ...rest }) => (
+
+);
+
+SelectRedux.propTypes = {
+ input: PropTypes.object.isRequired,
+ children: PropTypes.node.isRequired,
+};
+/* End */
+
+/* Checkbox */
+export const CheckboxRedux = ({ input, ...rest }) => (
+
+);
+
+CheckboxRedux.propTypes = {
+ input: PropTypes.object.isRequired,
+};
+/* End */
+
+/* Switch */
+export const SwitchRedux = ({ input, ...rest }) => (
+
+);
+
+SwitchRedux.propTypes = {
+ input: PropTypes.object.isRequired,
+};
+/* End */
diff --git a/front/odiparpack/app/components/Forms/RegisterForm.js b/front/odiparpack/app/components/Forms/RegisterForm.js
new file mode 100644
index 0000000..2ac4c65
--- /dev/null
+++ b/front/odiparpack/app/components/Forms/RegisterForm.js
@@ -0,0 +1,174 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import { Field, reduxForm } from 'redux-form/immutable';
+import ArrowForward from '@material-ui/icons/ArrowForward';
+import AllInclusive from '@material-ui/icons/AllInclusive';
+import Brightness5 from '@material-ui/icons/Brightness5';
+import People from '@material-ui/icons/People';
+import { Button, FormControl, FormControlLabel, Tabs, Tab } from '@material-ui/core';
+import styles from './user-jss';
+import { TextFieldRedux, CheckboxRedux } from './ReduxFormMUI';
+import PapperBlock from '../PapperBlock/PapperBlock';
+
+
+// validation functions
+const required = value => (value == null ? 'Required' : undefined);
+const email = value => (
+ value && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)
+ ? 'Invalid email'
+ : undefined
+);
+
+const passwordsMatch = (value, allValues) => {
+ console.log(value, allValues.get('password'));
+ if (value !== allValues.get('password')) {
+ return 'Passwords dont match';
+ }
+ return undefined;
+};
+
+class RegisterForm extends React.Component {
+ state = {
+ tab: 0,
+ };
+
+ handleClickShowPassword = () => {
+ this.setState({ showPassword: !this.state.showPassword });
+ };
+
+ handleMouseDownPassword = event => {
+ event.preventDefault();
+ };
+
+ handleChangeTab = (event, value) => {
+ this.setState({ tab: value });
+ };
+
+ render() {
+ const {
+ classes,
+ handleSubmit,
+ pristine,
+ submitting
+ } = this.props;
+ const { tab } = this.state;
+ return (
+
+
+
+
+
+
+ {tab === 0
+ && (
+
+ )
+ }
+ {tab === 1
+ && (
+
+
+
+
+
+ )
+ }
+
+
+ );
+ }
+}
+
+RegisterForm.propTypes = {
+ classes: PropTypes.object.isRequired,
+ handleSubmit: PropTypes.func.isRequired,
+ pristine: PropTypes.bool.isRequired,
+ submitting: PropTypes.bool.isRequired,
+};
+
+const RegisterFormReduxed = reduxForm({
+ form: 'immutableExample',
+ enableReinitialize: true,
+})(RegisterForm);
+
+export default withStyles(styles)(RegisterFormReduxed);
diff --git a/front/odiparpack/app/components/Forms/ResetForm.js b/front/odiparpack/app/components/Forms/ResetForm.js
new file mode 100644
index 0000000..95bf93b
--- /dev/null
+++ b/front/odiparpack/app/components/Forms/ResetForm.js
@@ -0,0 +1,71 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import { Field, reduxForm } from 'redux-form/immutable';
+import ArrowForward from '@material-ui/icons/ArrowForward';
+import { Button, FormControl } from '@material-ui/core';
+import styles from './user-jss';
+import PapperBlock from '../PapperBlock/PapperBlock';
+import { TextFieldRedux } from './ReduxFormMUI';
+
+
+// validation functions
+const required = value => (value == null ? 'Required' : undefined);
+const email = value => (
+ value && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)
+ ? 'Invalid email'
+ : undefined
+);
+
+class ResetForm extends React.Component {
+ render() {
+ const {
+ classes,
+ handleSubmit,
+ pristine,
+ submitting
+ } = this.props;
+ return (
+
+ );
+ }
+}
+
+ResetForm.propTypes = {
+ classes: PropTypes.object.isRequired,
+ handleSubmit: PropTypes.func.isRequired,
+ pristine: PropTypes.bool.isRequired,
+ submitting: PropTypes.bool.isRequired,
+};
+
+const ResetFormReduxed = reduxForm({
+ form: 'immutableEResetFrm',
+ enableReinitialize: true,
+})(ResetForm);
+
+export default withStyles(styles)(ResetFormReduxed);
diff --git a/front/odiparpack/app/components/Forms/helpers/helpers.js b/front/odiparpack/app/components/Forms/helpers/helpers.js
new file mode 100644
index 0000000..99c953a
--- /dev/null
+++ b/front/odiparpack/app/components/Forms/helpers/helpers.js
@@ -0,0 +1,8 @@
+export default function isImage(file) {
+ const fileName = file.name || file.path;
+ const suffix = fileName.substr(fileName.indexOf('.') + 1).toLowerCase();
+ if (suffix === 'jpg' || suffix === 'jpeg' || suffix === 'bmp' || suffix === 'png') {
+ return true;
+ }
+ return false;
+}
diff --git a/front/odiparpack/app/components/Forms/user-jss.js b/front/odiparpack/app/components/Forms/user-jss.js
new file mode 100644
index 0000000..5b9ae4a
--- /dev/null
+++ b/front/odiparpack/app/components/Forms/user-jss.js
@@ -0,0 +1,179 @@
+import { cyan, indigo, red } from '@material-ui/core/colors';
+const styles = theme => ({
+ root: {
+ display: 'flex',
+ width: '100%',
+ zIndex: 1,
+ position: 'relative'
+ },
+ container: {
+ overflow: 'hidden',
+ display: 'flex',
+ alignItems: 'center',
+ width: '100%',
+ [theme.breakpoints.down('md')]: {
+ overflow: 'hidden'
+ },
+ },
+ formControl: {
+ width: '100%',
+ marginBottom: theme.spacing(3)
+ },
+ loginWrap: {
+ [theme.breakpoints.up('md')]: {
+ width: 860
+ },
+ },
+ formWrap: {
+ [theme.breakpoints.up('md')]: {
+ marginTop: -24
+ },
+ },
+ btnArea: {
+ justifyContent: 'space-between',
+ display: 'flex',
+ alignItems: 'center',
+ marginBottom: theme.spacing(3),
+ [theme.breakpoints.down('sm')]: {
+ flexDirection: 'column',
+ '& button': {
+ width: '100%',
+ margin: 5
+ }
+ },
+ },
+ noMargin: {
+ margin: 0
+ },
+ optArea: {
+ justifyContent: 'space-between',
+ display: 'flex',
+ alignItems: 'center',
+ width: '100%',
+ [theme.breakpoints.up('sm')]: {
+ width: '60%'
+ },
+ },
+ redBtn: {
+ color: theme.palette.getContrastText(red[500]),
+ backgroundColor: red[500],
+ '&:hover': {
+ backgroundColor: red[700],
+ },
+ },
+ blueBtn: {
+ color: theme.palette.getContrastText(indigo[500]),
+ backgroundColor: indigo[500],
+ '&:hover': {
+ backgroundColor: indigo[700],
+ },
+ },
+ cyanBtn: {
+ color: theme.palette.getContrastText(cyan[700]),
+ backgroundColor: cyan[500],
+ '&:hover': {
+ backgroundColor: cyan[700],
+ },
+ },
+ leftIcon: {
+ marginRight: theme.spacing(1),
+ },
+ rightIcon: {
+ marginLeft: theme.spacing(1),
+ },
+ iconSmall: {
+ fontSize: 20,
+ },
+ footer: {
+ textAlign: 'center',
+ padding: 5,
+ background: theme.palette.grey[100],
+ fontSize: 14,
+ position: 'relative'
+ },
+ welcomeWrap: {
+ position: 'relative'
+ },
+ welcome: {
+ background: theme.palette.secondary.light,
+ position: 'absolute',
+ width: '100%',
+ height: 'calc(100% + 30px)',
+ padding: '20px 50px',
+ top: -15,
+ left: 2,
+ boxShadow: theme.shadows[5],
+ borderRadius: 2,
+ display: 'flex',
+ alignItems: 'center',
+ overflow: 'hidden'
+ },
+ brand: {
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'flex-start',
+ position: 'relative',
+ marginBottom: 20,
+ '& img': {
+ width: 32
+ },
+ '& h3': {
+ fontSize: 18,
+ margin: 0,
+ paddingLeft: 10,
+ fontWeight: 500,
+ color: theme.palette.grey[700]
+ }
+ },
+ brandText: {
+ marginTop: 10,
+ color: 'rgba(0, 0, 0, 0.54)',
+ },
+ decoBottom: {
+ fontSize: 480,
+ position: 'absolute',
+ left: 10,
+ bottom: -190,
+ opacity: 0.1,
+ color: theme.palette.secondary.dark
+ },
+ tab: {
+ marginBottom: 20,
+ [theme.breakpoints.up('md')]: {
+ marginTop: theme.spacing(1) * -3,
+ },
+ },
+ link: {
+ fontSize: 12,
+ marginLeft: -30,
+ color: theme.palette.secondary.main,
+ textDecoration: 'none',
+ '&:hover': {
+ textDecoration: 'underline'
+ }
+ },
+ socMedFull: {
+ marginBottom: theme.spacing(2)
+ },
+ lockWrap: {
+ textAlign: 'center',
+ padding: theme.spacing(3)
+ },
+ avatar: {
+ width: 150,
+ height: 150,
+ margin: '5px auto 30px',
+ [theme.breakpoints.up('md')]: {
+ margin: '-75px auto 30px',
+ },
+ boxShadow: theme.shadows[8]
+ },
+ userName: {
+ marginBottom: theme.spacing(3)
+ },
+ hint: {
+ padding: theme.spacing(1)
+ }
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/Gallery/PhotoGallery.js b/front/odiparpack/app/components/Gallery/PhotoGallery.js
new file mode 100644
index 0000000..3877bba
--- /dev/null
+++ b/front/odiparpack/app/components/Gallery/PhotoGallery.js
@@ -0,0 +1,83 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import 'ba-styles/vendors/image-lightbox/image-lightbox.css';
+import { Typography, ButtonBase } from '@material-ui/core';
+import ImageLightbox from '../ImageLightbox/ImageLightbox';
+import styles from './photo-jss';
+
+
+class PhotoGallery extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ photoIndex: 0,
+ isOpen: false,
+ };
+ }
+
+ openPopup = (photoIndex) => {
+ this.setState({ isOpen: true, photoIndex });
+ }
+
+ render() {
+ const { photoIndex, isOpen } = this.state;
+ const { classes, imgData } = this.props;
+ return (
+
+ {isOpen && (
+
this.setState({ isOpen: false })}
+ onMovePrevRequest={() => this.setState({
+ photoIndex: (photoIndex + (imgData.length - 1)) % imgData.length,
+ })
+ }
+ onMoveNextRequest={() => this.setState({
+ photoIndex: (photoIndex + 1) % imgData.length,
+ })
+ }
+ />
+ )}
+
+ {
+ imgData.map((thumb, index) => (
+
+ this.openPopup(index)}
+ >
+
+
+
+
+ {thumb.title}
+
+
+
+
+
+ ))
+ }
+
+
+ );
+ }
+}
+
+PhotoGallery.propTypes = {
+ classes: PropTypes.object.isRequired,
+ imgData: PropTypes.array.isRequired
+};
+
+export default withStyles(styles)(PhotoGallery);
diff --git a/front/odiparpack/app/components/Gallery/ProductDetail.js b/front/odiparpack/app/components/Gallery/ProductDetail.js
new file mode 100644
index 0000000..f05852f
--- /dev/null
+++ b/front/odiparpack/app/components/Gallery/ProductDetail.js
@@ -0,0 +1,195 @@
+import React, { Fragment } from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import Slider from 'react-slick';
+import CloseIcon from '@material-ui/icons/Close';
+import AddShoppingCart from '@material-ui/icons/AddShoppingCart';
+import imgData from 'ba-api/imgData';
+import Type from 'ba-styles/Typography.scss';
+import 'ba-styles/vendors/slick-carousel/slick-carousel.css';
+import 'ba-styles/vendors/slick-carousel/slick.css';
+import 'ba-styles/vendors/slick-carousel/slick-theme.css';
+import {
+ Typography,
+ Grid,
+ Dialog,
+ AppBar,
+ Toolbar,
+ IconButton,
+ Slide,
+ Button,
+ Chip,
+ TextField,
+} from '@material-ui/core';
+import Rating from '../Rating/Rating';
+import styles from './product-jss';
+
+const getThumb = imgData.map(a => a.thumb);
+
+const Transition = React.forwardRef(function Transition(props, ref) { // eslint-disable-line
+ return ;
+});
+
+class ProductDetail extends React.Component {
+ state = {
+ qty: 1,
+ }
+
+ handleQtyChange = event => {
+ this.setState({ qty: event.target.value });
+ }
+
+ submitToCart = itemAttr => {
+ this.props.handleAddToCart(itemAttr);
+ this.props.close();
+ }
+
+ render() {
+ const {
+ classes,
+ open,
+ close,
+ detailContent,
+ productIndex
+ } = this.props;
+
+ const { qty } = this.state;
+
+ const itemAttr = (item) => {
+ if (item !== undefined) {
+ return {
+ id: detailContent.getIn([productIndex, 'id']),
+ name: detailContent.getIn([productIndex, 'name']),
+ thumbnail: detailContent.getIn([productIndex, 'thumbnail']),
+ price: detailContent.getIn([productIndex, 'price']),
+ quantity: qty
+ };
+ }
+ return false;
+ };
+
+ const settings = {
+ customPaging: (i) => (
+
+
+
+ ),
+ infinite: true,
+ dots: true,
+ slidesToShow: 1,
+ slidesToScroll: 1,
+ };
+
+ return (
+
+ );
+ }
+}
+
+ProductDetail.propTypes = {
+ classes: PropTypes.object.isRequired,
+ open: PropTypes.bool.isRequired,
+ close: PropTypes.func.isRequired,
+ handleAddToCart: PropTypes.func.isRequired,
+ detailContent: PropTypes.object.isRequired,
+ productIndex: PropTypes.number,
+};
+
+ProductDetail.defaultProps = {
+ productIndex: undefined
+};
+
+export default withStyles(styles)(ProductDetail);
diff --git a/front/odiparpack/app/components/Gallery/ProductGallery.js b/front/odiparpack/app/components/Gallery/ProductGallery.js
new file mode 100644
index 0000000..94f6c1e
--- /dev/null
+++ b/front/odiparpack/app/components/Gallery/ProductGallery.js
@@ -0,0 +1,152 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import ViewList from '@material-ui/icons/ViewList';
+import GridOn from '@material-ui/icons/GridOn';
+import { Grid, Typography, Button } from '@material-ui/core';
+import ProductCard from '../CardPaper/ProductCard';
+import ProductDetail from './ProductDetail';
+
+
+const styles = theme => ({
+ result: {
+ margin: theme.spacing(1)
+ },
+ option: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: 10
+ },
+ button: {
+ fontSize: 12,
+ '& svg': {
+ marginRight: 10
+ }
+ },
+ appBar: {
+ position: 'relative',
+ },
+ flex: {
+ flex: 1,
+ },
+});
+
+class ProductGallery extends React.Component {
+ state = {
+ listView: false,
+ open: false,
+ }
+
+ handleDetailOpen = (product) => {
+ this.setState({ open: true });
+ this.props.showDetail(product);
+ };
+
+ handleClose = () => {
+ this.setState({ open: false });
+ };
+
+ handleSwitchView = () => {
+ this.setState({
+ listView: !this.state.listView
+ });
+ }
+
+ render() {
+ const { classes } = this.props;
+ const { listView, open } = this.state;
+ const {
+ dataProduct,
+ handleAddToCart,
+ productIndex,
+ keyword,
+ } = this.props;
+
+ const getTotalResult = dataArray => {
+ let totalResult = 0;
+ for (let i = 0; i < dataArray.size; i += 1) {
+ if (dataArray.getIn([i, 'name']) === undefined) {
+ return false;
+ }
+ if (dataArray.getIn([i, 'name']).toLowerCase().indexOf(keyword) !== -1) {
+ totalResult += 1;
+ }
+ }
+ return totalResult;
+ };
+
+ return (
+
+
+
+
+ {getTotalResult(dataProduct)}
+ {' '}
+Results
+
+
+
+
+ {
+ dataProduct.map((product, index) => {
+ if (product.get('name').toLowerCase().indexOf(keyword) === -1) {
+ return false;
+ }
+ const itemAttr = {
+ id: product.get('id'),
+ name: product.get('name'),
+ thumbnail: product.get('thumbnail'),
+ price: product.get('price'),
+ quantity: 1
+ };
+ return (
+
+ this.handleDetailOpen(product)}
+ addToCart={() => handleAddToCart(itemAttr)}
+ />
+
+ );
+ })
+ }
+
+
+ );
+ }
+}
+
+ProductGallery.propTypes = {
+ classes: PropTypes.object.isRequired,
+ dataProduct: PropTypes.object.isRequired,
+ handleAddToCart: PropTypes.func.isRequired,
+ showDetail: PropTypes.func.isRequired,
+ productIndex: PropTypes.number.isRequired,
+ keyword: PropTypes.string.isRequired,
+};
+
+export default withStyles(styles)(ProductGallery);
diff --git a/front/odiparpack/app/components/Gallery/photo-jss.js b/front/odiparpack/app/components/Gallery/photo-jss.js
new file mode 100644
index 0000000..61a6961
--- /dev/null
+++ b/front/odiparpack/app/components/Gallery/photo-jss.js
@@ -0,0 +1,79 @@
+const styles = theme => ({
+ masonry: { /* Masonry container */
+ [theme.breakpoints.up('sm')]: {
+ columnCount: 2,
+ },
+ [theme.breakpoints.up('md')]: {
+ columnCount: 3,
+ },
+ columnGap: '1em',
+ columnFill: 'initial',
+ marginTop: 20
+ },
+ item: {
+ display: 'inline-table',
+ margin: `0 0 ${theme.spacing(2)}px`,
+ width: '100%',
+ boxShadow: theme.shadows[4],
+ overflow: 'hidden',
+ borderRadius: 2,
+ transition: 'box-shadow .3s',
+ '&:hover': {
+ cursor: 'pointer',
+ boxShadow: theme.shadows[7],
+ },
+ '& img': {
+ marginBottom: -7
+ }
+ },
+ image: {
+ position: 'relative',
+ [theme.breakpoints.down('xs')]: {
+ width: '100% !important', // Overrides inline-style
+ },
+ '&:hover, &$focusVisible': {
+ zIndex: 1,
+ '& $imageBackdrop': {
+ opacity: 0.15,
+ },
+ },
+ },
+ focusVisible: {},
+ imageButton: {
+ position: 'absolute',
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0,
+ display: 'flex',
+ alignItems: 'flex-end',
+ justifyContent: 'center',
+ color: theme.palette.common.white,
+ paddingBottom: 10
+ },
+ imageBackdrop: {
+ position: 'absolute',
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0,
+ backgroundColor: theme.palette.common.black,
+ opacity: 0,
+ transition: theme.transitions.create('opacity'),
+ },
+ imageTitle: {
+ position: 'relative',
+ padding: `${theme.spacing(2)}px ${theme.spacing(4)}px ${theme.spacing(1) + 6}px`,
+ },
+ imageMarked: {
+ height: 3,
+ width: 18,
+ backgroundColor: theme.palette.common.white,
+ position: 'absolute',
+ bottom: -2,
+ left: 'calc(50% - 9px)',
+ transition: theme.transitions.create('opacity'),
+ },
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/Gallery/product-jss.js b/front/odiparpack/app/components/Gallery/product-jss.js
new file mode 100644
index 0000000..230ebf0
--- /dev/null
+++ b/front/odiparpack/app/components/Gallery/product-jss.js
@@ -0,0 +1,87 @@
+import { blueGrey as dark } from '@material-ui/core/colors';
+const styles = theme => ({
+ root: {
+ flexGrow: 1,
+ },
+ rootSlider: {
+ display: 'flex',
+ flexDirection: 'column',
+ justifyContent: 'center'
+ },
+ item: {
+ textAlign: 'center',
+ '& img': {
+ margin: '10px auto'
+ }
+ },
+ appBar: {
+ position: 'relative',
+ },
+ flex: {
+ flex: 1,
+ },
+ detailContainer: {
+ margin: '-16px auto 0',
+ maxWidth: '100%',
+ [theme.breakpoints.up('lg')]: {
+ maxWidth: 1080,
+ },
+ [theme.breakpoints.up('md')]: {
+ maxWidth: 960,
+ paddingTop: 40,
+ marginTop: 0
+ },
+ [theme.breakpoints.down('sm')]: {
+ overflowX: 'hidden',
+ }
+ },
+ chipDiscount: {
+ background: theme.palette.primary.light,
+ color: theme.palette.primary.dark,
+ marginBottom: 10,
+ },
+ chipSold: {
+ background: dark[500],
+ color: theme.palette.getContrastText(dark[500]),
+ marginBottom: 10,
+ },
+ detailWrap: {
+ padding: 30
+ },
+ title: {
+ marginBottom: 30
+ },
+ price: {
+ display: 'flex',
+ alignItems: 'center',
+ marginTop: 30,
+ padding: '8px 12px',
+ '& > *': {
+ marginRight: 10
+ }
+ },
+ ratting: {
+ borderBottom: `1px solid ${theme.palette.grey[400]}`,
+ marginBottom: 20,
+ },
+ btnArea: {
+ display: 'flex',
+ alignItems: 'center',
+ marginTop: 20,
+ background: theme.palette.grey[100],
+ padding: '10px 20px'
+ },
+ quantity: {
+ width: 40,
+ marginRight: 40,
+ marginLeft: 10,
+ '& input': {
+ textAlign: 'right'
+ }
+ },
+ desc: {
+ padding: '10px 0'
+ }
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/Header/Header.js b/front/odiparpack/app/components/Header/Header.js
new file mode 100644
index 0000000..e1d0bf5
--- /dev/null
+++ b/front/odiparpack/app/components/Header/Header.js
@@ -0,0 +1,63 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import SearchIcon from '@material-ui/icons/Search';
+import MenuIcon from '@material-ui/icons/Menu';
+import { AppBar, Toolbar, IconButton, Hidden } from '@material-ui/core';
+import UserMenu from './UserMenu';
+import styles from './header-jss';
+
+function Header(props) {
+ const {
+ classes,
+ toggleDrawerOpen,
+ margin,
+ turnDarker,
+ } = props;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+Header.propTypes = {
+ classes: PropTypes.object.isRequired,
+ toggleDrawerOpen: PropTypes.func.isRequired,
+ margin: PropTypes.bool.isRequired,
+ turnDarker: PropTypes.bool.isRequired,
+};
+
+export default withStyles(styles)(Header);
diff --git a/front/odiparpack/app/components/Header/UserMenu.js b/front/odiparpack/app/components/Header/UserMenu.js
new file mode 100644
index 0000000..3ec891e
--- /dev/null
+++ b/front/odiparpack/app/components/Header/UserMenu.js
@@ -0,0 +1,177 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import { Link } from 'react-router-dom';
+import Avatar from '@material-ui/core/Avatar';
+import IconButton from '@material-ui/core/IconButton';
+import Button from '@material-ui/core/Button';
+import Info from '@material-ui/icons/Info';
+import Warning from '@material-ui/icons/Warning';
+import Check from '@material-ui/icons/CheckCircle';
+import Error from '@material-ui/icons/RemoveCircle';
+import ExitToApp from '@material-ui/icons/ExitToApp';
+import Badge from '@material-ui/core/Badge';
+import Divider from '@material-ui/core/Divider';
+import Menu from '@material-ui/core/Menu';
+import MenuItem from '@material-ui/core/MenuItem';
+import ListItemIcon from '@material-ui/core/ListItemIcon';
+import ListItemText from '@material-ui/core/ListItemText';
+import ListItemAvatar from '@material-ui/core/ListItemAvatar';
+import Notification from '@material-ui/icons/Notifications';
+import dummy from 'ba-api/dummyContents';
+import messageStyles from 'ba-styles/Messages.scss';
+import avatarApi from 'ba-api/avatars';
+import link from 'ba-api/link';
+import styles from './header-jss';
+
+function UserMenu(props) {
+ const { classes, dark } = props;
+ const [anchorEl, setAnchorEl] = useState(null);
+ const [openMenu, setOpenMenu] = useState(null);
+
+ const handleMenu = menu => (event) => {
+ setOpenMenu(openMenu === menu ? null : menu);
+ setAnchorEl(event.currentTarget);
+ };
+
+ const handleClose = () => {
+ setOpenMenu(null);
+ setAnchorEl(null);
+ };
+
+ return (
+
+ );
+}
+
+UserMenu.propTypes = {
+ classes: PropTypes.object.isRequired,
+ dark: PropTypes.bool,
+};
+
+UserMenu.defaultProps = {
+ dark: false
+};
+
+export default withStyles(styles)(UserMenu);
diff --git a/front/odiparpack/app/components/Header/header-jss.js b/front/odiparpack/app/components/Header/header-jss.js
new file mode 100644
index 0000000..e0a1d6b
--- /dev/null
+++ b/front/odiparpack/app/components/Header/header-jss.js
@@ -0,0 +1,166 @@
+import { fade } from '@material-ui/core/styles/colorManipulator';
+const drawerWidth = 240;
+
+const styles = theme => ({
+ appBar: {
+ position: 'fixed',
+ zIndex: theme.zIndex.drawer + 1,
+ transition: theme.transitions.create(['width', 'margin', 'background'], {
+ easing: theme.transitions.easing.sharp,
+ duration: theme.transitions.duration.leavingScreen,
+ }),
+ boxShadow: 'none !important',
+ '& ::-webkit-input-placeholder': { /* Chrome/Opera/Safari */
+ color: 'rgba(255,255,255,.3)'
+ },
+ '& ::-moz-placeholder': { /* Firefox 19+ */
+ color: 'rgba(255,255,255,.3)'
+ },
+ '& :-ms-input-placeholder': { /* IE 10+ */
+ color: 'rgba(255,255,255,.3)'
+ },
+ '& :-moz-placeholder': { /* Firefox 18- */
+ color: 'rgba(255,255,255,.3)'
+ },
+ '& $menuButton': {
+ marginLeft: theme.spacing(2)
+ }
+ },
+ flex: {
+ flex: 1,
+ textAlign: 'right'
+ },
+ appBarShift: {
+ transition: theme.transitions.create(['width', 'margin', 'background'], {
+ easing: theme.transitions.easing.sharp,
+ duration: theme.transitions.duration.enteringScreen,
+ }),
+ [theme.breakpoints.up('lg')]: {
+ marginLeft: drawerWidth,
+ width: `calc(100% - ${drawerWidth}px)`,
+ },
+ '& $menuButton': {
+ marginLeft: 0
+ }
+ },
+ menuButton: {
+ [theme.breakpoints.up('lg')]: {
+ marginLeft: 0,
+ }
+ },
+ hide: {
+ display: 'none',
+ },
+ textField: {
+ marginLeft: theme.spacing(1),
+ marginRight: theme.spacing(1),
+ width: 200,
+ },
+ container: {
+ display: 'flex',
+ flexWrap: 'wrap',
+ },
+ wrapper: {
+ fontFamily: theme.typography.fontFamily,
+ position: 'relative',
+ marginRight: theme.spacing(2),
+ marginLeft: theme.spacing(1),
+ borderRadius: 2,
+ background: fade(theme.palette.common.white, 0.15),
+ display: 'inline-block',
+ '&:hover': {
+ background: fade(theme.palette.common.white, 0.25),
+ },
+ '& $input': {
+ transition: theme.transitions.create('width'),
+ width: 180,
+ '&:focus': {
+ width: 350,
+ },
+ [theme.breakpoints.down('xs')]: {
+ display: 'none'
+ },
+ },
+ },
+ search: {
+ width: theme.spacing(9),
+ height: '100%',
+ position: 'absolute',
+ pointerEvents: 'none',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ [theme.breakpoints.down('xs')]: {
+ display: 'none'
+ },
+ },
+ input: {
+ font: 'inherit',
+ padding: `${theme.spacing(1)}px ${theme.spacing(1)}px ${theme.spacing(1)}px ${theme.spacing(9)}px`,
+ border: 0,
+ display: 'block',
+ verticalAlign: 'middle',
+ whiteSpace: 'normal',
+ background: 'none',
+ margin: 0, // Reset for Safari
+ color: 'inherit',
+ width: '100%',
+ '&:focus': {
+ outline: 0,
+ },
+ },
+ userMenu: {
+ display: 'flex',
+ alignItems: 'center'
+ },
+ popperClose: {
+ pointerEvents: 'none',
+ zIndex: 2
+ },
+ darker: {
+ background: theme.palette.primary.dark,
+ '&:after': {
+ content: '""',
+ left: -240,
+ width: 'calc(100% + 240px)',
+ position: 'absolute',
+ bottom: -2,
+ height: 1,
+ background: '#000',
+ filter: 'blur(3px)'
+ }
+ },
+ separatorV: {
+ borderLeft: `1px solid ${theme.palette.grey[300]}`,
+ height: 20,
+ margin: '0 10px',
+ opacity: 0.4
+ },
+ notifMenu: {
+ width: 350,
+ '& li': {
+ height: 'auto',
+ '& h3': {
+ overflow: 'hidden',
+ whiteSpace: 'nowrap',
+ textOverflow: 'ellipsis'
+ }
+ }
+ },
+ badgeMenu: {
+ '& span': {
+ top: 0,
+ right: -30
+ }
+ },
+ textNotif: {
+ '& span': {
+ display: 'block',
+ overflow: 'hidden',
+ whiteSpace: 'nowrap',
+ textOverflow: 'ellipsis'
+ }
+ }
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/ImageLightbox/ImageLightbox.js b/front/odiparpack/app/components/ImageLightbox/ImageLightbox.js
new file mode 100644
index 0000000..86e18a6
--- /dev/null
+++ b/front/odiparpack/app/components/ImageLightbox/ImageLightbox.js
@@ -0,0 +1,1794 @@
+/* eslint-disable */
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import Modal from 'react-modal';
+import {
+ translate,
+ getWindowWidth,
+ getWindowHeight,
+ getHighestSafeWindowContext,
+} from './util';
+import {
+ KEYS,
+ MIN_ZOOM_LEVEL,
+ MAX_ZOOM_LEVEL,
+ ZOOM_RATIO,
+ WHEEL_MOVE_X_THRESHOLD,
+ WHEEL_MOVE_Y_THRESHOLD,
+ ZOOM_BUTTON_INCREMENT_SIZE,
+ ACTION_NONE,
+ ACTION_MOVE,
+ ACTION_SWIPE,
+ ACTION_PINCH,
+ SOURCE_ANY,
+ SOURCE_MOUSE,
+ SOURCE_TOUCH,
+ SOURCE_POINTER,
+ MIN_SWIPE_DISTANCE,
+} from './constant';
+
+class ReactImageLightbox extends Component {
+ static isTargetMatchImage(target) {
+ return target && /ril-image-current/.test(target.className);
+ }
+
+ static parseMouseEvent(mouseEvent) {
+ return {
+ id: 'mouse',
+ source: SOURCE_MOUSE,
+ x: parseInt(mouseEvent.clientX, 10),
+ y: parseInt(mouseEvent.clientY, 10),
+ };
+ }
+
+ static parseTouchPointer(touchPointer) {
+ return {
+ id: touchPointer.identifier,
+ source: SOURCE_TOUCH,
+ x: parseInt(touchPointer.clientX, 10),
+ y: parseInt(touchPointer.clientY, 10),
+ };
+ }
+
+ static parsePointerEvent(pointerEvent) {
+ return {
+ id: pointerEvent.pointerId,
+ source: SOURCE_POINTER,
+ x: parseInt(pointerEvent.clientX, 10),
+ y: parseInt(pointerEvent.clientY, 10),
+ };
+ }
+
+ // Request to transition to the previous image
+ static getTransform({
+ x = 0,
+ y = 0,
+ zoom = 1,
+ width,
+ targetWidth
+ }) {
+ let nextX = x;
+ const windowWidth = getWindowWidth();
+ if (width > windowWidth) {
+ nextX += (windowWidth - width) / 2;
+ }
+ const scaleFactor = zoom * (targetWidth / width);
+
+ return {
+ transform: `translate3d(${nextX}px,${y}px,0) scale3d(${scaleFactor},${scaleFactor},1)`,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ //-----------------------------
+ // Animation
+ //-----------------------------
+
+ // Lightbox is closing
+ // When Lightbox is mounted, if animation is enabled it will open with the reverse of the closing animation
+ isClosing: !props.animationDisabled,
+
+ // Component parts should animate (e.g., when images are moving, or image is being zoomed)
+ shouldAnimate: false,
+
+ //-----------------------------
+ // Zoom settings
+ //-----------------------------
+ // Zoom level of image
+ zoomLevel: MIN_ZOOM_LEVEL,
+
+ //-----------------------------
+ // Image position settings
+ //-----------------------------
+ // Horizontal offset from center
+ offsetX: 0,
+
+ // Vertical offset from center
+ offsetY: 0,
+
+ // image load error for srcType
+ loadErrorStatus: {},
+ };
+
+ this.closeIfClickInner = this.closeIfClickInner.bind(this);
+ this.handleImageDoubleClick = this.handleImageDoubleClick.bind(this);
+ this.handleImageMouseWheel = this.handleImageMouseWheel.bind(this);
+ this.handleKeyInput = this.handleKeyInput.bind(this);
+ this.handleMouseUp = this.handleMouseUp.bind(this);
+ this.handleMouseDown = this.handleMouseDown.bind(this);
+ this.handleMouseMove = this.handleMouseMove.bind(this);
+ this.handleOuterMousewheel = this.handleOuterMousewheel.bind(this);
+ this.handleTouchStart = this.handleTouchStart.bind(this);
+ this.handleTouchMove = this.handleTouchMove.bind(this);
+ this.handleTouchEnd = this.handleTouchEnd.bind(this);
+ this.handlePointerEvent = this.handlePointerEvent.bind(this);
+ this.handleCaptionMousewheel = this.handleCaptionMousewheel.bind(this);
+ this.handleWindowResize = this.handleWindowResize.bind(this);
+ this.handleZoomInButtonClick = this.handleZoomInButtonClick.bind(this);
+ this.handleZoomOutButtonClick = this.handleZoomOutButtonClick.bind(this);
+ this.requestClose = this.requestClose.bind(this);
+ this.requestMoveNext = this.requestMoveNext.bind(this);
+ this.requestMovePrev = this.requestMovePrev.bind(this);
+ }
+
+ componentWillMount() {
+ // Timeouts - always clear it before umount
+ this.timeouts = [];
+
+ // Current action
+ this.currentAction = ACTION_NONE;
+
+ // Events source
+ this.eventsSource = SOURCE_ANY;
+
+ // Empty pointers list
+ this.pointerList = [];
+
+ // Prevent inner close
+ this.preventInnerClose = false;
+ this.preventInnerCloseTimeout = null;
+
+ // Used to disable animation when changing props.mainSrc|nextSrc|prevSrc
+ this.keyPressed = false;
+
+ // Used to store load state / dimensions of images
+ this.imageCache = {};
+
+ // Time the last keydown event was called (used in keyboard action rate limiting)
+ this.lastKeyDownTime = 0;
+
+ // Used for debouncing window resize event
+ this.resizeTimeout = null;
+
+ // Used to determine when actions are triggered by the scroll wheel
+ this.wheelActionTimeout = null;
+ this.resetScrollTimeout = null;
+ this.scrollX = 0;
+ this.scrollY = 0;
+
+ // Used in panning zoomed images
+ this.moveStartX = 0;
+ this.moveStartY = 0;
+ this.moveStartOffsetX = 0;
+ this.moveStartOffsetY = 0;
+
+ // Used to swipe
+ this.swipeStartX = 0;
+ this.swipeStartY = 0;
+ this.swipeEndX = 0;
+ this.swipeEndY = 0;
+
+ // Used to pinch
+ this.pinchTouchList = null;
+ this.pinchDistance = 0;
+
+ // Used to differentiate between images with identical src
+ this.keyCounter = 0;
+
+ // Used to detect a move when all src's remain unchanged (four or more of the same image in a row)
+ this.moveRequested = false;
+
+ if (!this.props.animationDisabled) {
+ // Make opening animation play
+ this.setState({ isClosing: false });
+ }
+ }
+
+ componentDidMount() {
+ // Prevents cross-origin errors when using a cross-origin iframe
+ this.windowContext = getHighestSafeWindowContext();
+
+ this.listeners = {
+ resize: this.handleWindowResize,
+ mouseup: this.handleMouseUp,
+ touchend: this.handleTouchEnd,
+ touchcancel: this.handleTouchEnd,
+ pointerdown: this.handlePointerEvent,
+ pointermove: this.handlePointerEvent,
+ pointerup: this.handlePointerEvent,
+ pointercancel: this.handlePointerEvent,
+ };
+ Object.keys(this.listeners).forEach(type => {
+ this.windowContext.addEventListener(type, this.listeners[type]);
+ });
+
+ this.loadAllImages();
+ }
+
+ componentWillReceiveProps(nextProps) {
+ // Iterate through the source types for prevProps and nextProps to
+ // determine if any of the sources changed
+ let sourcesChanged = false;
+ const prevSrcDict = {};
+ const nextSrcDict = {};
+ this.getSrcTypes().forEach(srcType => {
+ if (this.props[srcType.name] !== nextProps[srcType.name]) {
+ sourcesChanged = true;
+
+ prevSrcDict[this.props[srcType.name]] = true;
+ nextSrcDict[nextProps[srcType.name]] = true;
+ }
+ });
+
+ if (sourcesChanged || this.moveRequested) {
+ // Reset the loaded state for images not rendered next
+ Object.keys(prevSrcDict).forEach(prevSrc => {
+ if (!(prevSrc in nextSrcDict) && prevSrc in this.imageCache) {
+ this.imageCache[prevSrc].loaded = false;
+ }
+ });
+
+ this.moveRequested = false;
+
+ // Load any new images
+ this.loadAllImages(nextProps);
+ }
+ }
+
+ shouldComponentUpdate() {
+ // Wait for move...
+ return !this.moveRequested;
+ }
+
+ componentWillUnmount() {
+ this.didUnmount = true;
+ Object.keys(this.listeners).forEach(type => {
+ this.windowContext.removeEventListener(type, this.listeners[type]);
+ });
+ this.timeouts.forEach(tid => clearTimeout(tid));
+ }
+
+ setTimeout(func, time) {
+ const id = setTimeout(() => {
+ this.timeouts = this.timeouts.filter(tid => tid !== id);
+ func();
+ }, time);
+ this.timeouts.push(id);
+ return id;
+ }
+
+ setPreventInnerClose() {
+ if (this.preventInnerCloseTimeout) {
+ this.clearTimeout(this.preventInnerCloseTimeout);
+ }
+ this.preventInnerClose = true;
+ this.preventInnerCloseTimeout = this.setTimeout(() => {
+ this.preventInnerClose = false;
+ this.preventInnerCloseTimeout = null;
+ }, 100);
+ }
+
+ // Get info for the best suited image to display with the given srcType
+ getBestImageForType(srcType) {
+ let imageSrc = this.props[srcType];
+ let fitSizes = {};
+
+ if (this.isImageLoaded(imageSrc)) {
+ // Use full-size image if available
+ fitSizes = this.getFitSizes(
+ this.imageCache[imageSrc].width,
+ this.imageCache[imageSrc].height
+ );
+ } else if (this.isImageLoaded(this.props[`${srcType}Thumbnail`])) {
+ // Fall back to using thumbnail if the image has not been loaded
+ imageSrc = this.props[`${srcType}Thumbnail`];
+ fitSizes = this.getFitSizes(
+ this.imageCache[imageSrc].width,
+ this.imageCache[imageSrc].height,
+ true
+ );
+ } else {
+ return null;
+ }
+
+ return {
+ src: imageSrc,
+ height: this.imageCache[imageSrc].height,
+ width: this.imageCache[imageSrc].width,
+ targetHeight: fitSizes.height,
+ targetWidth: fitSizes.width,
+ };
+ }
+
+ // Get sizing for when an image is larger than the window
+ getFitSizes(width, height, stretch) {
+ const boxSize = this.getLightboxRect();
+ let maxHeight = boxSize.height - (this.props.imagePadding * 2);
+ let maxWidth = boxSize.width - (this.props.imagePadding * 2);
+
+ if (!stretch) {
+ maxHeight = Math.min(maxHeight, height);
+ maxWidth = Math.min(maxWidth, width);
+ }
+
+ const maxRatio = maxWidth / maxHeight;
+ const srcRatio = width / height;
+
+ if (maxRatio > srcRatio) {
+ // height is the constraining dimension of the photo
+ return {
+ width: (width * maxHeight) / height,
+ height: maxHeight,
+ };
+ }
+
+ return {
+ width: maxWidth,
+ height: (height * maxWidth) / width,
+ };
+ }
+
+ getMaxOffsets(zoomLevel = this.state.zoomLevel) {
+ const currentImageInfo = this.getBestImageForType('mainSrc');
+ if (currentImageInfo === null) {
+ return {
+ maxX: 0,
+ minX: 0,
+ maxY: 0,
+ minY: 0
+ };
+ }
+
+ const boxSize = this.getLightboxRect();
+ const zoomMultiplier = this.getZoomMultiplier(zoomLevel);
+
+ let maxX = 0;
+ if ((zoomMultiplier * currentImageInfo.width) - boxSize.width < 0) {
+ // if there is still blank space in the X dimension, don't limit except to the opposite edge
+ maxX = (boxSize.width - (zoomMultiplier * currentImageInfo.width)) / 2;
+ } else {
+ maxX = ((zoomMultiplier * currentImageInfo.width) - boxSize.width) / 2;
+ }
+
+ let maxY = 0;
+ if ((zoomMultiplier * currentImageInfo.height) - boxSize.height < 0) {
+ // if there is still blank space in the Y dimension, don't limit except to the opposite edge
+ maxY = (boxSize.height - (zoomMultiplier * currentImageInfo.height)) / 2;
+ } else {
+ maxY = ((zoomMultiplier * currentImageInfo.height) - boxSize.height) / 2;
+ }
+
+ return {
+ maxX,
+ maxY,
+ minX: -1 * maxX,
+ minY: -1 * maxY,
+ };
+ }
+
+ // Get image src types
+ getSrcTypes() {
+ return [
+ {
+ name: 'mainSrc',
+ keyEnding: `i${this.keyCounter}`,
+ },
+ {
+ name: 'mainSrcThumbnail',
+ keyEnding: `t${this.keyCounter}`,
+ },
+ {
+ name: 'nextSrc',
+ keyEnding: `i${this.keyCounter + 1}`,
+ },
+ {
+ name: 'nextSrcThumbnail',
+ keyEnding: `t${this.keyCounter + 1}`,
+ },
+ {
+ name: 'prevSrc',
+ keyEnding: `i${this.keyCounter - 1}`,
+ },
+ {
+ name: 'prevSrcThumbnail',
+ keyEnding: `t${this.keyCounter - 1}`,
+ },
+ ];
+ }
+
+ /**
+ * Get sizing when the image is scaled
+ */
+ getZoomMultiplier(zoomLevel = this.state.zoomLevel) {
+ return ZOOM_RATIO ** zoomLevel;
+ }
+
+ /**
+ * Get the size of the lightbox in pixels
+ */
+ getLightboxRect() {
+ if (this.outerEl) {
+ return this.outerEl.getBoundingClientRect();
+ }
+
+ return {
+ width: getWindowWidth(),
+ height: getWindowHeight(),
+ top: 0,
+ right: 0,
+ bottom: 0,
+ left: 0,
+ };
+ }
+
+ clearTimeout(id) {
+ this.timeouts = this.timeouts.filter(tid => tid !== id);
+ clearTimeout(id);
+ }
+
+ // Change zoom level
+ changeZoom(zoomLevel, clientX, clientY) {
+ // Ignore if zoom disabled
+ if (!this.props.enableZoom) {
+ return;
+ }
+
+ // Constrain zoom level to the set bounds
+ const nextZoomLevel = Math.max(
+ MIN_ZOOM_LEVEL,
+ Math.min(MAX_ZOOM_LEVEL, zoomLevel)
+ );
+
+ // Ignore requests that don't change the zoom level
+ if (nextZoomLevel === this.state.zoomLevel) {
+ return;
+ } else if (nextZoomLevel === MIN_ZOOM_LEVEL) {
+ // Snap back to center if zoomed all the way out
+ this.setState({
+ zoomLevel: nextZoomLevel,
+ offsetX: 0,
+ offsetY: 0,
+ });
+
+ return;
+ }
+
+ const imageBaseSize = this.getBestImageForType('mainSrc');
+ if (imageBaseSize === null) {
+ return;
+ }
+
+ const currentZoomMultiplier = this.getZoomMultiplier();
+ const nextZoomMultiplier = this.getZoomMultiplier(nextZoomLevel);
+
+ // Default to the center of the image to zoom when no mouse position specified
+ const boxRect = this.getLightboxRect();
+ const pointerX =
+ typeof clientX !== 'undefined'
+ ? clientX - boxRect.left
+ : boxRect.width / 2;
+ const pointerY =
+ typeof clientY !== 'undefined'
+ ? clientY - boxRect.top
+ : boxRect.height / 2;
+
+ const currentImageOffsetX =
+ (boxRect.width - (imageBaseSize.width * currentZoomMultiplier)) / 2;
+ const currentImageOffsetY =
+ (boxRect.height - (imageBaseSize.height * currentZoomMultiplier)) / 2;
+
+ const currentImageRealOffsetX = currentImageOffsetX - this.state.offsetX;
+ const currentImageRealOffsetY = currentImageOffsetY - this.state.offsetY;
+
+ const currentPointerXRelativeToImage =
+ (pointerX - currentImageRealOffsetX) / currentZoomMultiplier;
+ const currentPointerYRelativeToImage =
+ (pointerY - currentImageRealOffsetY) / currentZoomMultiplier;
+
+ const nextImageRealOffsetX =
+ pointerX - (currentPointerXRelativeToImage * nextZoomMultiplier);
+ const nextImageRealOffsetY =
+ pointerY - (currentPointerYRelativeToImage * nextZoomMultiplier);
+
+ const nextImageOffsetX =
+ (boxRect.width - (imageBaseSize.width * nextZoomMultiplier)) / 2;
+ const nextImageOffsetY =
+ (boxRect.height - (imageBaseSize.height * nextZoomMultiplier)) / 2;
+
+ let nextOffsetX = nextImageOffsetX - nextImageRealOffsetX;
+ let nextOffsetY = nextImageOffsetY - nextImageRealOffsetY;
+
+ // When zooming out, limit the offset so things don't get left askew
+ if (this.currentAction !== ACTION_PINCH) {
+ const maxOffsets = this.getMaxOffsets();
+ if (this.state.zoomLevel > nextZoomLevel) {
+ nextOffsetX = Math.max(
+ maxOffsets.minX,
+ Math.min(maxOffsets.maxX, nextOffsetX)
+ );
+ nextOffsetY = Math.max(
+ maxOffsets.minY,
+ Math.min(maxOffsets.maxY, nextOffsetY)
+ );
+ }
+ }
+
+ this.setState({
+ zoomLevel: nextZoomLevel,
+ offsetX: nextOffsetX,
+ offsetY: nextOffsetY,
+ });
+ }
+
+ closeIfClickInner(event) {
+ if (
+ !this.preventInnerClose &&
+ event.target.className.search(/\bril-inner\b/) > -1
+ ) {
+ this.requestClose(event);
+ }
+ }
+
+ /**
+ * Handle user keyboard actions
+ */
+ handleKeyInput(event) {
+ event.stopPropagation();
+
+ // Ignore key input during animations
+ if (this.isAnimating()) {
+ return;
+ }
+
+ // Allow slightly faster navigation through the images when user presses keys repeatedly
+ if (event.type === 'keyup') {
+ this.lastKeyDownTime -= this.props.keyRepeatKeyupBonus;
+ return;
+ }
+
+ const keyCode = event.which || event.keyCode;
+
+ // Ignore key presses that happen too close to each other (when rapid fire key pressing or holding down the key)
+ // But allow it if it's a lightbox closing action
+ const currentTime = new Date();
+ if (
+ currentTime.getTime() - this.lastKeyDownTime <
+ this.props.keyRepeatLimit &&
+ keyCode !== KEYS.ESC
+ ) {
+ return;
+ }
+ this.lastKeyDownTime = currentTime.getTime();
+
+ switch (keyCode) {
+ // ESC key closes the lightbox
+ case KEYS.ESC:
+ event.preventDefault();
+ this.requestClose(event);
+ break;
+
+ // Left arrow key moves to previous image
+ case KEYS.LEFT_ARROW:
+ if (!this.props.prevSrc) {
+ return;
+ }
+
+ event.preventDefault();
+ this.keyPressed = true;
+ this.requestMovePrev(event);
+ break;
+
+ // Right arrow key moves to next image
+ case KEYS.RIGHT_ARROW:
+ if (!this.props.nextSrc) {
+ return;
+ }
+
+ event.preventDefault();
+ this.keyPressed = true;
+ this.requestMoveNext(event);
+ break;
+
+ default:
+ }
+ }
+
+ /**
+ * Handle a mouse wheel event over the lightbox container
+ */
+ handleOuterMousewheel(event) {
+ // Prevent scrolling of the background
+ event.preventDefault();
+ event.stopPropagation();
+
+ const xThreshold = WHEEL_MOVE_X_THRESHOLD;
+ let actionDelay = 0;
+ const imageMoveDelay = 500;
+
+ this.clearTimeout(this.resetScrollTimeout);
+ this.resetScrollTimeout = this.setTimeout(() => {
+ this.scrollX = 0;
+ this.scrollY = 0;
+ }, 300);
+
+ // Prevent rapid-fire zoom behavior
+ if (this.wheelActionTimeout !== null || this.isAnimating()) {
+ return;
+ }
+
+ if (Math.abs(event.deltaY) < Math.abs(event.deltaX)) {
+ // handle horizontal scrolls with image moves
+ this.scrollY = 0;
+ this.scrollX += event.deltaX;
+
+ const bigLeapX = xThreshold / 2;
+ // If the scroll amount has accumulated sufficiently, or a large leap was taken
+ if (this.scrollX >= xThreshold || event.deltaX >= bigLeapX) {
+ // Scroll right moves to next
+ this.requestMoveNext(event);
+ actionDelay = imageMoveDelay;
+ this.scrollX = 0;
+ } else if (
+ this.scrollX <= -1 * xThreshold ||
+ event.deltaX <= -1 * bigLeapX
+ ) {
+ // Scroll left moves to previous
+ this.requestMovePrev(event);
+ actionDelay = imageMoveDelay;
+ this.scrollX = 0;
+ }
+ }
+
+ // Allow successive actions after the set delay
+ if (actionDelay !== 0) {
+ this.wheelActionTimeout = this.setTimeout(() => {
+ this.wheelActionTimeout = null;
+ }, actionDelay);
+ }
+ }
+
+ handleImageMouseWheel(event) {
+ event.preventDefault();
+ const yThreshold = WHEEL_MOVE_Y_THRESHOLD;
+
+ if (Math.abs(event.deltaY) >= Math.abs(event.deltaX)) {
+ event.stopPropagation();
+ // If the vertical scroll amount was large enough, perform a zoom
+ if (Math.abs(event.deltaY) < yThreshold) {
+ return;
+ }
+
+ this.scrollX = 0;
+ this.scrollY += event.deltaY;
+
+ this.changeZoom(
+ this.state.zoomLevel - event.deltaY,
+ event.clientX,
+ event.clientY
+ );
+ }
+ }
+
+ /**
+ * Handle a double click on the current image
+ */
+ handleImageDoubleClick(event) {
+ if (this.state.zoomLevel > MIN_ZOOM_LEVEL) {
+ // A double click when zoomed in zooms all the way out
+ this.changeZoom(MIN_ZOOM_LEVEL, event.clientX, event.clientY);
+ } else {
+ // A double click when zoomed all the way out zooms in
+ this.changeZoom(
+ this.state.zoomLevel + ZOOM_BUTTON_INCREMENT_SIZE,
+ event.clientX,
+ event.clientY
+ );
+ }
+ }
+
+ shouldHandleEvent(source) {
+ if (this.eventsSource === source) {
+ return true;
+ }
+ if (this.eventsSource === SOURCE_ANY) {
+ this.eventsSource = source;
+ return true;
+ }
+ switch (source) {
+ case SOURCE_MOUSE:
+ return false;
+ case SOURCE_TOUCH:
+ this.eventsSource = SOURCE_TOUCH;
+ this.filterPointersBySource();
+ return true;
+ case SOURCE_POINTER:
+ if (this.eventsSource === SOURCE_MOUSE) {
+ this.eventsSource = SOURCE_POINTER;
+ this.filterPointersBySource();
+ return true;
+ }
+ return false;
+ default:
+ return false;
+ }
+ }
+
+ addPointer(pointer) {
+ this.pointerList.push(pointer);
+ }
+
+ removePointer(pointer) {
+ this.pointerList = this.pointerList.filter(({ id }) => id !== pointer.id);
+ }
+
+ filterPointersBySource() {
+ this.pointerList = this.pointerList.filter(
+ ({ source }) => source === this.eventsSource
+ );
+ }
+
+ handleMouseDown(event) {
+ if (
+ this.shouldHandleEvent(SOURCE_MOUSE) &&
+ ReactImageLightbox.isTargetMatchImage(event.target)
+ ) {
+ this.addPointer(ReactImageLightbox.parseMouseEvent(event));
+ this.multiPointerStart(event);
+ }
+ }
+
+ handleMouseMove(event) {
+ if (this.shouldHandleEvent(SOURCE_MOUSE)) {
+ this.multiPointerMove(event, [ReactImageLightbox.parseMouseEvent(event)]);
+ }
+ }
+
+ handleMouseUp(event) {
+ if (this.shouldHandleEvent(SOURCE_MOUSE)) {
+ this.removePointer(ReactImageLightbox.parseMouseEvent(event));
+ this.multiPointerEnd(event);
+ }
+ }
+
+ handlePointerEvent(event) {
+ if (this.shouldHandleEvent(SOURCE_POINTER)) {
+ switch (event.type) {
+ case 'pointerdown':
+ if (ReactImageLightbox.isTargetMatchImage(event.target)) {
+ this.addPointer(ReactImageLightbox.parsePointerEvent(event));
+ this.multiPointerStart(event);
+ }
+ break;
+ case 'pointermove':
+ this.multiPointerMove(event, [
+ ReactImageLightbox.parsePointerEvent(event),
+ ]);
+ break;
+ case 'pointerup':
+ case 'pointercancel':
+ this.removePointer(ReactImageLightbox.parsePointerEvent(event));
+ this.multiPointerEnd(event);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ handleTouchStart(event) {
+ if (
+ this.shouldHandleEvent(SOURCE_TOUCH) &&
+ ReactImageLightbox.isTargetMatchImage(event.target)
+ ) {
+ [].forEach.call(event.changedTouches, eventTouch =>
+ this.addPointer(ReactImageLightbox.parseTouchPointer(eventTouch))
+ );
+ this.multiPointerStart(event);
+ }
+ }
+
+ handleTouchMove(event) {
+ if (this.shouldHandleEvent(SOURCE_TOUCH)) {
+ this.multiPointerMove(
+ event,
+ [].map.call(event.changedTouches, eventTouch =>
+ ReactImageLightbox.parseTouchPointer(eventTouch)
+ )
+ );
+ }
+ }
+
+ handleTouchEnd(event) {
+ if (this.shouldHandleEvent(SOURCE_TOUCH)) {
+ [].map.call(event.changedTouches, touch =>
+ this.removePointer(ReactImageLightbox.parseTouchPointer(touch))
+ );
+ this.multiPointerEnd(event);
+ }
+ }
+
+ decideMoveOrSwipe(pointer) {
+ if (this.state.zoomLevel <= MIN_ZOOM_LEVEL) {
+ this.handleSwipeStart(pointer);
+ } else {
+ this.handleMoveStart(pointer);
+ }
+ }
+
+ multiPointerStart(event) {
+ this.handleEnd(null);
+ switch (this.pointerList.length) {
+ case 1: {
+ event.preventDefault();
+ this.decideMoveOrSwipe(this.pointerList[0]);
+ break;
+ }
+ case 2: {
+ event.preventDefault();
+ this.handlePinchStart(this.pointerList);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ multiPointerMove(event, pointerList) {
+ switch (this.currentAction) {
+ case ACTION_MOVE: {
+ event.preventDefault();
+ this.handleMove(pointerList[0]);
+ break;
+ }
+ case ACTION_SWIPE: {
+ event.preventDefault();
+ this.handleSwipe(pointerList[0]);
+ break;
+ }
+ case ACTION_PINCH: {
+ event.preventDefault();
+ this.handlePinch(pointerList);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ multiPointerEnd(event) {
+ if (this.currentAction !== ACTION_NONE) {
+ this.setPreventInnerClose();
+ this.handleEnd(event);
+ }
+ switch (this.pointerList.length) {
+ case 0: {
+ this.eventsSource = SOURCE_ANY;
+ break;
+ }
+ case 1: {
+ event.preventDefault();
+ this.decideMoveOrSwipe(this.pointerList[0]);
+ break;
+ }
+ case 2: {
+ event.preventDefault();
+ this.handlePinchStart(this.pointerList);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ handleEnd(event) {
+ switch (this.currentAction) {
+ case ACTION_MOVE:
+ this.handleMoveEnd(event);
+ break;
+ case ACTION_SWIPE:
+ this.handleSwipeEnd(event);
+ break;
+ case ACTION_PINCH:
+ this.handlePinchEnd(event);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Handle move start over the lightbox container
+ // This happens:
+ // - On a mouseDown event
+ // - On a touchstart event
+ handleMoveStart({ x: clientX, y: clientY }) {
+ if (!this.props.enableZoom) {
+ return;
+ }
+ this.currentAction = ACTION_MOVE;
+ this.moveStartX = clientX;
+ this.moveStartY = clientY;
+ this.moveStartOffsetX = this.state.offsetX;
+ this.moveStartOffsetY = this.state.offsetY;
+ }
+
+ // Handle dragging over the lightbox container
+ // This happens:
+ // - After a mouseDown and before a mouseUp event
+ // - After a touchstart and before a touchend event
+ handleMove({ x: clientX, y: clientY }) {
+ const newOffsetX = (this.moveStartX - clientX) + this.moveStartOffsetX;
+ const newOffsetY = (this.moveStartY - clientY) + this.moveStartOffsetY;
+ if (
+ this.state.offsetX !== newOffsetX ||
+ this.state.offsetY !== newOffsetY
+ ) {
+ this.setState({
+ offsetX: newOffsetX,
+ offsetY: newOffsetY,
+ });
+ }
+ }
+
+ handleMoveEnd() {
+ this.currentAction = ACTION_NONE;
+ this.moveStartX = 0;
+ this.moveStartY = 0;
+ this.moveStartOffsetX = 0;
+ this.moveStartOffsetY = 0;
+ // Snap image back into frame if outside max offset range
+ const maxOffsets = this.getMaxOffsets();
+ const nextOffsetX = Math.max(
+ maxOffsets.minX,
+ Math.min(maxOffsets.maxX, this.state.offsetX)
+ );
+ const nextOffsetY = Math.max(
+ maxOffsets.minY,
+ Math.min(maxOffsets.maxY, this.state.offsetY)
+ );
+ if (
+ nextOffsetX !== this.state.offsetX ||
+ nextOffsetY !== this.state.offsetY
+ ) {
+ this.setState({
+ offsetX: nextOffsetX,
+ offsetY: nextOffsetY,
+ shouldAnimate: true,
+ });
+ this.setTimeout(() => {
+ this.setState({ shouldAnimate: false });
+ }, this.props.animationDuration);
+ }
+ }
+
+ handleSwipeStart({ x: clientX, y: clientY }) {
+ this.currentAction = ACTION_SWIPE;
+ this.swipeStartX = clientX;
+ this.swipeStartY = clientY;
+ this.swipeEndX = clientX;
+ this.swipeEndY = clientY;
+ }
+
+ handleSwipe({ x: clientX, y: clientY }) {
+ this.swipeEndX = clientX;
+ this.swipeEndY = clientY;
+ }
+
+ handleSwipeEnd(event) {
+ const xDiff = this.swipeEndX - this.swipeStartX;
+ const xDiffAbs = Math.abs(xDiff);
+ const yDiffAbs = Math.abs(this.swipeEndY - this.swipeStartY);
+
+ this.currentAction = ACTION_NONE;
+ this.swipeStartX = 0;
+ this.swipeStartY = 0;
+ this.swipeEndX = 0;
+ this.swipeEndY = 0;
+
+ if (!event || this.isAnimating() || xDiffAbs < yDiffAbs * 1.5) {
+ return;
+ }
+
+ if (xDiffAbs < MIN_SWIPE_DISTANCE) {
+ const boxRect = this.getLightboxRect();
+ if (xDiffAbs < boxRect.width / 4) {
+ return;
+ }
+ }
+
+ if (xDiff > 0 && this.props.prevSrc) {
+ event.preventDefault();
+ this.requestMovePrev();
+ } else if (xDiff < 0 && this.props.nextSrc) {
+ event.preventDefault();
+ this.requestMoveNext();
+ }
+ }
+
+ calculatePinchDistance([a, b] = this.pinchTouchList) {
+ return Math.sqrt(((a.x - b.x) ** 2) + ((a.y - b.y) ** 2));
+ }
+
+ calculatePinchCenter([a, b] = this.pinchTouchList) {
+ return {
+ x: a.x - ((a.x - b.x) / 2),
+ y: a.y - ((a.y - b.y) / 2),
+ };
+ }
+
+ handlePinchStart(pointerList) {
+ if (!this.props.enableZoom) {
+ return;
+ }
+ this.currentAction = ACTION_PINCH;
+ this.pinchTouchList = pointerList.map(({ id, x, y }) => ({ id, x, y }));
+ this.pinchDistance = this.calculatePinchDistance();
+ }
+
+ handlePinch(pointerList) {
+ this.pinchTouchList = this.pinchTouchList.map(oldPointer => {
+ for (let i = 0; i < pointerList.length; i += 1) {
+ if (pointerList[i].id === oldPointer.id) {
+ return pointerList[i];
+ }
+ }
+
+ return oldPointer;
+ });
+
+ const newDistance = this.calculatePinchDistance();
+
+ const zoomLevel = (this.state.zoomLevel + newDistance) - this.pinchDistance;
+
+ this.pinchDistance = newDistance;
+ const { x: clientX, y: clientY } = this.calculatePinchCenter(
+ this.pinchTouchList
+ );
+ this.changeZoom(zoomLevel, clientX, clientY);
+ }
+
+ handlePinchEnd() {
+ this.currentAction = ACTION_NONE;
+ this.pinchTouchList = null;
+ this.pinchDistance = 0;
+ }
+
+ // Handle the window resize event
+ handleWindowResize() {
+ this.clearTimeout(this.resizeTimeout);
+ this.resizeTimeout = this.setTimeout(this.forceUpdate.bind(this), 100);
+ }
+
+ handleZoomInButtonClick() {
+ this.changeZoom(this.state.zoomLevel + ZOOM_BUTTON_INCREMENT_SIZE);
+ }
+
+ handleZoomOutButtonClick() {
+ this.changeZoom(this.state.zoomLevel - ZOOM_BUTTON_INCREMENT_SIZE);
+ }
+
+ handleCaptionMousewheel(event) {
+ event.stopPropagation();
+
+ if (!this.caption) {
+ return;
+ }
+
+ const { height } = this.caption.getBoundingClientRect();
+ const { scrollHeight, scrollTop } = this.caption;
+ if (
+ (event.deltaY > 0 && height + scrollTop >= scrollHeight) ||
+ (event.deltaY < 0 && scrollTop <= 0)
+ ) {
+ event.preventDefault();
+ }
+ }
+
+ // Detach key and mouse input events
+ isAnimating() {
+ return this.state.shouldAnimate || this.state.isClosing;
+ }
+
+ // Check if image is loaded
+ isImageLoaded(imageSrc) {
+ return (
+ imageSrc &&
+ imageSrc in this.imageCache &&
+ this.imageCache[imageSrc].loaded
+ );
+ }
+
+ // Load image from src and call callback with image width and height on load
+ loadImage(srcType, imageSrc, done) {
+ // Return the image info if it is already cached
+ if (this.isImageLoaded(imageSrc)) {
+ this.setTimeout(() => {
+ done();
+ }, 1);
+ return;
+ }
+
+ const inMemoryImage = new global.Image();
+
+ if (this.props.imageCrossOrigin) {
+ inMemoryImage.crossOrigin = this.props.imageCrossOrigin;
+ }
+
+ inMemoryImage.onerror = errorEvent => {
+ this.props.onImageLoadError(imageSrc, srcType, errorEvent);
+
+ // failed to load so set the state loadErrorStatus
+ this.setState(prevState => ({
+ loadErrorStatus: { ...prevState.loadErrorStatus, [srcType]: true },
+ }));
+
+ done(errorEvent);
+ };
+
+ inMemoryImage.onload = () => {
+ this.props.onImageLoad(imageSrc, srcType, inMemoryImage);
+
+ this.imageCache[imageSrc] = {
+ loaded: true,
+ width: inMemoryImage.width,
+ height: inMemoryImage.height,
+ };
+
+ done();
+ };
+
+ inMemoryImage.src = imageSrc;
+ }
+
+ // Load all images and their thumbnails
+ loadAllImages(props = this.props) {
+ const generateLoadDoneCallback = (srcType, imageSrc) => err => {
+ // Give up showing image on error
+ if (err) {
+ return;
+ }
+
+ // Don't rerender if the src is not the same as when the load started
+ // or if the component has unmounted
+ if (this.props[srcType] !== imageSrc || this.didUnmount) {
+ return;
+ }
+
+ // Force rerender with the new image
+ this.forceUpdate();
+ };
+
+ // Load the images
+ this.getSrcTypes().forEach(srcType => {
+ const type = srcType.name;
+
+ // there is no error when we try to load it initially
+ if (props[type] && this.state.loadErrorStatus[type]) {
+ this.setState(prevState => ({
+ loadErrorStatus: { ...prevState.loadErrorStatus, [type]: false },
+ }));
+ }
+
+ // Load unloaded images
+ if (props[type] && !this.isImageLoaded(props[type])) {
+ this.loadImage(
+ type,
+ props[type],
+ generateLoadDoneCallback(type, props[type])
+ );
+ }
+ });
+ }
+
+ // Request that the lightbox be closed
+ requestClose(event) {
+ // Call the parent close request
+ const closeLightbox = () => this.props.onCloseRequest(event);
+
+ if (
+ this.props.animationDisabled ||
+ (event.type === 'keydown' && !this.props.animationOnKeyInput)
+ ) {
+ // No animation
+ closeLightbox();
+ return;
+ }
+
+ // With animation
+ // Start closing animation
+ this.setState({ isClosing: true });
+
+ // Perform the actual closing at the end of the animation
+ this.setTimeout(closeLightbox, this.props.animationDuration);
+ }
+
+ requestMove(direction, event) {
+ // Reset the zoom level on image move
+ const nextState = {
+ zoomLevel: MIN_ZOOM_LEVEL,
+ offsetX: 0,
+ offsetY: 0,
+ };
+
+ // Enable animated states
+ if (
+ !this.props.animationDisabled &&
+ (!this.keyPressed || this.props.animationOnKeyInput)
+ ) {
+ nextState.shouldAnimate = true;
+ this.setTimeout(
+ () => this.setState({ shouldAnimate: false }),
+ this.props.animationDuration
+ );
+ }
+ this.keyPressed = false;
+
+ this.moveRequested = true;
+
+ if (direction === 'prev') {
+ this.keyCounter -= 1;
+ this.setState(nextState);
+ this.props.onMovePrevRequest(event);
+ } else {
+ this.keyCounter += 1;
+ this.setState(nextState);
+ this.props.onMoveNextRequest(event);
+ }
+ }
+
+ // Request to transition to the next image
+ requestMoveNext(event) {
+ this.requestMove('next', event);
+ }
+
+ // Request to transition to the previous image
+ requestMovePrev(event) {
+ this.requestMove('prev', event);
+ }
+
+ render() {
+ const {
+ animationDisabled,
+ animationDuration,
+ clickOutsideToClose,
+ discourageDownloads,
+ enableZoom,
+ imageTitle,
+ nextSrc,
+ prevSrc,
+ toolbarButtons,
+ reactModalStyle,
+ onAfterOpen,
+ imageCrossOrigin,
+ reactModalProps,
+ } = this.props;
+ const {
+ zoomLevel,
+ offsetX,
+ offsetY,
+ isClosing,
+ loadErrorStatus,
+ } = this.state;
+
+ const boxSize = this.getLightboxRect();
+ let transitionStyle = {};
+
+ // Transition settings for sliding animations
+ if (!animationDisabled && this.isAnimating()) {
+ transitionStyle = {
+ ...transitionStyle,
+ transition: `transform ${animationDuration}ms`,
+ };
+ }
+
+ // Key endings to differentiate between images with the same src
+ const keyEndings = {};
+ this.getSrcTypes().forEach(({ name, keyEnding }) => {
+ keyEndings[name] = keyEnding;
+ });
+
+ // Images to be displayed
+ const images = [];
+ const addImage = (srcType, imageClass, transforms) => {
+ // Ignore types that have no source defined for their full size image
+ if (!this.props[srcType]) {
+ return;
+ }
+ const bestImageInfo = this.getBestImageForType(srcType);
+
+ const imageStyle = {
+ ...transitionStyle,
+ ...ReactImageLightbox.getTransform({
+ ...transforms,
+ ...bestImageInfo,
+ }),
+ };
+
+ if (zoomLevel > MIN_ZOOM_LEVEL) {
+ imageStyle.cursor = 'move';
+ }
+
+ // support IE 9 and 11
+ const hasTrueValue = object =>
+ Object.keys(object).some(key => object[key]);
+
+ // when error on one of the loads then push custom error stuff
+ if (bestImageInfo === null && hasTrueValue(loadErrorStatus)) {
+ images.push(
+
+
+ {this.props.imageLoadErrorMessage}
+
+
+ );
+
+ return;
+ } else if (bestImageInfo === null) {
+ const loadingIcon = (
+
+ {[...new Array(12)].map((_, index) => (
+
+ ))}
+
+ );
+
+ // Fall back to loading icon if the thumbnail has not been loaded
+ images.push(
+
+ );
+
+ return;
+ }
+
+ const imageSrc = bestImageInfo.src;
+ if (discourageDownloads) {
+ imageStyle.backgroundImage = `url('${imageSrc}')`;
+ images.push(
+
+ );
+ } else {
+ images.push(
+
e.preventDefault()}
+ style={imageStyle}
+ src={imageSrc}
+ key={imageSrc + keyEndings[srcType]}
+ alt={
+ typeof imageTitle === 'string' ? imageTitle : translate('Image')
+ }
+ draggable={false}
+ />
+ );
+ }
+ };
+
+ const zoomMultiplier = this.getZoomMultiplier();
+ // Next Image (displayed on the right)
+ addImage('nextSrc', 'ril-image-next ril__imageNext', {
+ x: boxSize.width,
+ });
+ // Main Image
+ addImage('mainSrc', 'ril-image-current', {
+ x: -1 * offsetX,
+ y: -1 * offsetY,
+ zoom: zoomMultiplier,
+ });
+ // Previous Image (displayed on the left)
+ addImage('prevSrc', 'ril-image-prev ril__imagePrev', {
+ x: -1 * boxSize.width,
+ });
+
+ const modalStyle = {
+ overlay: {
+ zIndex: 1000,
+ backgroundColor: 'transparent',
+ ...reactModalStyle.overlay, // Allow style overrides via props
+ },
+ content: {
+ backgroundColor: 'transparent',
+ overflow: 'hidden', // Needed, otherwise keyboard shortcuts scroll the page
+ border: 'none',
+ borderRadius: 0,
+ padding: 0,
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ ...reactModalStyle.content, // Allow style overrides via props
+ },
+ };
+
+ return (
+ {
+ // Focus on the div with key handlers
+ if (this.outerEl) {
+ this.outerEl.focus();
+ }
+
+ onAfterOpen();
+ }}
+ style={modalStyle}
+ contentLabel={translate('Lightbox')}
+ appElement={
+ typeof global.window !== 'undefined'
+ ? global.window.document.body
+ : undefined
+ }
+ {...reactModalProps}
+ >
+ {
+ this.outerEl = el;
+ }}
+ onWheel={this.handleOuterMousewheel}
+ onMouseMove={this.handleMouseMove}
+ onMouseDown={this.handleMouseDown}
+ onTouchStart={this.handleTouchStart}
+ onTouchMove={this.handleTouchMove}
+ tabIndex="-1" // Enables key handlers on div
+ onKeyDown={this.handleKeyInput}
+ onKeyUp={this.handleKeyInput}
+ >
+
+ {images}
+
+
+ {prevSrc && (
+
+ )}
+
+ {nextSrc && (
+
+ )}
+
+
+
+
+
+ {toolbarButtons &&
+ toolbarButtons.map((button, i) => (
+ -
+ {button}
+
+ ))}
+
+ {enableZoom && (
+ -
+
+
+ )}
+
+ {enableZoom && (
+ -
+
+
+ )}
+
+ -
+
+
+
+
+
+ {this.props.imageCaption && (
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions
+
event.stopPropagation()}
+ className="ril-caption ril__caption"
+ ref={el => {
+ this.caption = el;
+ }}
+ >
+
+ {this.props.imageCaption}
+
+
+ )}
+
+
+ );
+ }
+}
+
+ReactImageLightbox.propTypes = {
+ //-----------------------------
+ // Image sources
+ //-----------------------------
+
+ // Main display image url
+ mainSrc: PropTypes.string.isRequired, // eslint-disable-line react/no-unused-prop-types
+
+ // Previous display image url (displayed to the left)
+ // If left undefined, movePrev actions will not be performed, and the button not displayed
+ prevSrc: PropTypes.string,
+
+ // Next display image url (displayed to the right)
+ // If left undefined, moveNext actions will not be performed, and the button not displayed
+ nextSrc: PropTypes.string,
+
+ //-----------------------------
+ // Image thumbnail sources
+ //-----------------------------
+
+ // Thumbnail image url corresponding to props.mainSrc
+ mainSrcThumbnail: PropTypes.string, // eslint-disable-line react/no-unused-prop-types
+
+ // Thumbnail image url corresponding to props.prevSrc
+ prevSrcThumbnail: PropTypes.string, // eslint-disable-line react/no-unused-prop-types
+
+ // Thumbnail image url corresponding to props.nextSrc
+ nextSrcThumbnail: PropTypes.string, // eslint-disable-line react/no-unused-prop-types
+
+ //-----------------------------
+ // Event Handlers
+ //-----------------------------
+
+ // Close window event
+ // Should change the parent state such that the lightbox is not rendered
+ onCloseRequest: PropTypes.func.isRequired,
+
+ // Move to previous image event
+ // Should change the parent state such that props.prevSrc becomes props.mainSrc,
+ // props.mainSrc becomes props.nextSrc, etc.
+ onMovePrevRequest: PropTypes.func,
+
+ // Move to next image event
+ // Should change the parent state such that props.nextSrc becomes props.mainSrc,
+ // props.mainSrc becomes props.prevSrc, etc.
+ onMoveNextRequest: PropTypes.func,
+
+ // Called when an image fails to load
+ // (imageSrc: string, srcType: string, errorEvent: object): void
+ onImageLoadError: PropTypes.func,
+
+ // Called when image successfully loads
+ onImageLoad: PropTypes.func,
+
+ // Open window event
+ onAfterOpen: PropTypes.func,
+
+ //-----------------------------
+ // Download discouragement settings
+ //-----------------------------
+
+ // Enable download discouragement (prevents [right-click -> Save Image As...])
+ discourageDownloads: PropTypes.bool,
+
+ //-----------------------------
+ // Animation settings
+ //-----------------------------
+
+ // Disable all animation
+ animationDisabled: PropTypes.bool,
+
+ // Disable animation on actions performed with keyboard shortcuts
+ animationOnKeyInput: PropTypes.bool,
+
+ // Animation duration (ms)
+ animationDuration: PropTypes.number,
+
+ //-----------------------------
+ // Keyboard shortcut settings
+ //-----------------------------
+
+ // Required interval of time (ms) between key actions
+ // (prevents excessively fast navigation of images)
+ keyRepeatLimit: PropTypes.number,
+
+ // Amount of time (ms) restored after each keyup
+ // (makes rapid key presses slightly faster than holding down the key to navigate images)
+ keyRepeatKeyupBonus: PropTypes.number,
+
+ //-----------------------------
+ // Image info
+ //-----------------------------
+
+ // Image title
+ imageTitle: PropTypes.node,
+
+ // Image caption
+ imageCaption: PropTypes.node,
+
+ // Optional crossOrigin attribute
+ imageCrossOrigin: PropTypes.string,
+
+ //-----------------------------
+ // Lightbox style
+ //-----------------------------
+
+ // Set z-index style, etc., for the parent react-modal (format: https://github.com/reactjs/react-modal#styles )
+ reactModalStyle: PropTypes.shape({}),
+
+ // Padding (px) between the edge of the window and the lightbox
+ imagePadding: PropTypes.number,
+
+ wrapperClassName: PropTypes.string,
+
+ //-----------------------------
+ // Other
+ //-----------------------------
+
+ // Array of custom toolbar buttons
+ toolbarButtons: PropTypes.arrayOf(PropTypes.node),
+
+ // When true, clicks outside of the image close the lightbox
+ clickOutsideToClose: PropTypes.bool,
+
+ // Set to false to disable zoom functionality and hide zoom buttons
+ enableZoom: PropTypes.bool,
+
+ // Override props set on react-modal (https://github.com/reactjs/react-modal)
+ reactModalProps: PropTypes.shape({}),
+
+ // Aria-labels
+ nextLabel: PropTypes.string,
+ prevLabel: PropTypes.string,
+ zoomInLabel: PropTypes.string,
+ zoomOutLabel: PropTypes.string,
+ closeLabel: PropTypes.string,
+
+ imageLoadErrorMessage: PropTypes.node,
+};
+
+ReactImageLightbox.defaultProps = {
+ imageTitle: null,
+ imageCaption: null,
+ toolbarButtons: null,
+ reactModalProps: {},
+ animationDisabled: false,
+ animationDuration: 300,
+ animationOnKeyInput: false,
+ clickOutsideToClose: true,
+ closeLabel: 'Close lightbox',
+ discourageDownloads: false,
+ enableZoom: true,
+ imagePadding: 10,
+ imageCrossOrigin: null,
+ keyRepeatKeyupBonus: 40,
+ keyRepeatLimit: 180,
+ mainSrcThumbnail: null,
+ nextLabel: 'Next image',
+ nextSrc: null,
+ nextSrcThumbnail: null,
+ onAfterOpen: () => {},
+ onImageLoadError: () => {},
+ onImageLoad: () => {},
+ onMoveNextRequest: () => {},
+ onMovePrevRequest: () => {},
+ prevLabel: 'Previous image',
+ prevSrc: null,
+ prevSrcThumbnail: null,
+ reactModalStyle: {},
+ wrapperClassName: '',
+ zoomInLabel: 'Zoom in',
+ zoomOutLabel: 'Zoom out',
+ imageLoadErrorMessage: 'This image failed to load',
+};
+
+export default ReactImageLightbox;
diff --git a/front/odiparpack/app/components/ImageLightbox/constant.js b/front/odiparpack/app/components/ImageLightbox/constant.js
new file mode 100644
index 0000000..c310b9e
--- /dev/null
+++ b/front/odiparpack/app/components/ImageLightbox/constant.js
@@ -0,0 +1,39 @@
+// Min image zoom level
+export const MIN_ZOOM_LEVEL = 0;
+
+// Max image zoom level
+export const MAX_ZOOM_LEVEL = 300;
+
+// Size ratio between previous and next zoom levels
+export const ZOOM_RATIO = 1.007;
+
+// How much to increase/decrease the zoom level when the zoom buttons are clicked
+export const ZOOM_BUTTON_INCREMENT_SIZE = 100;
+
+// Used to judge the amount of horizontal scroll needed to initiate a image move
+export const WHEEL_MOVE_X_THRESHOLD = 200;
+
+// Used to judge the amount of vertical scroll needed to initiate a zoom action
+export const WHEEL_MOVE_Y_THRESHOLD = 1;
+
+export const KEYS = {
+ ESC: 27,
+ LEFT_ARROW: 37,
+ RIGHT_ARROW: 39,
+};
+
+// Actions
+export const ACTION_NONE = 0;
+export const ACTION_MOVE = 1;
+export const ACTION_SWIPE = 2;
+export const ACTION_PINCH = 3;
+export const ACTION_ROTATE = 4;
+
+// Events source
+export const SOURCE_ANY = 0;
+export const SOURCE_MOUSE = 1;
+export const SOURCE_TOUCH = 2;
+export const SOURCE_POINTER = 3;
+
+// Minimal swipe distance
+export const MIN_SWIPE_DISTANCE = 200;
diff --git a/front/odiparpack/app/components/ImageLightbox/util.js b/front/odiparpack/app/components/ImageLightbox/util.js
new file mode 100644
index 0000000..a76383a
--- /dev/null
+++ b/front/odiparpack/app/components/ImageLightbox/util.js
@@ -0,0 +1,46 @@
+/**
+ * Placeholder for future translate functionality
+ */
+export function translate(str, replaceStrings = null) {
+ if (!str) {
+ return '';
+ }
+
+ let translated = str;
+ if (replaceStrings) {
+ Object.keys(replaceStrings).forEach(placeholder => {
+ translated = translated.replace(placeholder, replaceStrings[placeholder]);
+ });
+ }
+
+ return translated;
+}
+
+export function getWindowWidth() {
+ return typeof global.window !== 'undefined' ? global.window.innerWidth : 0;
+}
+
+export function getWindowHeight() {
+ return typeof global.window !== 'undefined' ? global.window.innerHeight : 0;
+}
+
+// Get the highest window context that isn't cross-origin
+// (When in an iframe)
+export function getHighestSafeWindowContext(self = global.window.self) {
+ // If we reached the top level, return self
+ if (self === global.window.top) {
+ return self;
+ }
+
+ const getOrigin = href => href.match(/(.*\/\/.*?)(\/|$)/)[1];
+
+ // If parent is the same origin, we can move up one context
+ // Reference: https://stackoverflow.com/a/21965342/1601953
+ if (getOrigin(self.location.href) === getOrigin(self.document.referrer)) {
+ return getHighestSafeWindowContext(self.parent);
+ }
+
+ // If a different origin, we consider the current level
+ // as the top reachable one
+ return self;
+}
diff --git a/front/odiparpack/app/components/Loading/index.js b/front/odiparpack/app/components/Loading/index.js
new file mode 100644
index 0000000..41c92af
--- /dev/null
+++ b/front/odiparpack/app/components/Loading/index.js
@@ -0,0 +1,20 @@
+import React from 'react';
+import { PropTypes } from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import { CircularProgress } from '@material-ui/core';
+const styles = {
+ circularProgress: {
+ position: 'fixed',
+ top: 'calc(50% - 30px)',
+ left: 'calc(50% - 30px)',
+ }
+};
+
+function Loading(props) {
+ return ();
+}
+
+Loading.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+export default (withStyles(styles)(Loading));
diff --git a/front/odiparpack/app/components/Notification/Notification.js b/front/odiparpack/app/components/Notification/Notification.js
new file mode 100644
index 0000000..7e73896
--- /dev/null
+++ b/front/odiparpack/app/components/Notification/Notification.js
@@ -0,0 +1,59 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import CloseIcon from '@material-ui/icons/Close';
+
+import { Snackbar, IconButton } from '@material-ui/core';
+
+const styles = theme => ({
+ close: {
+ width: theme.spacing(4),
+ },
+});
+
+class Notification extends React.Component {
+ handleClose = (event, reason) => {
+ if (reason === 'clickaway') {
+ return;
+ }
+ this.props.close('crudTableDemo');
+ };
+
+ render() {
+ const { classes, message } = this.props;
+ return (
+ this.handleClose()}
+ ContentProps={{
+ 'aria-describedby': 'message-id',
+ }}
+ message={message}
+ action={[
+ this.handleClose()}
+ >
+
+ ,
+ ]}
+ />
+ );
+ }
+}
+
+Notification.propTypes = {
+ classes: PropTypes.object.isRequired,
+ close: PropTypes.func.isRequired,
+ message: PropTypes.string.isRequired,
+};
+
+export default withStyles(styles)(Notification);
diff --git a/front/odiparpack/app/components/Pagination/Pagination.js b/front/odiparpack/app/components/Pagination/Pagination.js
new file mode 100644
index 0000000..2ac2e29
--- /dev/null
+++ b/front/odiparpack/app/components/Pagination/Pagination.js
@@ -0,0 +1,189 @@
+import React from 'react';
+import { createUltimatePagination, ITEM_TYPES } from 'react-ultimate-pagination';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import NavigationFirstPage from '@material-ui/icons/FirstPage';
+import NavigationLastPage from '@material-ui/icons/LastPage';
+import NavigationChevronLeft from '@material-ui/icons/ChevronLeft';
+import NavigationChevronRight from '@material-ui/icons/ChevronRight';
+
+import { Button, Hidden, IconButton } from '@material-ui/core';
+
+const flatButtonStyle = {
+ minWidth: 36
+};
+
+const styles = {
+ paging: {
+ marginTop: 10,
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center'
+ }
+};
+
+const Page = ({
+ value,
+ isActive,
+ onClick,
+ isDisabled
+}) => (
+
+);
+
+Page.propTypes = {
+ value: PropTypes.number.isRequired,
+ isActive: PropTypes.bool.isRequired,
+ onClick: PropTypes.func.isRequired,
+ isDisabled: PropTypes.bool.isRequired,
+};
+
+const Ellipsis = ({ onClick, isDisabled }) => (
+
+);
+
+Ellipsis.propTypes = {
+ onClick: PropTypes.func.isRequired,
+ isDisabled: PropTypes.bool.isRequired,
+};
+
+const FirstPageLink = ({ onClick, isDisabled }) => (
+
+
+
+);
+
+
+FirstPageLink.propTypes = {
+ onClick: PropTypes.func.isRequired,
+ isDisabled: PropTypes.bool.isRequired,
+};
+
+const PreviousPageLink = ({ onClick, isDisabled }) => (
+
+
+
+);
+
+PreviousPageLink.propTypes = {
+ onClick: PropTypes.func.isRequired,
+ isDisabled: PropTypes.bool.isRequired,
+};
+
+const NextPageLink = ({ onClick, isDisabled }) => (
+
+
+
+);
+
+NextPageLink.propTypes = {
+ onClick: PropTypes.func.isRequired,
+ isDisabled: PropTypes.bool.isRequired,
+};
+
+const LastPageLink = ({ onClick, isDisabled }) => (
+
+
+
+);
+
+LastPageLink.propTypes = {
+ onClick: PropTypes.func.isRequired,
+ isDisabled: PropTypes.bool.isRequired,
+};
+
+
+const itemTypeToComponent = {
+ [ITEM_TYPES.PAGE]: Page,
+ [ITEM_TYPES.ELLIPSIS]: Ellipsis,
+ [ITEM_TYPES.FIRST_PAGE_LINK]: FirstPageLink,
+ [ITEM_TYPES.PREVIOUS_PAGE_LINK]: PreviousPageLink,
+ [ITEM_TYPES.NEXT_PAGE_LINK]: NextPageLink,
+ [ITEM_TYPES.LAST_PAGE_LINK]: LastPageLink
+};
+
+const UltmPagination = createUltimatePagination({ itemTypeToComponent });
+
+class Pagination extends React.Component {
+ constructor(props) {
+ super();
+ this.state = {
+ totalPages: props.totpages
+ };
+ }
+
+ render() {
+ const hide = true;
+ const { totalPages } = this.state;
+ const {
+ classes,
+ curpage,
+ onChange,
+ onGoFirst,
+ onPrev,
+ onNext,
+ onGoLast,
+ ...rest
+ } = this.props;
+ return (
+
+
+
+
+
+
+
= totalPages} onClick={onNext} />
+ = totalPages} onClick={onGoLast} />
+
+ );
+ }
+}
+
+Pagination.propTypes = {
+ curpage: PropTypes.number.isRequired,
+ totpages: PropTypes.number.isRequired,
+ onChange: PropTypes.func.isRequired,
+ onPrev: PropTypes.func.isRequired,
+ onNext: PropTypes.func.isRequired,
+ onGoFirst: PropTypes.func.isRequired,
+ onGoLast: PropTypes.func.isRequired,
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(Pagination);
diff --git a/front/odiparpack/app/components/Panel/FloatingPanel.js b/front/odiparpack/app/components/Panel/FloatingPanel.js
new file mode 100644
index 0000000..675166a
--- /dev/null
+++ b/front/odiparpack/app/components/Panel/FloatingPanel.js
@@ -0,0 +1,90 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import { isWidthDown } from '@material-ui/core/withWidth';
+import classNames from 'classnames';
+import CloseIcon from '@material-ui/icons/Close';
+import ExpandIcon from '@material-ui/icons/CallMade';
+import MinimizeIcon from '@material-ui/icons/CallReceived';
+import { withWidth, Tooltip, IconButton } from '@material-ui/core';
+import styles from './panel-jss';
+
+
+class FloatingPanel extends React.Component {
+ state = {
+ expanded: false
+ }
+
+ toggleExpand() {
+ this.setState({ expanded: !this.state.expanded });
+ }
+
+ render() {
+ const {
+ classes,
+ openForm,
+ closeForm,
+ children,
+ branch,
+ title,
+ extraSize,
+ width
+ } = this.props;
+ const { expanded } = this.state;
+ return (
+
+ );
+ }
+}
+
+FloatingPanel.propTypes = {
+ classes: PropTypes.object.isRequired,
+ openForm: PropTypes.bool.isRequired,
+ closeForm: PropTypes.func.isRequired,
+ children: PropTypes.node.isRequired,
+ branch: PropTypes.string.isRequired,
+ width: PropTypes.string.isRequired,
+ title: PropTypes.string,
+ extraSize: PropTypes.bool,
+};
+
+FloatingPanel.defaultProps = {
+ title: 'Add New Item',
+ extraSize: false,
+};
+
+const FloatingPanelResponsive = withWidth()(FloatingPanel);
+export default withStyles(styles)(FloatingPanelResponsive);
diff --git a/front/odiparpack/app/components/Panel/panel-jss.js b/front/odiparpack/app/components/Panel/panel-jss.js
new file mode 100644
index 0000000..d5d5e9c
--- /dev/null
+++ b/front/odiparpack/app/components/Panel/panel-jss.js
@@ -0,0 +1,95 @@
+import { darken } from '@material-ui/core/styles/colorManipulator';
+const expand = {
+ bottom: 'auto',
+ right: 'auto',
+ left: '50%',
+ top: '50%',
+ transform: 'translateX(-50%) translateY(-50%)'
+};
+
+const styles = theme => ({
+ formTheme: {
+ background: darken(theme.palette.primary.dark, 0.2),
+ boxShadow: theme.shadows[7]
+ },
+ hideForm: {
+ display: 'none'
+ },
+ showForm: {
+ display: 'block'
+ },
+ btnOpt: {},
+ expandButton: {
+ [theme.breakpoints.down('sm')]: {
+ display: 'none'
+ }
+ },
+ floatingForm: {
+ position: 'fixed',
+ width: 500,
+ bottom: 10,
+ right: 10,
+ zIndex: 1300,
+ borderRadius: 3,
+ overflow: 'hidden',
+ [theme.breakpoints.down('sm')]: {
+ width: '95% !important',
+ ...expand
+ },
+ '& header': {
+ color: theme.palette.common.white,
+ padding: '15px 20px',
+ '& $btnOpt': {
+ position: 'absolute',
+ right: 0,
+ top: 0,
+ '& > *': {
+ margin: '0 5px'
+ },
+ '& $expandButton': {
+ transform: 'rotate(270deg)'
+ },
+ '& svg': {
+ fill: theme.palette.common.white,
+ }
+ }
+ },
+ },
+ bodyForm: {
+ position: 'relative',
+ background: theme.palette.common.white,
+ padding: '15px 30px',
+ maxHeight: 900,
+ overflow: 'auto'
+ },
+ buttonArea: {
+ background: theme.palette.grey[100],
+ position: 'relative',
+ bottom: 0,
+ left: 0,
+ width: '100%',
+ textAlign: 'right',
+ padding: '10px 30px',
+ '& button': {
+ marginRight: 5
+ }
+ },
+ expanded: {
+ ...expand
+ },
+ formOverlay: {
+ position: 'fixed',
+ background: theme.palette.grey[900],
+ opacity: 0.7,
+ width: '100%',
+ height: '100%',
+ top: 0,
+ left: 0,
+ zIndex: 1300,
+ },
+ large: {
+ width: 650
+ }
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/PapperBlock/PapperBlock.js b/front/odiparpack/app/components/PapperBlock/PapperBlock.js
new file mode 100644
index 0000000..6663ae6
--- /dev/null
+++ b/front/odiparpack/app/components/PapperBlock/PapperBlock.js
@@ -0,0 +1,55 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import { withStyles } from '@material-ui/core/styles';
+import { Paper, Typography } from '@material-ui/core';
+import styles from './papperStyle-jss';
+
+
+function PaperSheet(props) {
+ const {
+ classes,
+ title,
+ desc,
+ children,
+ whiteBg,
+ noMargin,
+ colorMode,
+ overflowX
+ } = props;
+ return (
+
+
+
+ {title}
+
+
+ {desc}
+
+
+
+
+ );
+}
+
+PaperSheet.propTypes = {
+ classes: PropTypes.object.isRequired,
+ title: PropTypes.string.isRequired,
+ desc: PropTypes.string.isRequired,
+ children: PropTypes.node.isRequired,
+ whiteBg: PropTypes.bool,
+ colorMode: PropTypes.bool,
+ noMargin: PropTypes.bool,
+ overflowX: PropTypes.bool,
+};
+
+PaperSheet.defaultProps = {
+ whiteBg: false,
+ noMargin: false,
+ colorMode: false,
+ overflowX: false
+};
+
+export default withStyles(styles)(PaperSheet);
diff --git a/front/odiparpack/app/components/PapperBlock/papperStyle-jss.js b/front/odiparpack/app/components/PapperBlock/papperStyle-jss.js
new file mode 100644
index 0000000..521e349
--- /dev/null
+++ b/front/odiparpack/app/components/PapperBlock/papperStyle-jss.js
@@ -0,0 +1,58 @@
+const styles = theme => ({
+ root: theme.mixins.gutters({
+ paddingTop: theme.spacing(3),
+ paddingBottom: theme.spacing(3),
+ marginTop: theme.spacing(3),
+ '&$noMargin': {
+ margin: 0
+ },
+ }),
+ title: {
+ marginBottom: theme.spacing(4),
+ paddingBottom: theme.spacing(2),
+ position: 'relative',
+ textTransform: 'capitalize',
+ fontSize: 28,
+ '&:after': {
+ content: '""',
+ display: 'block',
+ position: 'absolute',
+ bottom: 0,
+ left: 0,
+ width: 40,
+ borderBottom: `4px solid ${theme.palette.primary.main}`
+ }
+ },
+ description: {
+ maxWidth: 960,
+ fontSize: 16,
+ },
+ content: {
+ marginTop: theme.spacing(2),
+ padding: theme.spacing(1),
+ backgroundColor: theme.palette.background.default,
+ },
+ whiteBg: {
+ backgroundColor: 'transparent',
+ margin: 0,
+ },
+ noMargin: {},
+ colorMode: {
+ backgroundColor: theme.palette.secondary.main,
+ '& $title': {
+ color: theme.palette.grey[100],
+ '&:after': {
+ borderBottom: `5px solid ${theme.palette.primary.light}`
+ }
+ },
+ '& $description': {
+ color: theme.palette.grey[100],
+ }
+ },
+ overflowX: {
+ width: '100%',
+ overflowX: 'auto',
+ }
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/Profile/About.js b/front/odiparpack/app/components/Profile/About.js
new file mode 100644
index 0000000..9872a47
--- /dev/null
+++ b/front/odiparpack/app/components/Profile/About.js
@@ -0,0 +1,243 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import { withStyles } from '@material-ui/core/styles';
+import LocalPhone from '@material-ui/icons/LocalPhone';
+import DateRange from '@material-ui/icons/DateRange';
+import LocationOn from '@material-ui/icons/LocationOn';
+import InfoIcon from '@material-ui/icons/Info';
+import Check from '@material-ui/icons/Check';
+import AcUnit from '@material-ui/icons/AcUnit';
+import Adb from '@material-ui/icons/Adb';
+import AllInclusive from '@material-ui/icons/AllInclusive';
+import AssistantPhoto from '@material-ui/icons/AssistantPhoto';
+import imgData from 'ba-api/imgData';
+import Type from 'ba-styles/Typography.scss';
+import {
+ Grid,
+ Paper,
+ Typography,
+ List,
+ ListItem,
+ ListItemText,
+ ListItemAvatar,
+ Avatar,
+ Button,
+ LinearProgress,
+ Divider,
+ Chip,
+ GridList,
+ GridListTile,
+ GridListTileBar,
+ IconButton,
+} from '@material-ui/core';
+import Timeline from '../SocialMedia/Timeline';
+import PapperBlock from '../PapperBlock/PapperBlock';
+import styles from './profile-jss';
+
+
+class About extends React.Component {
+ render() {
+ const { classes, data } = this.props;
+ return (
+
+
+
+
+
+
+
+ {/* Profile Progress */}
+
+
+
+ Profile Strength:
+ Intermediate
+
+
+
+
+
+ )}
+ label="60% Progress"
+ className={classes.chip}
+ color="primary"
+ />
+
+
+
+
+ {/* ----------------------------------------------------------------------*/}
+ {/* About Me */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* ----------------------------------------------------------------------*/}
+ {/* My Albums */}
+
+
+
+ {
+ imgData.map((tile, index) => {
+ if (index >= 4) {
+ return false;
+ }
+ return (
+
+
+
+by:
+ {tile.author}
+
+ )}
+ actionIcon={(
+
+
+
+ )}
+ />
+
+ );
+ })
+ }
+
+
+
+
+
+
+
+ {/* ----------------------------------------------------------------------*/}
+ {/* My Connection Me */}
+
+
+
+
+ H
+
+
+
+
+
+ J
+
+
+
+
+
+ V
+
+
+
+
+
+ H
+
+
+
+
+
+
+
+
+
+ {/* ----------------------------------------------------------------------*/}
+ {/* My Interests */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* ----------------------------------------------------------------------*/}
+
+
+ );
+ }
+}
+
+About.propTypes = {
+ classes: PropTypes.object.isRequired,
+ data: PropTypes.object.isRequired
+};
+
+export default withStyles(styles)(About);
diff --git a/front/odiparpack/app/components/Profile/Albums.js b/front/odiparpack/app/components/Profile/Albums.js
new file mode 100644
index 0000000..dbb6d19
--- /dev/null
+++ b/front/odiparpack/app/components/Profile/Albums.js
@@ -0,0 +1,152 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import imgData from 'ba-api/imgData';
+import { Grid, GridList, GridListTile, ButtonBase, Typography } from '@material-ui/core';
+import styles from './profile-jss';
+
+
+function Albums(props) {
+ const { classes } = props;
+
+ return (
+
+ );
+}
+
+Albums.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(Albums);
diff --git a/front/odiparpack/app/components/Profile/Connection.js b/front/odiparpack/app/components/Profile/Connection.js
new file mode 100644
index 0000000..beef128
--- /dev/null
+++ b/front/odiparpack/app/components/Profile/Connection.js
@@ -0,0 +1,45 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import datas from 'ba-api/connectionData';
+import { Grid } from '@material-ui/core';
+import ProfileCard from '../CardPaper/ProfileCard';
+import styles from './profile-jss';
+
+class Connection extends React.Component {
+ render() {
+ const { classes } = this.props;
+ return (
+
+ {
+ datas.map((data, index) => (
+
+
+
+ ))
+ }
+
+ );
+ }
+}
+
+Connection.propTypes = {
+ classes: PropTypes.object.isRequired
+};
+
+export default withStyles(styles)(Connection);
diff --git a/front/odiparpack/app/components/Profile/Favorites.js b/front/odiparpack/app/components/Profile/Favorites.js
new file mode 100644
index 0000000..ac6b506
--- /dev/null
+++ b/front/odiparpack/app/components/Profile/Favorites.js
@@ -0,0 +1,130 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import imgApi from 'ba-api/images';
+import avatarApi from 'ba-api/avatars';
+import { Typography, Grid, Divider } from '@material-ui/core';
+import GeneralCard from '../CardPaper/GeneralCard';
+import PostCard from '../CardPaper/PostCard';
+import Quote from '../Quote/Quote';
+
+
+const styles = theme => ({
+ divider: {
+ margin: `${theme.spacing(3)}px 0`,
+ },
+});
+
+class Favorites extends React.Component {
+ render() {
+ const { classes } = this.props;
+ const bull = •;
+ return (
+
+
+
+
+
+
+ Word of the Day
+
+
+ be
+ {bull}
+nev
+ {bull}
+o
+ {bull}
+lent
+
+
+ adjective
+
+
+ well meaning and kindly.
+
+ {'"a benevolent smile"'}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+Favorites.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(Favorites);
diff --git a/front/odiparpack/app/components/Profile/profile-jss.js b/front/odiparpack/app/components/Profile/profile-jss.js
new file mode 100644
index 0000000..32c3308
--- /dev/null
+++ b/front/odiparpack/app/components/Profile/profile-jss.js
@@ -0,0 +1,155 @@
+import { deepOrange, deepPurple, pink, green } from '@material-ui/core/colors';
+const styles = theme => ({
+ profileList: {
+ padding: 0,
+ '& li': {
+ paddingLeft: 0
+ }
+ },
+ avatar: {
+ margin: 10,
+ },
+ orangeAvatar: {
+ backgroundColor: deepOrange[500],
+ },
+ purpleAvatar: {
+ backgroundColor: deepPurple[500],
+ },
+ pinkAvatar: {
+ backgroundColor: pink[500],
+ },
+ greenAvatar: {
+ backgroundColor: green[500],
+ },
+ divider: {
+ margin: `${theme.spacing(3)}px 0`,
+ },
+ albumRoot: {
+ display: 'flex',
+ flexWrap: 'wrap',
+ justifyContent: 'space-around',
+ overflow: 'hidden',
+ backgroundColor: theme.palette.background.paper,
+ },
+ gridList: {
+ width: 500,
+ height: 'auto',
+ },
+ icon: {
+ color: 'rgba(255, 255, 255, 0.54)',
+ },
+ img: {
+ maxWidth: 'none'
+ },
+ root: theme.mixins.gutters({
+ paddingTop: 16,
+ paddingBottom: 16,
+ marginTop: theme.spacing(3),
+ }),
+ progressRoot: {
+ marginBottom: 30,
+ },
+ styledPaper: {
+ backgroundColor: theme.palette.secondary.main,
+ padding: 20,
+ '& $title, & $subtitle': {
+ color: theme.palette.common.white
+ }
+ },
+ progress: {
+ marginTop: 20,
+ background: theme.palette.secondary.dark,
+ '& div': {
+ background: theme.palette.primary.light,
+ }
+ },
+ chip: {
+ marginTop: 20,
+ background: theme.palette.primary.light,
+ '& div': {
+ background: green[500],
+ color: theme.palette.common.white
+ }
+ },
+ colList: {
+ '& li': {
+ padding: '10px 0'
+ },
+ '& $avatar': {
+ margin: 0
+ }
+ },
+ title: {},
+ subtitle: {},
+ rootAlbum: {
+ display: 'flex',
+ flexWrap: 'wrap',
+ justifyContent: 'space-around',
+ overflow: 'hidden',
+ },
+ image: {
+ position: 'relative',
+ height: 'auto',
+ boxShadow: theme.shadows[6],
+ borderRadius: 2,
+ overflow: 'hidden',
+ marginBottom: 30,
+ width: '100% !important', // Overrides inline-style
+ '&:hover, &$focusVisible': {
+ zIndex: 1,
+ '& $imageBackdrop': {
+ opacity: 0.6,
+ },
+ '& $imageMarked': {
+ opacity: 0,
+ },
+ '& $imageTitle': {
+ border: '4px solid currentColor',
+ },
+ },
+ },
+ imageButton: {
+ position: 'absolute',
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0,
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ color: theme.palette.common.white,
+ },
+ imageBackdrop: {
+ position: 'absolute',
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0,
+ backgroundColor: theme.palette.common.black,
+ opacity: 0.4,
+ transition: theme.transitions.create('opacity'),
+ },
+ imageTitle: {
+ position: 'relative',
+ padding: `${theme.spacing(2)}px ${theme.spacing(4)}px ${theme.spacing(1) + 6}px`,
+ },
+ imageMarked: {
+ height: 3,
+ width: 18,
+ backgroundColor: theme.palette.common.white,
+ position: 'absolute',
+ bottom: -2,
+ left: 'calc(50% - 9px)',
+ transition: theme.transitions.create('opacity'),
+ },
+ focusVisible: {},
+ gridListAlbum: {
+ height: 'auto',
+ background: theme.palette.common.black
+ },
+ subheader: {
+ width: '100%',
+ },
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/Quote/Quote.js b/front/odiparpack/app/components/Quote/Quote.js
new file mode 100644
index 0000000..21665d5
--- /dev/null
+++ b/front/odiparpack/app/components/Quote/Quote.js
@@ -0,0 +1,81 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import { blueGrey } from '@material-ui/core/colors';
+import { Typography } from '@material-ui/core';
+
+const styles = ({
+ quoteWrap: {
+ padding: '0 25',
+ margin: 10,
+ position: 'relative',
+ '&:before': {
+ color: blueGrey[100],
+ fontSize: '4em',
+ lineHeight: '.1em',
+ marginRight: '.25em',
+ verticalAlign: '-.4em'
+ }
+ },
+ quoteLeft: {
+ extend: 'quoteWrap',
+ textAlign: 'left',
+ borderLeft: '5px solid' + blueGrey[50],
+ paddingLeft: 25,
+ '&:before': {
+ content: 'open-quote',
+ }
+ },
+ quoteRight: {
+ extend: 'quoteWrap',
+ textAlign: 'right',
+ borderRight: '5px solid' + blueGrey[50],
+ paddingRight: 25,
+ '&:before': {
+ content: 'close-quote',
+ }
+ },
+ quoteBody: {
+ minHeight: 100,
+ marginBottom: 20
+ }
+});
+
+
+class Quote extends React.Component {
+ render() {
+ const {
+ align,
+ content,
+ footnote,
+ classes
+ } = this.props;
+ return (
+
+
+ {content}
+
+
+ {footnote}
+
+
+ );
+ }
+}
+
+Quote.propTypes = {
+ align: PropTypes.string.isRequired,
+ content: PropTypes.string.isRequired,
+ footnote: PropTypes.string.isRequired,
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(Quote);
diff --git a/front/odiparpack/app/components/Rating/Rating.js b/front/odiparpack/app/components/Rating/Rating.js
new file mode 100644
index 0000000..6f65d06
--- /dev/null
+++ b/front/odiparpack/app/components/Rating/Rating.js
@@ -0,0 +1,144 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import ToggleStar from '@material-ui/icons/Star';
+import ToggleStarBorder from '@material-ui/icons/StarBorder';
+import { orange, grey } from '@material-ui/core/colors';
+
+import { IconButton } from '@material-ui/core';
+
+const styles = {
+ disabled: {
+ pointerEvents: 'none'
+ }
+};
+
+class Rating extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ hoverValue: props.value
+ };
+ }
+
+ renderIcon(i) {
+ const filled = i <= this.props.value;
+ const hovered = i <= this.state.hoverValue;
+
+ if ((hovered && !filled) || (!hovered && filled)) {
+ return this.props.iconHoveredRenderer ? this.props.iconHoveredRenderer({
+ ...this.props,
+ index: i
+ }) : this.props.iconHovered;
+ } if (filled) {
+ return this.props.iconFilledRenderer ? this.props.iconFilledRenderer({
+ ...this.props,
+ index: i
+ }) : this.props.iconFilled;
+ }
+ return this.props.iconNormalRenderer ? this.props.iconNormalRenderer({
+ ...this.props,
+ index: i
+ }) : this.props.iconNormal;
+ }
+
+ render() {
+ const {
+ disabled,
+ iconFilled,
+ iconHovered,
+ iconNormal,
+ tooltip,
+ tooltipRenderer,
+ tooltipPosition,
+ tooltipStyles,
+ iconFilledRenderer,
+ iconHoveredRenderer,
+ iconNormalRenderer,
+ itemStyle,
+ itemClassName,
+ itemIconStyle,
+ max,
+ onChange,
+ readOnly,
+ style,
+ value,
+ ...other
+ } = this.props;
+
+ const rating = [];
+
+ for (let i = 1; i <= max; i += 1) {
+ rating.push(
+ this.setState({ hoverValue: i })}
+ onMouseLeave={() => this.setState({ hoverValue: value })}
+ onClick={() => {
+ if (!readOnly && onChange) {
+ onChange(i);
+ }
+ }}
+ >
+ {this.renderIcon(i)}
+
+ );
+ }
+
+ return (
+
+ {rating}
+
+ );
+ }
+}
+
+Rating.propTypes = {
+ disabled: PropTypes.bool,
+ iconFilled: PropTypes.node,
+ iconHovered: PropTypes.node,
+ iconNormal: PropTypes.node,
+ tooltip: PropTypes.node,
+ tooltipRenderer: PropTypes.func,
+ tooltipPosition: PropTypes.string,
+ tooltipStyles: PropTypes.object,
+ iconFilledRenderer: PropTypes.func,
+ iconHoveredRenderer: PropTypes.func,
+ iconNormalRenderer: PropTypes.func,
+ itemStyle: PropTypes.object,
+ itemClassName: PropTypes.object,
+ itemIconStyle: PropTypes.object,
+ max: PropTypes.number,
+ onChange: PropTypes.func,
+ readOnly: PropTypes.bool,
+ style: PropTypes.object,
+ value: PropTypes.number
+};
+
+Rating.defaultProps = {
+ disabled: false,
+ iconFilled: ,
+ iconHovered: ,
+ iconNormal: ,
+ tooltipPosition: 'bottom-center',
+ max: 5,
+ readOnly: false,
+ value: 0,
+ tooltip: null,
+ tooltipRenderer: () => {},
+ tooltipStyles: null,
+ iconFilledRenderer: undefined,
+ iconHoveredRenderer: undefined,
+ iconNormalRenderer: undefined,
+ itemStyle: undefined,
+ itemClassName: undefined,
+ itemIconStyle: undefined,
+ onChange: () => {},
+ style: null,
+};
+
+export default Rating;
diff --git a/front/odiparpack/app/components/Search/SearchProduct.js b/front/odiparpack/app/components/Search/SearchProduct.js
new file mode 100644
index 0000000..b465808
--- /dev/null
+++ b/front/odiparpack/app/components/Search/SearchProduct.js
@@ -0,0 +1,84 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import ShoppingCartIcon from '@material-ui/icons/ShoppingCart';
+import SearchIcon from '@material-ui/icons/Search';
+import { AppBar, Toolbar, IconButton, Badge } from '@material-ui/core';
+import Cart from '../Cart/Cart';
+import styles from './search-jss';
+
+
+class SearchProduct extends React.Component {
+ state = {
+ anchorEl: null,
+ };
+
+ handleClick = event => {
+ this.setState({ anchorEl: event.currentTarget });
+ };
+
+ handleClose = () => {
+ this.setState({ anchorEl: null });
+ };
+
+ render() {
+ const { anchorEl } = this.state;
+ const {
+ classes,
+ dataCart,
+ removeItem,
+ checkout,
+ totalItems,
+ totalPrice,
+ search
+ } = this.props;
+ return (
+
+
+
+
+
+
+
+
+
search(event)} />
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+SearchProduct.propTypes = {
+ classes: PropTypes.object.isRequired,
+ dataCart: PropTypes.object.isRequired,
+ removeItem: PropTypes.func.isRequired,
+ search: PropTypes.func.isRequired,
+ checkout: PropTypes.func.isRequired,
+ totalItems: PropTypes.number.isRequired,
+ totalPrice: PropTypes.number.isRequired,
+};
+
+export default withStyles(styles)(SearchProduct);
diff --git a/front/odiparpack/app/components/Search/search-jss.js b/front/odiparpack/app/components/Search/search-jss.js
new file mode 100644
index 0000000..5ba6ee0
--- /dev/null
+++ b/front/odiparpack/app/components/Search/search-jss.js
@@ -0,0 +1,48 @@
+const styles = theme => ({
+ root: {
+ flexGrow: 1,
+ marginTop: 20,
+ marginBottom: 40
+ },
+ flex: {
+ flex: 1,
+ },
+ menuButton: {
+ marginLeft: -12,
+ marginRight: 20,
+ },
+ wrapper: {
+ fontFamily: theme.typography.fontFamily,
+ position: 'relative',
+ marginRight: theme.spacing(2),
+ marginLeft: theme.spacing(1),
+ borderRadius: 2,
+ display: 'block',
+ },
+ search: {
+ width: 'auto',
+ height: '100%',
+ position: 'absolute',
+ pointerEvents: 'none',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ input: {
+ font: 'inherit',
+ padding: `${theme.spacing(1)}px ${theme.spacing(1)}px ${theme.spacing(1)}px ${theme.spacing(4)}px`,
+ border: 0,
+ display: 'block',
+ verticalAlign: 'middle',
+ whiteSpace: 'normal',
+ background: 'none',
+ margin: 0, // Reset for Safari
+ color: 'inherit',
+ width: '100%',
+ '&:focus': {
+ outline: 0,
+ },
+ },
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/Sidebar/MainMenu.js b/front/odiparpack/app/components/Sidebar/MainMenu.js
new file mode 100644
index 0000000..edeb420
--- /dev/null
+++ b/front/odiparpack/app/components/Sidebar/MainMenu.js
@@ -0,0 +1,130 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import { bindActionCreators } from 'redux';
+import { connect } from 'react-redux';
+import { NavLink } from 'react-router-dom';
+import ExpandLess from '@material-ui/icons/ExpandLess';
+import ExpandMore from '@material-ui/icons/ExpandMore';
+// Menu Object
+import MenuContent from 'ba-api/menu';
+import { List, ListItem, ListItemIcon, ListItemText, Collapse, Icon } from '@material-ui/core';
+import styles from './sidebar-jss';
+
+function sortByKey(array, key) {
+ return array.sort((a, b) => {
+ const x = a[key]; const y = b[key];
+ return ((x < y) ? -1 : ((x > y) ? 1 : 0));
+ });
+}
+
+const LinkBtn = React.forwardRef(function LinkBtn(props, ref) { // eslint-disable-line
+ return ; // eslint-disable-line
+});
+
+function MainMenu(props) {
+ const {
+ classes,
+ toggleDrawerOpen,
+ loadTransition,
+ openSubMenu,
+ open,
+ } = props;
+
+ const handleClick = () => {
+ toggleDrawerOpen();
+ loadTransition(false);
+ };
+
+ const getMenus = menuArray => menuArray.map((item, index) => {
+ if (item.child) {
+ return (
+
+ -1 ? classes.opened : '')}
+ onClick={() => openSubMenu(item.key, item.keyParent)}
+ >
+ {item.icon
+ && (
+
+ {item.icon}
+
+ )
+ }
+
+ { open.indexOf(item.key) > -1 ? : }
+
+ -1}
+ timeout="auto"
+ unmountOnExit
+ >
+
+ { getMenus(sortByKey(item.child, 'key')) }
+
+
+
+ );
+ }
+ return (
+
+ {item.icon
+ && (
+
+ {item.icon}
+
+ )
+ }
+
+
+ );
+ });
+ return (
+
+ {getMenus(MenuContent)}
+
+ );
+}
+
+MainMenu.propTypes = {
+ classes: PropTypes.object.isRequired,
+ open: PropTypes.object.isRequired,
+ openSubMenu: PropTypes.func.isRequired,
+ toggleDrawerOpen: PropTypes.func.isRequired,
+ loadTransition: PropTypes.func.isRequired,
+};
+
+const openAction = (key, keyParent) => ({ type: 'OPEN_SUBMENU', key, keyParent });
+const reducer = 'ui';
+
+const mapStateToProps = state => ({
+ force: state, // force active class for sidebar menu
+ open: state.getIn([reducer, 'subMenuOpen'])
+});
+
+const mapDispatchToProps = dispatch => ({
+ openSubMenu: bindActionCreators(openAction, dispatch)
+});
+
+const MainMenuMapped = connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(MainMenu);
+
+export default withStyles(styles)(MainMenuMapped);
diff --git a/front/odiparpack/app/components/Sidebar/OtherMenu.js b/front/odiparpack/app/components/Sidebar/OtherMenu.js
new file mode 100644
index 0000000..ea2f597
--- /dev/null
+++ b/front/odiparpack/app/components/Sidebar/OtherMenu.js
@@ -0,0 +1,44 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import { NavLink } from 'react-router-dom';
+import OtherMenuContent from 'ba-api/otherMenu';
+import { ListItem, ListItemText } from '@material-ui/core';
+import styles from './sidebar-jss';
+
+const LinkBtn = React.forwardRef(function LinkBtn(props, ref) { // eslint-disable-line
+ return ; // eslint-disable-line
+});
+
+function OtherMenu(props) {
+ const { toggleDrawerOpen, classes } = props;
+ const getOtherMenu = menuArray => menuArray.map((item, index) => {
+ const keyIndex = index.toString();
+ return (
+
+
+
+
+
+ );
+ });
+
+ return (
+
+ {getOtherMenu(OtherMenuContent)}
+
+ );
+}
+
+OtherMenu.propTypes = {
+ classes: PropTypes.object.isRequired,
+ toggleDrawerOpen: PropTypes.func.isRequired,
+};
+
+export default withStyles(styles)(OtherMenu);
diff --git a/front/odiparpack/app/components/Sidebar/Sidebar.js b/front/odiparpack/app/components/Sidebar/Sidebar.js
new file mode 100644
index 0000000..01de4ec
--- /dev/null
+++ b/front/odiparpack/app/components/Sidebar/Sidebar.js
@@ -0,0 +1,122 @@
+import React, { Fragment } from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import brand from 'ba-api/brand';
+import dummy from 'ba-api/dummyContents';
+import logo from 'ba-images/logo.svg';
+import { Hidden, Drawer, SwipeableDrawer, List, Divider, Avatar } from '@material-ui/core';
+import MainMenu from './MainMenu';
+import OtherMenu from './OtherMenu';
+import styles from './sidebar-jss';
+
+const MenuContent = props => {
+ const {
+ classes,
+ turnDarker,
+ drawerPaper,
+ toggleDrawerOpen,
+ loadTransition
+ } = props;
+ return (
+
+
+
+

+
{brand.name}
+
+
+
+
+
{dummy.user.name}
+ {dummy.user.title}
+
+
+
+
+
+ );
+};
+
+MenuContent.propTypes = {
+ classes: PropTypes.object.isRequired,
+ drawerPaper: PropTypes.bool.isRequired,
+ turnDarker: PropTypes.bool,
+ toggleDrawerOpen: PropTypes.func,
+ loadTransition: PropTypes.func,
+};
+
+MenuContent.defaultProps = {
+ turnDarker: false
+};
+
+MenuContent.defaultProps = {
+ toggleDrawerOpen: () => {},
+ loadTransition: () => {},
+};
+
+const MenuContentStyle = withStyles(styles)(MenuContent);
+
+function Sidebar(props) {
+ const {
+ classes,
+ open,
+ toggleDrawerOpen,
+ loadTransition,
+ turnDarker
+ } = props;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+Sidebar.propTypes = {
+ classes: PropTypes.object.isRequired,
+ toggleDrawerOpen: PropTypes.func.isRequired,
+ loadTransition: PropTypes.func.isRequired,
+ turnDarker: PropTypes.bool.isRequired,
+ open: PropTypes.bool.isRequired,
+};
+
+export default withStyles(styles)(Sidebar);
diff --git a/front/odiparpack/app/components/Sidebar/sidebar-jss.js b/front/odiparpack/app/components/Sidebar/sidebar-jss.js
new file mode 100644
index 0000000..e9bf4f6
--- /dev/null
+++ b/front/odiparpack/app/components/Sidebar/sidebar-jss.js
@@ -0,0 +1,205 @@
+const drawerWidth = 240;
+const styles = theme => ({
+ user: {
+ justifyContent: 'center'
+ },
+ drawerPaper: {
+ position: 'relative',
+ height: '100%',
+ overflow: 'hidden',
+ backgroundColor: theme.palette.background.default,
+ border: 'none',
+ width: drawerWidth,
+ transition: theme.transitions.create('width', {
+ easing: theme.transitions.easing.sharp,
+ duration: theme.transitions.duration.enteringScreen,
+ }),
+ },
+ swipeDrawerPaper: {
+ width: drawerWidth,
+ },
+ opened: {
+ background: theme.palette.grey[200],
+ '& $primary, & $icon': {
+ color: theme.palette.secondary.dark,
+ },
+ },
+ drawerInner: {
+ height: '100%',
+ position: 'fixed',
+ width: drawerWidth,
+ },
+ drawerPaperClose: {
+ width: 66,
+ position: 'fixed',
+ overflowX: 'hidden',
+ transition: theme.transitions.create('width', {
+ easing: theme.transitions.easing.sharp,
+ duration: theme.transitions.duration.leavingScreen,
+ }),
+ '& $user': {
+ justifyContent: 'flex-start'
+ },
+ '& $bigAvatar': {
+ width: 40,
+ height: 40,
+ },
+ '& li ul': {
+ display: 'none'
+ },
+ '&:hover': {
+ width: drawerWidth,
+ boxShadow: theme.shadows[6],
+ '& li ul': {
+ display: 'block'
+ }
+ },
+ '& $menuContainer': {
+ paddingLeft: theme.spacing(1.5),
+ paddingRight: theme.spacing(1.5),
+ width: drawerWidth,
+ },
+ '& $drawerInner': {
+ width: 'auto'
+ },
+ '& $brandBar': {
+ opacity: 0
+ }
+ },
+ drawerHeader: {
+ background: theme.palette.primary.main,
+ color: theme.palette.primary.contrastText,
+ padding: '0',
+ ...theme.mixins.toolbar,
+ '& h3': {
+ color: theme.palette.primary.contrastText,
+ }
+ },
+ avatar: {
+ margin: 10,
+ },
+ bigAvatar: {
+ width: 80,
+ height: 80,
+ },
+ brandBar: {
+ transition: theme.transitions.create(['width', 'margin', 'background'], {
+ easing: theme.transitions.easing.sharp,
+ duration: theme.transitions.duration.enteringScreen,
+ }),
+ '&:after': {
+ transition: theme.transitions.create(['box-shadow'], {
+ easing: theme.transitions.easing.sharp,
+ duration: theme.transitions.duration.enteringScreen,
+ }),
+ }
+ },
+ darker: {
+ background: theme.palette.primary.dark,
+ },
+ title: {},
+ nested: {
+ paddingLeft: 0,
+ paddingTop: theme.spacing(0.5),
+ paddingBottom: theme.spacing(0.5),
+ '& > div > span': {
+ fontSize: '0.8125rem'
+ }
+ },
+ child: {
+ '& a': {
+ paddingLeft: theme.spacing(3),
+ }
+ },
+ dense: {
+ '& > $title:first-child': {
+ margin: '0'
+ },
+ '& $head': {
+ paddingLeft: theme.spacing(7)
+ }
+ },
+ active: {
+ backgroundColor: theme.palette.primary.light,
+ '& $primary, & $icon': {
+ color: theme.palette.secondary.dark,
+ },
+ '&:hover': {
+ backgroundColor: theme.palette.primary.light,
+ }
+ },
+ nolist: {
+ listStyle: 'none',
+ },
+ primary: {},
+ iconWrapper: {
+ width: theme.spacing(5),
+ minWidth: 0,
+ marginRight: 0,
+ marginLeft: theme.spacing(2)
+ },
+ icon: {
+ marginRight: 0,
+ color: theme.palette.secondary.dark,
+ },
+ head: {
+ paddingLeft: 0
+ },
+ brand: {
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ padding: '10px 10px 5px',
+ height: 64,
+ position: 'relative',
+ '& img': {
+ width: 20
+ },
+ '& h3': {
+ fontSize: 16,
+ margin: 0,
+ paddingLeft: 10,
+ fontWeight: 500
+ }
+ },
+ profile: {
+ height: 120,
+ display: 'flex',
+ fontSize: 14,
+ padding: 10,
+ alignItems: 'center',
+ '& h4': {
+ fontSize: 18,
+ marginBottom: 0,
+ textOverflow: 'ellipsis',
+ overflow: 'hidden',
+ whiteSpace: 'nowrap',
+ width: 110
+ },
+ '& span': {
+ fontSize: 12,
+ textOverflow: 'ellipsis',
+ whiteSpace: 'nowrap',
+ width: 110,
+ display: 'block',
+ overflow: 'hidden'
+ }
+ },
+ menuContainer: {
+ padding: theme.spacing(1),
+ background: theme.palette.background.default,
+ [theme.breakpoints.up('lg')]: {
+ padding: theme.spacing(1.5),
+ },
+ paddingRight: theme.spacing(1),
+ overflow: 'auto',
+ height: 'calc(100% - 185px)',
+ position: 'relative',
+ display: 'block'
+ },
+ divider: {
+ marginTop: theme.spacing(1)
+ }
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/SocialMedia/Comment.js b/front/odiparpack/app/components/SocialMedia/Comment.js
new file mode 100644
index 0000000..9ead6e7
--- /dev/null
+++ b/front/odiparpack/app/components/SocialMedia/Comment.js
@@ -0,0 +1,135 @@
+import React, { Fragment } from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import Type from 'ba-styles/Typography.scss';
+import { withStyles } from '@material-ui/core/styles';
+import Send from '@material-ui/icons/Send';
+import CommentIcon from '@material-ui/icons/Comment';
+import CloseIcon from '@material-ui/icons/Close';
+import dummy from 'ba-api/dummyContents';
+import {
+ Typography,
+ List,
+ ListItem,
+ Avatar,
+ Input,
+ Dialog,
+ DialogActions,
+ DialogContent,
+ DialogTitle,
+ IconButton,
+ Fab,
+ Slide,
+ Divider,
+ withMobileDialog,
+} from '@material-ui/core';
+import styles from './jss/socialMedia-jss';
+
+const Transition = React.forwardRef(function Transition(props, ref) { // eslint-disable-line
+ return ;
+});
+
+class Comment extends React.Component {
+ state = {
+ comment: ''
+ };
+
+ handleChange = event => {
+ this.setState({ comment: event.target.value });
+ };
+
+ handleSubmit = comment => {
+ this.props.submitComment(comment);
+ this.setState({ comment: '' });
+ }
+
+ render() {
+ const {
+ open,
+ handleClose,
+ classes,
+ dataComment,
+ fullScreen
+ } = this.props;
+ const { comment } = this.state;
+ const getItem = dataArray => dataArray.map(data => (
+
+
+
+
+
+
+ {data.get('from')}
+ {data.get('date')}
+
+
+
{data.get('message')}
+
+
+
+
+ ));
+
+ return (
+
+
+
+ );
+ }
+}
+
+Comment.propTypes = {
+ open: PropTypes.bool.isRequired,
+ handleClose: PropTypes.func.isRequired,
+ submitComment: PropTypes.func.isRequired,
+ classes: PropTypes.object.isRequired,
+ dataComment: PropTypes.object,
+ fullScreen: PropTypes.bool.isRequired,
+};
+
+Comment.defaultProps = {
+ dataComment: undefined
+};
+
+const CommentResponsive = withMobileDialog()(Comment);
+export default withStyles(styles)(CommentResponsive);
diff --git a/front/odiparpack/app/components/SocialMedia/Cover.js b/front/odiparpack/app/components/SocialMedia/Cover.js
new file mode 100644
index 0000000..4823f13
--- /dev/null
+++ b/front/odiparpack/app/components/SocialMedia/Cover.js
@@ -0,0 +1,103 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import VerifiedUser from '@material-ui/icons/VerifiedUser';
+import Info from '@material-ui/icons/Info';
+import MoreVertIcon from '@material-ui/icons/MoreVert';
+import { withStyles } from '@material-ui/core/styles';
+import { Avatar, Typography, Menu, MenuItem, Button, IconButton } from '@material-ui/core';
+import styles from './jss/cover-jss';
+
+
+const optionsOpt = [
+ 'Edit Profile',
+ 'Change Cover',
+ 'Option 1',
+ 'Option 2',
+ 'Option 3',
+];
+
+const ITEM_HEIGHT = 48;
+
+class Cover extends React.Component {
+ state = {
+ anchorElOpt: null,
+ };
+
+ handleClickOpt = event => {
+ this.setState({ anchorElOpt: event.currentTarget });
+ };
+
+ handleCloseOpt = () => {
+ this.setState({ anchorElOpt: null });
+ };
+
+ render() {
+ const {
+ classes,
+ avatar,
+ name,
+ desc,
+ coverImg,
+ } = this.props;
+ const { anchorElOpt } = this.state;
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {name}
+
+
+
+ {desc}
+
+
+
+
+ );
+ }
+}
+
+Cover.propTypes = {
+ classes: PropTypes.object.isRequired,
+ avatar: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ desc: PropTypes.string.isRequired,
+ coverImg: PropTypes.string.isRequired,
+};
+
+export default withStyles(styles)(Cover);
diff --git a/front/odiparpack/app/components/SocialMedia/SideSection.js b/front/odiparpack/app/components/SocialMedia/SideSection.js
new file mode 100644
index 0000000..1fe8d9c
--- /dev/null
+++ b/front/odiparpack/app/components/SocialMedia/SideSection.js
@@ -0,0 +1,209 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import { withStyles } from '@material-ui/core/styles';
+import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft';
+import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight';
+import SwipeableViews from 'react-swipeable-views';
+import imgApi from 'ba-api/images';
+import avatarApi from 'ba-api/avatars';
+import {
+ Grid,
+ Typography,
+ MobileStepper,
+ Paper,
+ Avatar,
+ Button,
+ Divider,
+ List,
+ ListItem,
+ ListItemText,
+} from '@material-ui/core';
+import PapperBlock from '../PapperBlock/PapperBlock';
+import NewsCard from '../CardPaper/NewsCard';
+import ProfileCard from '../CardPaper/ProfileCard';
+import styles from './jss/socialMedia-jss';
+
+
+const slideData = [
+ {
+ label: 'How to be happy :)',
+ imgPath: imgApi[49],
+ },
+ {
+ label: '1. Work with something that you like, like…',
+ imgPath: imgApi[17],
+ },
+ {
+ label: '2. Keep your friends close to you and hangout with them',
+ imgPath: imgApi[34],
+ },
+ {
+ label: '3. Travel everytime that you have a chance',
+ imgPath: imgApi[10],
+ },
+ {
+ label: '4. And contribute to Material-UI :D',
+ imgPath: imgApi[40]
+ },
+];
+
+class SideSection extends React.Component {
+ state = {
+ activeStepSwipe: 0,
+ };
+
+ handleNextSwipe = () => {
+ this.setState(prevState => ({
+ activeStepSwipe: prevState.activeStepSwipe + 1,
+ }));
+ };
+
+ handleBackSwipe = () => {
+ this.setState(prevState => ({
+ activeStepSwipe: prevState.activeStepSwipe - 1,
+ }));
+ };
+
+ handleStepChangeSwipe = activeStepSwipe => {
+ this.setState({ activeStepSwipe });
+ };
+
+ render() {
+ const { classes, theme } = this.props;
+ const { activeStepSwipe } = this.state;
+
+ const maxStepsSwipe = slideData.length;
+ return (
+
+ {/* Profile */}
+
+
+ {/* ----------------------------------------------------------------------*/}
+ {/* News Or Ads Block */}
+
+
+ {slideData.map((slide, index) => (
+
+
+
+ {slide.label}
+
+
+
+ ))}
+
+
+ Next
+ {theme.direction === 'rtl' ? : }
+
+ )}
+ backButton={(
+
+ )}
+ />
+
+ {/* ----------------------------------------------------------------------*/}
+ {/* People */}
+
+
+
+ H
+
+
+
+
+ J
+
+
+
+
+ V
+
+
+
+
+ H
+
+
+
+
+
+
+
+
+
+ {/* ----------------------------------------------------------------------*/}
+ {/* Trending */}
+
+
+
+ #Lorem ipsum dolor
+
+
+
+ #Aliquam venenatis
+
+
+
+ #Nam sollicitudin
+
+
+
+ #Cras convallis
+
+
+
+ #Aenean sit amet
+
+
+
+ #Quisque
+
+
+
+ #Lorem ipusm dolor
+
+
+
+
+
+ );
+ }
+}
+
+SideSection.propTypes = {
+ classes: PropTypes.object.isRequired,
+ theme: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles, { withTheme: true })(SideSection);
diff --git a/front/odiparpack/app/components/SocialMedia/Timeline.js b/front/odiparpack/app/components/SocialMedia/Timeline.js
new file mode 100644
index 0000000..e46826b
--- /dev/null
+++ b/front/odiparpack/app/components/SocialMedia/Timeline.js
@@ -0,0 +1,177 @@
+import React, { Fragment } from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import FavoriteIcon from '@material-ui/icons/Favorite';
+import ShareIcon from '@material-ui/icons/Share';
+import CommentIcon from '@material-ui/icons/Comment';
+import MoreVertIcon from '@material-ui/icons/MoreVert';
+import {
+ Typography,
+ Card,
+ Menu,
+ MenuItem,
+ CardHeader,
+ CardMedia,
+ CardContent,
+ CardActions,
+ IconButton,
+ Icon,
+ Avatar,
+ Tooltip,
+} from '@material-ui/core';
+import Comment from './Comment';
+import styles from './jss/timeline-jss';
+
+
+const optionsOpt = [
+ 'Option 1',
+ 'Option 2',
+ 'Option 3',
+];
+
+const ITEM_HEIGHT = 48;
+
+class Timeline extends React.Component {
+ state = {
+ anchorElOpt: null,
+ openComment: false,
+ };
+
+ handleClickOpt = event => {
+ this.setState({ anchorElOpt: event.currentTarget });
+ };
+
+ handleCloseOpt = () => {
+ this.setState({ anchorElOpt: null });
+ };
+
+ handleOpenComment = (data) => {
+ this.props.fetchComment(data);
+ this.setState({ openComment: true });
+ };
+
+ handleCloseComment = () => {
+ this.setState({ openComment: false });
+ };
+
+ render() {
+ const {
+ classes,
+ dataTimeline,
+ onlike,
+ commentIndex,
+ submitComment,
+ } = this.props;
+ const { anchorElOpt, openComment } = this.state;
+ const getItem = dataArray => dataArray.map(data => (
+
+
+
+
+ {data.get('icon')}
+
+
+
+
+
+ }
+ action={(
+
+
+
+ )}
+ title={data.get('name')}
+ subheader={data.get('date')}
+ />
+ { data.get('image') !== ''
+ && (
+
+ )
+ }
+
+
+ {data.get('content')}
+
+
+
+ onlike(data)}>
+
+
+
+
+
+
+
+ {data.get('comments') !== undefined ? data.get('comments').size : 0}
+
+ this.handleOpenComment(data)}>
+
+
+
+
+
+
+ ));
+ return (
+
+
+
+
+ {getItem(dataTimeline)}
+
+
+ );
+ }
+}
+
+Timeline.propTypes = {
+ classes: PropTypes.object.isRequired,
+ onlike: PropTypes.func,
+ dataTimeline: PropTypes.object.isRequired,
+ fetchComment: PropTypes.func,
+ submitComment: PropTypes.func,
+ commentIndex: PropTypes.number,
+};
+
+Timeline.defaultProps = {
+ onlike: () => (false),
+ fetchComment: () => {},
+ submitComment: () => {},
+ commentIndex: 0,
+};
+
+export default withStyles(styles)(Timeline);
diff --git a/front/odiparpack/app/components/SocialMedia/WritePost.js b/front/odiparpack/app/components/SocialMedia/WritePost.js
new file mode 100644
index 0000000..3452aea
--- /dev/null
+++ b/front/odiparpack/app/components/SocialMedia/WritePost.js
@@ -0,0 +1,169 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import Dropzone from 'react-dropzone';
+import { withStyles } from '@material-ui/core/styles';
+import PhotoCamera from '@material-ui/icons/PhotoCamera';
+import Send from '@material-ui/icons/Send';
+import ActionDelete from '@material-ui/icons/Delete';
+import dummy from 'ba-api/dummyContents';
+import { IconButton, Fab, MenuItem, FormControl, Avatar, Paper, Select, Tooltip } from '@material-ui/core';
+import styles from './jss/writePost-jss';
+
+
+function isImage(file) {
+ const fileName = file.name || file.path;
+ const suffix = fileName.substr(fileName.indexOf('.') + 1).toLowerCase();
+ if (suffix === 'jpg' || suffix === 'jpeg' || suffix === 'bmp' || suffix === 'png') {
+ return true;
+ }
+ return false;
+}
+
+class WritePost extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ privacy: 'public',
+ files: [],
+ message: ''
+ };
+ this.onDrop = this.onDrop.bind(this);
+ }
+
+ onDrop(filesVal) {
+ const { files } = this.state;
+ let oldFiles = files;
+ const filesLimit = 2;
+ oldFiles = oldFiles.concat(filesVal);
+ if (oldFiles.length > filesLimit) {
+ console.log('Cannot upload more than ' + filesLimit + ' items.');
+ } else {
+ this.setState({ files: filesVal });
+ }
+ }
+
+ handleRemove(file, fileIndex) {
+ const thisFiles = this.state.files;
+ // This is to prevent memory leaks.
+ window.URL.revokeObjectURL(file.preview);
+
+ thisFiles.splice(fileIndex, 1);
+ this.setState({ files: thisFiles });
+ }
+
+ handleChange = event => {
+ this.setState({ privacy: event.target.value });
+ };
+
+ handleWrite = event => {
+ this.setState({ message: event.target.value });
+ };
+
+ handlePost = (message, files, privacy) => {
+ // Submit Post to reducer
+ this.props.submitPost(message, files, privacy);
+ // Reset all fields
+ this.setState({
+ privacy: 'public',
+ files: [],
+ message: ''
+ });
+ }
+
+ render() {
+ const { classes } = this.props;
+ let dropzoneRef;
+ const { privacy, files, message } = this.state;
+ const acceptedFiles = ['image/jpeg', 'image/png', 'image/bmp'];
+ const fileSizeLimit = 3000000;
+ const deleteBtn = (file, index) => (
+
+
this.handleRemove(file, index)}>
+
+
+
+ );
+ const previews = filesArray => filesArray.map((file, index) => {
+ const path = URL.createObjectURL(file) || '/pic' + file.path;
+ if (isImage(file)) {
+ return (
+
+

+ {deleteBtn(file, index)}
+
+ );
+ }
+ return false;
+ });
+ return (
+
+
+
+
+ { dropzoneRef = node; }}
+ >
+ {({ getRootProps, getInputProps }) => (
+
+
+
+ )}
+
+
+ {previews(files)}
+
+
+
+ {
+ dropzoneRef.open();
+ }}
+ >
+
+
+
+
+
+
+
+
+
+ this.handlePost(message, files, privacy)} size="small" color="secondary" aria-label="send" className={classes.sendBtn}>
+
+
+
+
+
+
+ );
+ }
+}
+
+WritePost.propTypes = {
+ classes: PropTypes.object.isRequired,
+ submitPost: PropTypes.func.isRequired
+};
+
+export default withStyles(styles)(WritePost);
diff --git a/front/odiparpack/app/components/SocialMedia/jss/cover-jss.js b/front/odiparpack/app/components/SocialMedia/jss/cover-jss.js
new file mode 100644
index 0000000..695512d
--- /dev/null
+++ b/front/odiparpack/app/components/SocialMedia/jss/cover-jss.js
@@ -0,0 +1,55 @@
+import { fade } from '@material-ui/core/styles/colorManipulator';
+const styles = theme => ({
+ root: {
+ flexGrow: 1,
+ },
+ cover: {
+ '& $name, & $subheading': {
+ color: theme.palette.common.white
+ },
+ position: 'relative',
+ width: '100%',
+ overflow: 'hidden',
+ height: 360,
+ backgroundColor: theme.palette.primary.main,
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'flex-end',
+ borderRadius: 2,
+ backgroundSize: 'cover',
+ textAlign: 'center',
+ boxShadow: theme.shadows[7]
+ },
+ content: {
+ background: fade(theme.palette.secondary.main, 0.3),
+ height: '100%',
+ width: '100%',
+ padding: `70px ${theme.spacing(3)}px 30px`
+ },
+ name: {},
+ subheading: {},
+ avatar: {
+ margin: '0 auto',
+ width: 120,
+ height: 120,
+ border: '3px solid rgba(255, 255, 255, .5)'
+ },
+ opt: {
+ position: 'absolute',
+ top: 10,
+ right: 10,
+ '& button': {
+ color: theme.palette.common.white
+ }
+ },
+ verified: {
+ margin: theme.spacing(1),
+ top: 10,
+ position: 'relative'
+ },
+ button: {
+ marginTop: theme.spacing(1)
+ }
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/SocialMedia/jss/socialMedia-jss.js b/front/odiparpack/app/components/SocialMedia/jss/socialMedia-jss.js
new file mode 100644
index 0000000..03f5726
--- /dev/null
+++ b/front/odiparpack/app/components/SocialMedia/jss/socialMedia-jss.js
@@ -0,0 +1,89 @@
+import { deepOrange, deepPurple, pink, green } from '@material-ui/core/colors';
+
+const styles = theme => ({
+ mobileStepper: {
+ margin: `0 auto ${theme.spacing(4)}px`,
+ textAlign: 'center'
+ },
+ avatar: {
+ marginRight: 15,
+ },
+ orangeAvatar: {
+ backgroundColor: deepOrange[500],
+ },
+ purpleAvatar: {
+ backgroundColor: deepPurple[500],
+ },
+ pinkAvatar: {
+ backgroundColor: pink[500],
+ },
+ greenAvatar: {
+ backgroundColor: green[500],
+ },
+ divider: {
+ margin: `${theme.spacing(2)}px 0`,
+ background: 'none'
+ },
+ link: {
+ color: theme.palette.primary.main
+ },
+ noPadding: {
+ padding: '5px',
+ marginLeft: -10
+ },
+ sliderWrap: {
+ height: 310,
+ overflow: 'hidden'
+ },
+ title: {
+ whiteSpace: 'nowrap',
+ textOverflow: 'ellipsis',
+ overflow: 'hidden',
+ fontSize: 18
+ },
+ profileList: {},
+ trendingList: {
+ '& li': {
+ display: 'block'
+ }
+ },
+ input: {},
+ commentContent: {
+ padding: 10
+ },
+ commentText: {
+ marginTop: 5
+ },
+ buttonClose: {
+ position: 'absolute',
+ top: 20,
+ right: 20
+ },
+ avatarMini: {
+ width: 30,
+ height: 30,
+ },
+ commentAction: {
+ background: theme.palette.grey[100],
+ margin: 0,
+ },
+ commentForm: {
+ display: 'flex',
+ alignItems: 'center',
+ [theme.breakpoints.up('md')]: {
+ minWidth: 600,
+ },
+ width: '100%',
+ padding: '15px 20px',
+ margin: 0,
+ '& $input': {
+ flex: 1,
+ margin: '0 10px'
+ }
+ },
+ commentHead: {
+ display: 'flex'
+ }
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/SocialMedia/jss/timeline-jss.js b/front/odiparpack/app/components/SocialMedia/jss/timeline-jss.js
new file mode 100644
index 0000000..dcdb018
--- /dev/null
+++ b/front/odiparpack/app/components/SocialMedia/jss/timeline-jss.js
@@ -0,0 +1,95 @@
+import { pink } from '@material-ui/core/colors';
+const styles = theme => ({
+ card: {
+ display: 'flex',
+ justifyContent: 'space-between'
+ },
+ content: {
+ flex: '1 0 auto',
+ },
+ cover: {
+ width: 150,
+ height: 150,
+ },
+ avatar: {
+ width: 40,
+ height: 40
+ },
+ cardSocmed: {
+ [theme.breakpoints.up('md')]: {
+ marginLeft: 90,
+ minWidth: 400,
+ },
+ marginBottom: theme.spacing(3),
+ position: 'relative',
+ },
+ media: {
+ height: 0,
+ paddingTop: '56.25%', // 16:9
+ },
+ actions: {
+ display: 'flex',
+ },
+ expandOpen: {
+ transform: 'rotate(180deg)',
+ },
+ iconBullet: {},
+ icon: {},
+ timeline: {
+ position: 'relative',
+ '&:before': {
+ left: 39,
+ content: '""',
+ top: 40,
+ height: '101%',
+ border: `1px solid ${theme.palette.grey[300]}`,
+ position: 'absolute',
+ [theme.breakpoints.down('sm')]: {
+ display: 'none'
+ },
+ },
+ '& li': {
+ position: 'relative',
+ display: 'block'
+ },
+ '& time': {
+ top: 70,
+ left: 20,
+ position: 'absolute',
+ textAlign: 'center',
+ background: theme.palette.common.white,
+ boxShadow: theme.shadows[3],
+ padding: '4px 40px 4px 15px',
+ borderLeft: `3px solid ${theme.palette.secondary.main}`
+ },
+ '& $iconBullet': {
+ position: 'absolute',
+ borderRadius: '50%',
+ top: 20,
+ width: 40,
+ height: 40,
+ background: theme.palette.secondary.main,
+ boxShadow: theme.shadows[5],
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ left: 20,
+ '& $icon': {
+ color: theme.palette.common.white,
+ },
+ [theme.breakpoints.down('sm')]: {
+ display: 'none'
+ },
+ },
+ },
+ rightIcon: {
+ marginLeft: 'auto',
+ display: 'flex',
+ alignItems: 'center'
+ },
+ liked: {
+ color: pink[500]
+ }
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/SocialMedia/jss/writePost-jss.js b/front/odiparpack/app/components/SocialMedia/jss/writePost-jss.js
new file mode 100644
index 0000000..cd18422
--- /dev/null
+++ b/front/odiparpack/app/components/SocialMedia/jss/writePost-jss.js
@@ -0,0 +1,73 @@
+const styles = theme => ({
+ statusWrap: {
+ marginBottom: theme.spacing(3),
+ '& > div': {
+ overflow: 'hidden'
+ },
+ '& textarea': {
+ border: 'none',
+ padding: '20px 20px 20px 50px',
+ outline: 'none',
+ width: '100%',
+ resize: 'none',
+ overflow: 'hidden',
+ height: 50,
+ transition: theme.transitions.create(['height'], {
+ easing: theme.transitions.easing.sharp,
+ duration: theme.transitions.duration.leavingScreen,
+ }),
+ '&:focus': {
+ height: 100,
+ overflow: 'auto',
+ }
+ }
+ },
+ avatarMini: {
+ width: 30,
+ height: 30,
+ position: 'absolute',
+ top: 40,
+ left: 10
+ },
+ control: {
+ padding: '10px 20px 0',
+ display: 'flex'
+ },
+ privacy: {
+ flex: 1,
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'flex-end',
+ textAlign: 'right',
+ },
+ button: {
+ margin: theme.spacing(0.5)
+ },
+ sendBtn: {
+ position: 'relative',
+ top: 5
+ },
+ formControl: {
+ margin: '0 20px',
+ width: 150,
+ paddingLeft: 10,
+ textAlign: 'left',
+ '&:before, &:after': {
+ borderBottom: 'none'
+ }
+ },
+ hiddenDropzone: {
+ display: 'none'
+ },
+ preview: {
+ position: 'relative',
+ '& figure': {
+ textAlign: 'center'
+ }
+ },
+ removeBtn: {
+ opacity: 1
+ }
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/SourceReader/SourceReader.js b/front/odiparpack/app/components/SourceReader/SourceReader.js
new file mode 100644
index 0000000..e1303a0
--- /dev/null
+++ b/front/odiparpack/app/components/SourceReader/SourceReader.js
@@ -0,0 +1,114 @@
+import React, { Component } from 'react';
+import { PropTypes } from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import Axios from 'axios';
+import SyntaxHighlighter, { registerLanguage } from 'react-syntax-highlighter/prism-light';
+import jsx from 'react-syntax-highlighter/languages/prism/jsx';
+import themeSource from 'react-syntax-highlighter/styles/prism/xonokai';
+import classNames from 'classnames';
+import Code from '@material-ui/icons/Code';
+import Close from '@material-ui/icons/Close';
+import { Button, LinearProgress, Icon } from '@material-ui/core';
+import codePreview from '../../config/codePreview';
+
+
+const url = '/api/docs?src=';
+
+const styles = theme => ({
+ button: {
+ margin: '8px 5px',
+ },
+ iconSmall: {
+ fontSize: 20,
+ },
+ leftIcon: {
+ marginRight: theme.spacing(1),
+ },
+ source: {
+ overflow: 'hidden',
+ height: 0,
+ position: 'relative',
+ transition: 'all .5s',
+ margin: '0 -10px'
+ },
+ preloader: {
+ position: 'absolute',
+ top: 36,
+ left: 0,
+ width: '100%'
+ },
+ open: {
+ height: 'auto',
+ },
+ src: {
+ textAlign: 'center',
+ margin: 10,
+ fontFamily: 'monospace',
+ '& span': {
+ fontSize: 14,
+ marginRight: 5,
+ top: 3,
+ position: 'relative'
+ }
+ }
+});
+
+class SourceReader extends Component {
+ state = { raws: [], open: false, loading: false };
+
+ sourceOpen = () => {
+ const name = this.props.componentName;
+ this.setState({ loading: true }, () => {
+ Axios.get(url + name).then(result => this.setState({
+ raws: result.data.records,
+ loading: false
+ }));
+ this.setState({ open: !this.state.open });
+ });
+ };
+
+ render() {
+ const { raws, open, loading } = this.state;
+ const { classes } = this.props;
+ registerLanguage('jsx', jsx);
+ if (codePreview.enable) {
+ return (
+
+
+
+
+ description
+ src/app/
+ {this.props.componentName}
+
+ {loading
+ &&
+ }
+ {raws.map((raw, index) => ([
+
+
+ {raw.source.toString()}
+
+
+ ])
+ )}
+
+
+ );
+ }
+ return false;
+ }
+}
+
+SourceReader.propTypes = {
+ componentName: PropTypes.string.isRequired,
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(SourceReader);
diff --git a/front/odiparpack/app/components/Tables/AdvTable.js b/front/odiparpack/app/components/Tables/AdvTable.js
new file mode 100644
index 0000000..acb2803
--- /dev/null
+++ b/front/odiparpack/app/components/Tables/AdvTable.js
@@ -0,0 +1,206 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import tableStyles from 'ba-styles/Table.scss';
+import { Table, TableBody, TableCell, TableRow, TablePagination, Paper, Checkbox } from '@material-ui/core';
+import EnhancedTableHead from './tableParts/TableHeader';
+import EnhancedTableToolbar from './tableParts/TableToolbar';
+
+
+const styles = theme => ({
+ root: {
+ width: '100%',
+ marginTop: theme.spacing(3),
+ },
+ table: {
+ minWidth: 1020,
+ },
+ tableWrapper: {
+ overflowX: 'auto',
+ },
+});
+
+class AdvTable extends React.Component {
+ constructor(props, context) {
+ super(props, context);
+
+ this.state = {
+ order: this.props.order,
+ orderBy: this.props.orderBy,
+ selected: this.props.selected,
+ data: this.props.data.sort((a, b) => (a.calories < b.calories ? -1 : 1)),
+ page: this.props.page,
+ rowsPerPage: this.props.rowsPerPage,
+ defaultPerPage: this.props.defaultPerPage,
+ filterText: this.props.filterText,
+ };
+ }
+
+ handleRequestSort = (event, property) => {
+ const orderBy = property;
+ let order = 'desc';
+
+ if (this.state.orderBy === property && this.state.order === 'desc') {
+ order = 'asc';
+ }
+
+ const data = order === 'desc'
+ ? this.state.data.sort((a, b) => (b[orderBy] < a[orderBy] ? -1 : 1))
+ : this.state.data.sort((a, b) => (a[orderBy] < b[orderBy] ? -1 : 1));
+
+ this.setState({ data, order, orderBy });
+ };
+
+ handleSelectAllClick = (event, checked) => {
+ if (checked) {
+ this.setState({ selected: this.state.data.map(n => n.id) });
+ return;
+ }
+ this.setState({ selected: [] });
+ };
+
+ handleClick = (event, id) => {
+ const { selected } = this.state;
+ const selectedIndex = selected.indexOf(id);
+ let newSelected = [];
+
+ if (selectedIndex === -1) {
+ newSelected = newSelected.concat(selected, id);
+ } else if (selectedIndex === 0) {
+ newSelected = newSelected.concat(selected.slice(1));
+ } else if (selectedIndex === selected.length - 1) {
+ newSelected = newSelected.concat(selected.slice(0, -1));
+ } else if (selectedIndex > 0) {
+ newSelected = newSelected.concat(
+ selected.slice(0, selectedIndex),
+ selected.slice(selectedIndex + 1),
+ );
+ }
+
+ this.setState({ selected: newSelected });
+ };
+
+ handleChangePage = (event, page) => {
+ this.setState({ page });
+ };
+
+ handleChangeRowsPerPage = event => {
+ this.setState({ rowsPerPage: event.target.value });
+ };
+
+ isSelected = id => this.state.selected.indexOf(id) !== -1;
+
+ handleUserInput(value) {
+ // Show all item first
+ if (value !== '') {
+ this.setState({ rowsPerPage: this.state.data.length });
+ } else {
+ this.setState({ rowsPerPage: this.state.defaultPerPage });
+ }
+
+ // Show result base on keyword
+ this.setState({ filterText: value.toLowerCase() });
+ }
+
+ render() {
+ const { classes } = this.props;
+ const {
+ data,
+ order,
+ orderBy,
+ selected,
+ rowsPerPage,
+ page,
+ filterText
+ } = this.state;
+ const { columnData } = this.props;
+ const checkcell = true;
+ const emptyRows = rowsPerPage - Math.min(rowsPerPage, data.length - (page * rowsPerPage));
+ const renderCell = (dataArray, keyArray) => keyArray.map((itemCell, index) => (
+ {dataArray[itemCell.id]}
+ ));
+ return (
+
+ this.handleUserInput(event)}
+ />
+
+
+
+
+ {data.slice(page * rowsPerPage, (page * rowsPerPage) + rowsPerPage).map(n => {
+ const isSelected = this.isSelected(n.id);
+ if (n.name.toLowerCase().indexOf(filterText) === -1) {
+ return false;
+ }
+ return (
+ this.handleClick(event, n.id)}
+ role="checkbox"
+ aria-checked={isSelected}
+ tabIndex={-1}
+ key={n.id}
+ selected={isSelected}
+ >
+
+
+
+ {renderCell(n, columnData)}
+
+ );
+ })}
+ {emptyRows > 0 && (
+
+
+
+ )}
+
+
+
+
+
+ );
+ }
+}
+
+AdvTable.propTypes = {
+ classes: PropTypes.object.isRequired,
+ data: PropTypes.array.isRequired,
+ order: PropTypes.string.isRequired,
+ orderBy: PropTypes.string.isRequired,
+ selected: PropTypes.array.isRequired,
+ rowsPerPage: PropTypes.number.isRequired,
+ page: PropTypes.number.isRequired,
+ defaultPerPage: PropTypes.number.isRequired,
+ filterText: PropTypes.string.isRequired,
+ columnData: PropTypes.array.isRequired,
+};
+
+export default withStyles(styles)(AdvTable);
diff --git a/front/odiparpack/app/components/Tables/CrudTable.js b/front/odiparpack/app/components/Tables/CrudTable.js
new file mode 100644
index 0000000..d3dd164
--- /dev/null
+++ b/front/odiparpack/app/components/Tables/CrudTable.js
@@ -0,0 +1,52 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import MainTable from './tableParts/MainTable';
+
+class CrudTable extends React.Component {
+ componentDidMount() {
+ this.props.fetchData(this.props.dataInit, this.props.branch);
+ }
+
+ render() {
+ const {
+ title,
+ dataTable,
+ addEmptyRow,
+ removeRow,
+ updateRow,
+ editRow,
+ finishEditRow,
+ anchor,
+ branch
+ } = this.props;
+ return (
+
+ );
+ }
+}
+
+CrudTable.propTypes = {
+ title: PropTypes.string.isRequired,
+ anchor: PropTypes.array.isRequired,
+ dataInit: PropTypes.array.isRequired,
+ dataTable: PropTypes.object.isRequired,
+ fetchData: PropTypes.func.isRequired,
+ addEmptyRow: PropTypes.func.isRequired,
+ removeRow: PropTypes.func.isRequired,
+ updateRow: PropTypes.func.isRequired,
+ editRow: PropTypes.func.isRequired,
+ finishEditRow: PropTypes.func.isRequired,
+ branch: PropTypes.string.isRequired,
+};
+
+export default CrudTable;
diff --git a/front/odiparpack/app/components/Tables/CrudTableForm.js b/front/odiparpack/app/components/Tables/CrudTableForm.js
new file mode 100644
index 0000000..d2d2ea8
--- /dev/null
+++ b/front/odiparpack/app/components/Tables/CrudTableForm.js
@@ -0,0 +1,70 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Form from './tableParts/Form';
+import MainTableForm from './tableParts/MainTableForm';
+import FloatingPanel from './../Panel/FloatingPanel';
+
+class CrudTableForm extends React.Component {
+ componentDidMount() {
+ this.props.fetchData(this.props.dataInit, this.props.branch);
+ }
+
+ sendValues = (values) => {
+ setTimeout(() => {
+ this.props.submit(values, this.props.branch);
+ }, 500);
+ }
+
+ render() {
+ const {
+ title,
+ dataTable,
+ openForm,
+ closeForm,
+ removeRow,
+ addNew,
+ editRow,
+ anchor,
+ children,
+ branch,
+ initValues
+ } = this.props;
+ return (
+
+
+
+
+
+
+ );
+ }
+}
+
+CrudTableForm.propTypes = {
+ title: PropTypes.string.isRequired,
+ anchor: PropTypes.array.isRequired,
+ dataInit: PropTypes.array.isRequired,
+ dataTable: PropTypes.object.isRequired,
+ fetchData: PropTypes.func.isRequired,
+ submit: PropTypes.func.isRequired,
+ addNew: PropTypes.func.isRequired,
+ openForm: PropTypes.bool.isRequired,
+ closeForm: PropTypes.func.isRequired,
+ removeRow: PropTypes.func.isRequired,
+ editRow: PropTypes.func.isRequired,
+ children: PropTypes.node.isRequired,
+ initValues: PropTypes.object.isRequired,
+ branch: PropTypes.string.isRequired,
+};
+
+export default CrudTableForm;
diff --git a/front/odiparpack/app/components/Tables/EmptyData.js b/front/odiparpack/app/components/Tables/EmptyData.js
new file mode 100644
index 0000000..a59c3d6
--- /dev/null
+++ b/front/odiparpack/app/components/Tables/EmptyData.js
@@ -0,0 +1,14 @@
+import React from 'react';
+import tableStyles from 'ba-styles/Table.scss';
+import TableIcon from '@material-ui/icons/Apps';
+
+function EmptyData() {
+ return (
+
+ );
+}
+
+export default EmptyData;
diff --git a/front/odiparpack/app/components/Tables/TreeTable.js b/front/odiparpack/app/components/Tables/TreeTable.js
new file mode 100644
index 0000000..12eeb19
--- /dev/null
+++ b/front/odiparpack/app/components/Tables/TreeTable.js
@@ -0,0 +1,190 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import ExpandLess from '@material-ui/icons/KeyboardArrowRight';
+import ExpandMore from '@material-ui/icons/ExpandMore';
+import Add from '@material-ui/icons/AddCircle';
+import Remove from '@material-ui/icons/RemoveCircleOutline';
+
+import { Table, TableBody, TableCell, TableHead, TableRow } from '@material-ui/core';
+
+const styles = theme => ({
+ root: {
+ width: '100%',
+ marginTop: theme.spacing(3),
+ overflowX: 'auto',
+ },
+ table: {
+ minWidth: 700,
+ },
+ hideRow: {
+ display: 'none'
+ },
+ anchor: {
+ cursor: 'pointer'
+ },
+ icon: {
+ top: 5,
+ position: 'relative',
+ left: -5
+ }
+});
+
+let RenderRow = props => {
+ const {
+ classes,
+ toggleTree,
+ treeOpen,
+ item,
+ parent,
+ arrowMore,
+ icon,
+ branch
+ } = props;
+
+ const keyID = item.id;
+ const dataBody = Object.keys(item);
+ const dataBodyVal = Object.values(item);
+
+ const renderIconMore = (iconName) => {
+ if (iconName === 'arrow') {
+ return ;
+ }
+ return ;
+ };
+
+ const renderIconLess = (iconName) => {
+ if (iconName === 'arrow') {
+ return ;
+ }
+ return ;
+ };
+
+ const renderCell = (dataArray, parentCell) => dataArray.map((itemCell, index) => {
+ if (index < 1) {
+ if (parentCell) {
+ return (
+
+ {arrowMore.indexOf(keyID) > -1 ? renderIconMore(icon) : renderIconLess(icon)}
+ {keyID}
+
+ );
+ }
+ return (
+ {keyID}
+ );
+ }
+
+ if (itemCell !== 'child') {
+ return (
+ {dataBodyVal[index]}
+ );
+ }
+
+ return false;
+ });
+
+ const row = parent ? (
+ -1 ? classes.hideRow : classes.anchor}
+ onClick={() => toggleTree(keyID, item.child, branch)}
+ >
+ {renderCell(dataBody, true)}
+
+ ) : (
+ -1 ? classes.hideRow : ''}
+ >
+ {renderCell(dataBody, false)}
+
+ );
+
+ return [row];
+};
+
+RenderRow.propTypes = {
+ classes: PropTypes.object.isRequired,
+ item: PropTypes.object.isRequired,
+ parent: PropTypes.bool.isRequired,
+ toggleTree: PropTypes.func.isRequired,
+ treeOpen: PropTypes.object.isRequired,
+ arrowMore: PropTypes.object.isRequired,
+ branch: PropTypes.string.isRequired,
+ icon: PropTypes.string.isRequired
+};
+
+RenderRow = withStyles(styles)(RenderRow);
+
+class TreeTable extends React.Component {
+ render() {
+ const {
+ classes,
+ dataTable,
+ icon,
+ treeOpen,
+ arrowMore,
+ toggleTree,
+ branch
+ } = this.props;
+ const parentRow = true;
+ const getData = dataArray => dataArray.map((item, index) => {
+ if (item.child) {
+ return [
+ ,
+ getData(item.child)
+ ];
+ }
+ return (
+
+ );
+ });
+
+ const getHead = dataArray => dataArray.map((item, index) => {item.label}
+ );
+
+ return (
+
+
+
+ { getHead(dataTable.head) }
+
+
+
+ { getData(dataTable.body) }
+
+
+ );
+ }
+}
+
+TreeTable.propTypes = {
+ classes: PropTypes.object.isRequired,
+ dataTable: PropTypes.object.isRequired,
+ treeOpen: PropTypes.object.isRequired,
+ toggleTree: PropTypes.func.isRequired,
+ arrowMore: PropTypes.object.isRequired,
+ branch: PropTypes.string.isRequired,
+ icon: PropTypes.string.isRequired
+};
+
+export default withStyles(styles)(TreeTable);
diff --git a/front/odiparpack/app/components/Tables/tableParts/DatePickerCell.js b/front/odiparpack/app/components/Tables/tableParts/DatePickerCell.js
new file mode 100644
index 0000000..161d0eb
--- /dev/null
+++ b/front/odiparpack/app/components/Tables/tableParts/DatePickerCell.js
@@ -0,0 +1,59 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { KeyboardDatePicker, MuiPickersUtilsProvider } from '@material-ui/pickers';
+import MomentUtils from '@date-io/moment';
+import css from 'ba-styles/Table.scss';
+import { TableCell } from '@material-ui/core';
+
+class DatePickerCell extends React.Component {
+ state = {
+ event: {
+ target: {
+ name: this.props.cellData.type, // eslint-disable-line
+ value: this.props.cellData.value, // eslint-disable-line
+ }
+ }
+ }
+
+ handleDateChange = date => {
+ const { event } = this.state;
+ const { branch, updateRow } = this.props;
+ event.target.value = date;
+ updateRow(event, branch);
+ }
+
+ render() {
+ const {
+ edited,
+ cellData
+ } = this.props;
+ const { event } = this.state;
+ return (
+
+
+
+
+
+ );
+ }
+}
+
+DatePickerCell.propTypes = {
+ cellData: PropTypes.object.isRequired,
+ updateRow: PropTypes.func.isRequired,
+ edited: PropTypes.bool.isRequired,
+ branch: PropTypes.string.isRequired,
+};
+
+export default DatePickerCell;
diff --git a/front/odiparpack/app/components/Tables/tableParts/EditableCell.js b/front/odiparpack/app/components/Tables/tableParts/EditableCell.js
new file mode 100644
index 0000000..2c7ba8f
--- /dev/null
+++ b/front/odiparpack/app/components/Tables/tableParts/EditableCell.js
@@ -0,0 +1,86 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import css from 'ba-styles/Table.scss';
+
+import { TableCell, Input, TextField } from '@material-ui/core';
+
+class EditableCell extends React.Component {
+ handleUpdate(event) {
+ event.persist();
+ this.props.updateRow(event, this.props.branch);
+ }
+
+ render() {
+ const {
+ cellData,
+ edited,
+ inputType
+ } = this.props;
+ switch (inputType) {
+ case 'text':
+ return (
+
+ this.handleUpdate(event)}
+ disabled={!edited}
+ margin="none"
+ inputProps={{
+ 'aria-label': 'Description',
+ }}
+ />
+
+ );
+ case 'number':
+ return (
+
+ this.handleUpdate(event)}
+ type="number"
+ InputLabelProps={{
+ shrink: true,
+ }}
+ margin="none"
+ disabled={!edited}
+ />
+
+ );
+ default:
+ return (
+
+ this.handleUpdate(event)}
+ disabled={!edited}
+ margin="none"
+ inputProps={{
+ 'aria-label': 'Description',
+ }}
+ />
+
+ );
+ }
+ }
+}
+
+EditableCell.propTypes = {
+ inputType: PropTypes.string.isRequired,
+ cellData: PropTypes.object.isRequired,
+ updateRow: PropTypes.func.isRequired,
+ edited: PropTypes.bool.isRequired,
+ branch: PropTypes.string.isRequired,
+};
+
+export default EditableCell;
diff --git a/front/odiparpack/app/components/Tables/tableParts/Form.js b/front/odiparpack/app/components/Tables/tableParts/Form.js
new file mode 100644
index 0000000..da66966
--- /dev/null
+++ b/front/odiparpack/app/components/Tables/tableParts/Form.js
@@ -0,0 +1,71 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import { reduxForm } from 'redux-form/immutable';
+import css from 'ba-styles/Form.scss';
+import { Button } from '@material-ui/core';
+
+class Form extends Component {
+ componentDidMount() {
+ // this.ref // the Field
+ // .getRenderedComponent() // on Field, returns ReduxFormMaterialUITextField
+ // .getRenderedComponent() // on ReduxFormMaterialUITextField, returns TextField
+ // .focus() // on TextField
+ // console.log(this.props.initValues);
+ }
+
+ render() {
+ const {
+ handleSubmit,
+ children,
+ reset,
+ pristine,
+ submitting,
+ } = this.props;
+
+ return (
+
+ );
+ }
+}
+
+Form.propTypes = {
+ children: PropTypes.node.isRequired,
+ handleSubmit: PropTypes.func.isRequired,
+ reset: PropTypes.func.isRequired,
+ pristine: PropTypes.bool.isRequired,
+ submitting: PropTypes.bool.isRequired,
+};
+
+const FormMapped = reduxForm({
+ form: 'immutableExample',
+ enableReinitialize: true,
+})(Form);
+
+
+const FormMappedInit = connect(
+ state => ({
+ initialValues: state.getIn(['crudTableForm', 'formValues'])
+ })
+)(FormMapped);
+
+
+export default FormMappedInit;
diff --git a/front/odiparpack/app/components/Tables/tableParts/MainTable.js b/front/odiparpack/app/components/Tables/tableParts/MainTable.js
new file mode 100644
index 0000000..973bccf
--- /dev/null
+++ b/front/odiparpack/app/components/Tables/tableParts/MainTable.js
@@ -0,0 +1,104 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import AddIcon from '@material-ui/icons/Add';
+import css from 'ba-styles/Table.scss';
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableRow,
+ Toolbar,
+ Typography,
+ Tooltip,
+ Button,
+} from '@material-ui/core';
+import Row from './Row';
+import styles from './tableStyle-jss';
+
+
+class MainTable extends React.Component {
+ render() {
+ const {
+ classes,
+ items,
+ addEmptyRow,
+ removeRow,
+ updateRow,
+ editRow,
+ finishEditRow,
+ anchor,
+ branch,
+ title
+ } = this.props;
+
+ const getItems = dataArray => dataArray.map(item => (
+ updateRow(event, item, branch)}
+ item={item}
+ removeRow={() => removeRow(item, branch)}
+ key={item.get('id')}
+ editRow={() => editRow(item, branch)}
+ finishEditRow={() => finishEditRow(item, branch)}
+ branch={branch}
+ />
+ ));
+
+ const getHead = dataArray => dataArray.map((item, index) => {
+ if (!item.hidden) {
+ return (
+ {item.label}
+ );
+ }
+ return false;
+ });
+ return (
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+ { getHead(anchor) }
+
+
+
+ {getItems(items)}
+
+
+
+
+ );
+ }
+}
+
+MainTable.propTypes = {
+ title: PropTypes.string.isRequired,
+ classes: PropTypes.object.isRequired,
+ items: PropTypes.object.isRequired,
+ anchor: PropTypes.array.isRequired,
+ addEmptyRow: PropTypes.func.isRequired,
+ removeRow: PropTypes.func.isRequired,
+ updateRow: PropTypes.func.isRequired,
+ editRow: PropTypes.func.isRequired,
+ finishEditRow: PropTypes.func.isRequired,
+ branch: PropTypes.string.isRequired
+};
+
+export default withStyles(styles)(MainTable);
diff --git a/front/odiparpack/app/components/Tables/tableParts/MainTableForm.js b/front/odiparpack/app/components/Tables/tableParts/MainTableForm.js
new file mode 100644
index 0000000..ccf0e4a
--- /dev/null
+++ b/front/odiparpack/app/components/Tables/tableParts/MainTableForm.js
@@ -0,0 +1,97 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import AddIcon from '@material-ui/icons/Add';
+import css from 'ba-styles/Table.scss';
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableRow,
+ Toolbar,
+ Typography,
+ Tooltip,
+ Button,
+} from '@material-ui/core';
+import RowReadOnly from './RowReadOnly';
+import styles from './tableStyle-jss';
+
+
+class MainTableForm extends React.Component {
+ render() {
+ const {
+ title,
+ classes,
+ items,
+ removeRow,
+ editRow,
+ addNew,
+ anchor,
+ branch
+ } = this.props;
+ const getItems = dataArray => dataArray.map(item => (
+ removeRow(item, branch)}
+ key={item.get('id')}
+ editRow={() => editRow(item, branch)}
+ anchor={anchor}
+ branch={branch}
+ />
+ ));
+
+ const getHead = dataArray => dataArray.map((item, index) => {
+ if (!item.hidden) {
+ return (
+ {item.label}
+ );
+ }
+ return false;
+ });
+ return (
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+ { getHead(anchor) }
+
+
+
+ {getItems(items)}
+
+
+
+
+ );
+ }
+}
+
+MainTableForm.propTypes = {
+ title: PropTypes.string.isRequired,
+ classes: PropTypes.object.isRequired,
+ items: PropTypes.object.isRequired,
+ anchor: PropTypes.array.isRequired,
+ addNew: PropTypes.func.isRequired,
+ removeRow: PropTypes.func.isRequired,
+ editRow: PropTypes.func.isRequired,
+ branch: PropTypes.string.isRequired,
+};
+
+export default withStyles(styles)(MainTableForm);
diff --git a/front/odiparpack/app/components/Tables/tableParts/Row.js b/front/odiparpack/app/components/Tables/tableParts/Row.js
new file mode 100644
index 0000000..67e7a4d
--- /dev/null
+++ b/front/odiparpack/app/components/Tables/tableParts/Row.js
@@ -0,0 +1,167 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import DeleteIcon from '@material-ui/icons/Delete';
+import EditIcon from '@material-ui/icons/BorderColor';
+import DoneIcon from '@material-ui/icons/Done';
+import css from 'ba-styles/Table.scss';
+import { TableCell, IconButton } from '@material-ui/core';
+import EditableCell from './EditableCell';
+import SelectableCell from './SelectableCell';
+import ToggleCell from './ToggleCell';
+import DatePickerCell from './DatePickerCell';
+import TimePickerCell from './TimePickerCell';
+
+
+const styles = theme => ({
+ button: {
+ margin: theme.spacing(1),
+ },
+});
+
+class Row extends React.Component {
+ render() {
+ const {
+ classes,
+ anchor,
+ item,
+ removeRow,
+ updateRow,
+ editRow,
+ finishEditRow,
+ branch
+ } = this.props;
+ const eventDel = () => {
+ removeRow(item, branch);
+ };
+ const eventEdit = () => {
+ editRow(item, branch);
+ };
+ const eventDone = () => {
+ finishEditRow(item, branch);
+ };
+ const renderCell = dataArray => dataArray.map((itemCell, index) => {
+ if (itemCell.name !== 'action' && !itemCell.hidden) {
+ const inputType = anchor[index].type;
+ switch (inputType) {
+ case 'selection':
+ return (
+ updateRow(event, branch)}
+ cellData={{
+ type: itemCell.name,
+ value: item.get(itemCell.name),
+ id: item.get('id'),
+ }}
+ edited={item.get('edited')}
+ key={index.toString()}
+ options={anchor[index].options}
+ branch={branch}
+ />
+ );
+ case 'toggle':
+ return (
+ updateRow(event, branch)}
+ cellData={{
+ type: itemCell.name,
+ value: item.get(itemCell.name),
+ id: item.get('id'),
+ }}
+ edited={item.get('edited')}
+ key={index.toString()}
+ branch={branch}
+ />
+ );
+ case 'date':
+ return (
+ updateRow(event, branch)}
+ cellData={{
+ type: itemCell.name,
+ value: item.get(itemCell.name),
+ id: item.get('id'),
+ }}
+ edited={item.get('edited')}
+ key={index.toString()}
+ branch={branch}
+ />
+ );
+ case 'time':
+ return (
+ updateRow(event, branch)}
+ cellData={{
+ type: itemCell.name,
+ value: item.get(itemCell.name),
+ id: item.get('id'),
+ }}
+ edited={item.get('edited')}
+ key={index.toString()}
+ branch={branch}
+ />
+ );
+ default:
+ return (
+ updateRow(event, branch)}
+ cellData={{
+ type: itemCell.name,
+ value: item.get(itemCell.name),
+ id: item.get('id'),
+ }}
+ edited={item.get('edited')}
+ key={index.toString()}
+ inputType={inputType}
+ branch={branch}
+ />
+ );
+ }
+ }
+ return false;
+ });
+ return (
+
+ {renderCell(anchor)}
+
+ eventEdit(this)}
+ className={classNames((item.get('edited') ? css.hideAction : ''), classes.button)}
+ aria-label="Edit"
+ >
+
+
+ eventDone(this)}
+ color="secondary"
+ className={classNames((!item.get('edited') ? css.hideAction : ''), classes.button)}
+ aria-label="Done"
+ >
+
+
+ eventDel(this)}
+ className={classes.button}
+ aria-label="Delete"
+ >
+
+
+
+
+ );
+ }
+}
+
+Row.propTypes = {
+ classes: PropTypes.object.isRequired,
+ anchor: PropTypes.array.isRequired,
+ item: PropTypes.object.isRequired,
+ removeRow: PropTypes.func.isRequired,
+ updateRow: PropTypes.func.isRequired,
+ editRow: PropTypes.func.isRequired,
+ finishEditRow: PropTypes.func.isRequired,
+ branch: PropTypes.string.isRequired
+};
+
+export default withStyles(styles)(Row);
diff --git a/front/odiparpack/app/components/Tables/tableParts/RowReadOnly.js b/front/odiparpack/app/components/Tables/tableParts/RowReadOnly.js
new file mode 100644
index 0000000..7da655f
--- /dev/null
+++ b/front/odiparpack/app/components/Tables/tableParts/RowReadOnly.js
@@ -0,0 +1,76 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import css from 'ba-styles/Table.scss';
+import DeleteIcon from '@material-ui/icons/Delete';
+import EditIcon from '@material-ui/icons/BorderColor';
+
+import { TableCell, IconButton } from '@material-ui/core';
+
+const styles = theme => ({
+ button: {
+ margin: theme.spacing(1),
+ },
+});
+
+class RowReadOnly extends React.Component {
+ render() {
+ const {
+ anchor,
+ classes,
+ item,
+ removeRow,
+ editRow,
+ branch
+ } = this.props;
+ const eventDel = () => {
+ removeRow(item, branch);
+ };
+ const eventEdit = () => {
+ editRow(item, branch);
+ };
+ const renderCell = dataArray => dataArray.map((itemCell, index) => {
+ if (itemCell.name !== 'action' && !itemCell.hidden) {
+ return (
+
+ {item.get(itemCell.name) !== undefined ? item.get(itemCell.name).toString() : ''}
+
+ );
+ }
+ return false;
+ });
+ return (
+
+ {renderCell(anchor)}
+
+ eventEdit(this)}
+ className={classNames((item.get('edited') ? css.hideAction : ''), classes.button)}
+ aria-label="Edit"
+ >
+
+
+ eventDel(this)}
+ className={classes.button}
+ aria-label="Delete"
+ >
+
+
+
+
+ );
+ }
+}
+
+RowReadOnly.propTypes = {
+ anchor: PropTypes.array.isRequired,
+ classes: PropTypes.object.isRequired,
+ item: PropTypes.object.isRequired,
+ removeRow: PropTypes.func.isRequired,
+ editRow: PropTypes.func.isRequired,
+ branch: PropTypes.string.isRequired,
+};
+
+export default withStyles(styles)(RowReadOnly);
diff --git a/front/odiparpack/app/components/Tables/tableParts/SelectableCell.js b/front/odiparpack/app/components/Tables/tableParts/SelectableCell.js
new file mode 100644
index 0000000..66fde97
--- /dev/null
+++ b/front/odiparpack/app/components/Tables/tableParts/SelectableCell.js
@@ -0,0 +1,46 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import css from 'ba-styles/Table.scss';
+
+import { Select, MenuItem, TableCell } from '@material-ui/core';
+
+class SelectableCell extends React.Component {
+ handleChange = event => {
+ this.props.updateRow(event, this.props.branch);
+ this.setState({ [event.target.name]: event.target.value });
+ };
+
+ render() {
+ const {
+ cellData,
+ edited,
+ options,
+ } = this.props;
+ return (
+
+
+
+ );
+ }
+}
+
+SelectableCell.propTypes = {
+ options: PropTypes.array.isRequired,
+ cellData: PropTypes.object.isRequired,
+ updateRow: PropTypes.func.isRequired,
+ edited: PropTypes.bool.isRequired,
+ branch: PropTypes.string.isRequired,
+};
+
+export default SelectableCell;
diff --git a/front/odiparpack/app/components/Tables/tableParts/TableHeader.js b/front/odiparpack/app/components/Tables/tableParts/TableHeader.js
new file mode 100644
index 0000000..4ef6b0d
--- /dev/null
+++ b/front/odiparpack/app/components/Tables/tableParts/TableHeader.js
@@ -0,0 +1,74 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { TableCell, TableHead, TableRow, TableSortLabel, Checkbox, Tooltip } from '@material-ui/core';
+
+class TableHeader extends React.Component {
+ createSortHandler = property => event => {
+ this.props.onRequestSort(event, property);
+ };
+
+ render() {
+ const {
+ onSelectAllClick,
+ order,
+ orderBy,
+ numSelected,
+ rowCount,
+ columnData,
+ checkcell
+ } = this.props;
+
+ return (
+
+
+ {checkcell
+ && (
+
+ 0 && numSelected < rowCount}
+ checked={numSelected === rowCount}
+ onChange={onSelectAllClick}
+ />
+
+ )
+ }
+ {columnData.map(column => (
+
+
+
+ {column.label}
+
+
+
+ ), this)}
+
+
+ );
+ }
+}
+
+TableHeader.propTypes = {
+ numSelected: PropTypes.number.isRequired,
+ onRequestSort: PropTypes.func.isRequired,
+ onSelectAllClick: PropTypes.func.isRequired,
+ order: PropTypes.string.isRequired,
+ orderBy: PropTypes.string.isRequired,
+ rowCount: PropTypes.number.isRequired,
+ columnData: PropTypes.array.isRequired,
+ checkcell: PropTypes.bool.isRequired,
+};
+
+export default TableHeader;
diff --git a/front/odiparpack/app/components/Tables/tableParts/TableToolbar.js b/front/odiparpack/app/components/Tables/tableParts/TableToolbar.js
new file mode 100644
index 0000000..940a82c
--- /dev/null
+++ b/front/odiparpack/app/components/Tables/tableParts/TableToolbar.js
@@ -0,0 +1,123 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import { withStyles } from '@material-ui/core/styles';
+import DeleteIcon from '@material-ui/icons/Delete';
+import ArchiveIcon from '@material-ui/icons/Archive';
+import BookmarkIcon from '@material-ui/icons/Bookmark';
+import FilterListIcon from '@material-ui/icons/FilterList';
+import SearchIcon from '@material-ui/icons/Search';
+import {
+ Toolbar,
+ Typography,
+ IconButton,
+ Tooltip,
+ FormControl,
+ Input,
+ InputAdornment,
+} from '@material-ui/core';
+import styles from './tableStyle-jss';
+
+
+class TableToolbar extends React.Component {
+ state = {
+ showSearch: false,
+ }
+
+ toggleSearch() {
+ this.setState({ showSearch: !this.state.showSearch });
+ }
+
+ handleChange(event) {
+ event.persist();
+ this.props.onUserInput(event.target.value);
+ }
+
+ render() {
+ const { numSelected, classes, filterText } = this.props;
+ const { showSearch } = this.state;
+
+ return (
+ 0,
+ })}
+ >
+
+ {numSelected > 0 ? (
+
+ {numSelected}
+ {' '}
+selected
+
+ ) : (
+ Nutrition
+ )}
+
+
+
+ {numSelected > 0 ? (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ) : (
+
+ {showSearch
+ && (
+
+ this.handleChange(event)}
+ endAdornment={(
+
+
+
+
+
+ )}
+ />
+
+ )
+ }
+
+ this.toggleSearch()}
+ >
+
+
+
+
+ )}
+
+
+ );
+ }
+}
+
+TableToolbar.propTypes = {
+ classes: PropTypes.object.isRequired,
+ filterText: PropTypes.string.isRequired,
+ onUserInput: PropTypes.func.isRequired,
+ numSelected: PropTypes.number.isRequired,
+};
+
+export default withStyles(styles)(TableToolbar);
diff --git a/front/odiparpack/app/components/Tables/tableParts/TimePickerCell.js b/front/odiparpack/app/components/Tables/tableParts/TimePickerCell.js
new file mode 100644
index 0000000..941df31
--- /dev/null
+++ b/front/odiparpack/app/components/Tables/tableParts/TimePickerCell.js
@@ -0,0 +1,66 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { TimePicker, MuiPickersUtilsProvider } from '@material-ui/pickers';
+import MomentUtils from '@date-io/moment';
+import css from 'ba-styles/Table.scss';
+
+import { TableCell, InputAdornment, Icon, IconButton } from '@material-ui/core';
+
+class TimePickerCell extends React.Component {
+ state = {
+ event: {
+ target: {
+ name: this.props.cellData.type, // eslint-disable-line
+ value: this.props.cellData.value, // eslint-disable-line
+ }
+ }
+ }
+
+ handleTimeChange = date => {
+ const { event } = this.state;
+ const { updateRow, branch } = this.props;
+ event.target.value = date;
+ updateRow(event, branch);
+ }
+
+ render() {
+ const {
+ edited,
+ cellData
+ } = this.props;
+ const { event } = this.state;
+ return (
+
+
+
+
+ access_time
+
+
+ ),
+ }}
+ onChange={this.handleTimeChange}
+ />
+
+
+ );
+ }
+}
+
+TimePickerCell.propTypes = {
+ cellData: PropTypes.object.isRequired,
+ updateRow: PropTypes.func.isRequired,
+ edited: PropTypes.bool.isRequired,
+ branch: PropTypes.string.isRequired,
+};
+
+export default TimePickerCell;
diff --git a/front/odiparpack/app/components/Tables/tableParts/ToggleCell.js b/front/odiparpack/app/components/Tables/tableParts/ToggleCell.js
new file mode 100644
index 0000000..dc0af89
--- /dev/null
+++ b/front/odiparpack/app/components/Tables/tableParts/ToggleCell.js
@@ -0,0 +1,50 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import css from 'ba-styles/Table.scss';
+
+import { TableCell, FormControlLabel, Switch } from '@material-ui/core';
+
+class ToggleCell extends React.Component {
+ state = {
+ isChecked: this.props.cellData.value
+ };
+
+ handleChange = event => {
+ this.setState({ isChecked: event.target.checked });
+ this.props.updateRow(event, this.props.branch);
+ };
+
+ render() {
+ const {
+ cellData,
+ edited,
+ } = this.props;
+ return (
+
+
+
+ )}
+ />
+
+ );
+ }
+}
+
+ToggleCell.propTypes = {
+ cellData: PropTypes.object.isRequired,
+ updateRow: PropTypes.func.isRequired,
+ edited: PropTypes.bool.isRequired,
+ branch: PropTypes.string.isRequired,
+};
+
+export default ToggleCell;
diff --git a/front/odiparpack/app/components/Tables/tableParts/tableStyle-jss.js b/front/odiparpack/app/components/Tables/tableParts/tableStyle-jss.js
new file mode 100644
index 0000000..bede0b8
--- /dev/null
+++ b/front/odiparpack/app/components/Tables/tableParts/tableStyle-jss.js
@@ -0,0 +1,63 @@
+import { lighten } from '@material-ui/core/styles/colorManipulator';
+const styles = theme => ({
+ root: {
+ paddingRight: theme.spacing(1),
+ },
+ rootTable: {
+ width: '100%',
+ marginTop: theme.spacing(3),
+ overflowX: 'auto',
+ },
+ highlight:
+ theme.palette.type === 'light' ? {
+ color: theme.palette.secondary.main,
+ backgroundColor: lighten(theme.palette.secondary.light, 0.85),
+ } : {
+ color: theme.palette.text.primary,
+ backgroundColor: theme.palette.secondary.dark,
+ },
+ spacer: {
+ flex: '1 1 100%',
+ },
+ actionsToolbar: {
+ color: theme.palette.text.secondary,
+ flex: '1 0 auto',
+ },
+ titleToolbar: {
+ flex: '0 0 auto',
+ },
+ filterBtn: {
+ top: -5,
+ },
+ textField: {
+ flexBasis: 200,
+ width: 300
+ },
+ table: {
+ minWidth: 900,
+ },
+ actions: {
+ color: theme.palette.text.secondary,
+ margin: 10
+ },
+ toolbar: {
+ backgroundColor: theme.palette.grey[800],
+ },
+ title: {
+ flex: '0 0 auto',
+ '& h6': {
+ color: theme.palette.common.white
+ }
+ },
+ button: {
+ margin: theme.spacing(1),
+ },
+ iconSmall: {
+ fontSize: 20,
+ },
+ leftIcon: {
+ marginRight: theme.spacing(1),
+ },
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/TemplateSettings/ThemeThumb.js b/front/odiparpack/app/components/TemplateSettings/ThemeThumb.js
new file mode 100644
index 0000000..df16917
--- /dev/null
+++ b/front/odiparpack/app/components/TemplateSettings/ThemeThumb.js
@@ -0,0 +1,74 @@
+import React from 'react';
+import classNames from 'classnames';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import { withStyles } from '@material-ui/core/styles';
+import themePalette from 'ba-api/themePalette';
+import { Radio, Paper } from '@material-ui/core';
+import styles from './themeStyles-jss';
+
+const ThemeThumb = props => {
+ const { classes } = props;
+ return (
+
+
+ { props.name }
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+ThemeThumb.propTypes = {
+ value: PropTypes.string.isRequired,
+ theme: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ selectedValue: PropTypes.string.isRequired,
+ classes: PropTypes.object.isRequired,
+ handleChange: PropTypes.func.isRequired,
+};
+
+// Redux
+const reducer = 'ui';
+const mapStateToProps = state => ({
+ theme: state.getIn([reducer, 'theme']),
+});
+
+const ThumbsMapped = connect(
+ mapStateToProps,
+)(ThemeThumb);
+
+export default withStyles(styles)(ThumbsMapped);
diff --git a/front/odiparpack/app/components/TemplateSettings/index.js b/front/odiparpack/app/components/TemplateSettings/index.js
new file mode 100644
index 0000000..d36f5ec
--- /dev/null
+++ b/front/odiparpack/app/components/TemplateSettings/index.js
@@ -0,0 +1,93 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import {
+ FormControl,
+ Grid,
+ FormControlLabel,
+ Dialog,
+ DialogActions,
+ DialogContent,
+ DialogTitle,
+ Icon,
+ Slide,
+ Button,
+} from '@material-ui/core';
+import styles from './themeStyles-jss';
+import ThemeThumb from './ThemeThumb';
+
+const Transition = React.forwardRef(function Transition(props, ref) { // eslint-disable-line
+ return ;
+});
+
+function TemplateSettings(props) {
+ const {
+ classes,
+ palette,
+ open,
+ selectedValue,
+ changeTheme,
+ close
+ } = props;
+
+ const getItem = dataArray => dataArray.map((item, index) => (
+
+ )}
+ />
+ ));
+
+ return (
+
+ );
+}
+
+TemplateSettings.propTypes = {
+ classes: PropTypes.object.isRequired,
+ palette: PropTypes.object,
+ selectedValue: PropTypes.string.isRequired,
+ changeTheme: PropTypes.func.isRequired,
+ close: PropTypes.func.isRequired,
+ open: PropTypes.bool.isRequired,
+};
+
+TemplateSettings.defaultProps = {
+ palette: undefined
+};
+
+export default withStyles(styles)(TemplateSettings);
diff --git a/front/odiparpack/app/components/TemplateSettings/themeStyles-jss.js b/front/odiparpack/app/components/TemplateSettings/themeStyles-jss.js
new file mode 100644
index 0000000..efbac9c
--- /dev/null
+++ b/front/odiparpack/app/components/TemplateSettings/themeStyles-jss.js
@@ -0,0 +1,106 @@
+const styles = theme => ({
+ group: {
+ margin: `${theme.spacing(1)}px 0`,
+ maxWidth: 1000,
+ display: 'block',
+ '& label': {
+ display: 'inline-block',
+ margin: 0,
+ width: '99%',
+ [theme.breakpoints.up('sm')]: {
+ width: '45%'
+ },
+ [theme.breakpoints.up('md')]: {
+ width: '33.33%'
+ },
+ },
+ '& > label': {
+ position: 'relative',
+ '& > span': {
+ marginTop: 10,
+ display: 'block',
+ textAlign: 'center',
+ position: 'absolute',
+ top: 12,
+ left: 48,
+ }
+ }
+ },
+ thumb: {
+ margin: theme.spacing(1)
+ },
+ selectedTheme: {
+ boxShadow: `0px 1px 5px 0px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 3px 1px -2px rgba(0, 0, 0, 0.12), 0 0 0px 4px ${theme.palette.secondary.main}`
+ },
+ content: {},
+ title: {},
+ ctn1: {},
+ ctn2: {},
+ appPreview: {
+ width: '100%',
+ padding: 5,
+ height: 200,
+ position: 'relative',
+ display: 'flex',
+ background: theme.palette.grey[50],
+ '& header': {
+ width: '100%',
+ height: 50,
+ background: theme.palette.primary.main,
+ position: 'absolute',
+ top: 0,
+ left: 0
+ },
+ '& aside': {
+ width: '30%',
+ marginTop: 70,
+ '& li': {
+ margin: '0 10px 10px 5px',
+ display: 'flex',
+ '& i': {
+ borderRadius: '50%',
+ width: 8,
+ height: 8,
+ marginRight: 5,
+ marginTop: -3,
+ background: theme.palette.secondary.main,
+ },
+ '& p': {
+ width: 40,
+ height: 2,
+ background: theme.palette.grey[400],
+ }
+ }
+ },
+ '& $content': {
+ padding: 10,
+ marginTop: 20,
+ width: '70%',
+ zIndex: 10,
+ marginBottom: 10,
+ '& $title': {
+ background: theme.palette.primary.main,
+ height: 5,
+ width: '60%',
+ marginBottom: 10
+ },
+ '& $ctn1': {
+ margin: '5px 5px 10px 0',
+ width: '100%',
+ height: 30,
+ background: theme.palette.secondary.main,
+ display: 'block'
+ },
+ '& $ctn2': {
+ width: '50%',
+ marginLeft: 0,
+ height: 40,
+ border: '2px solid #FFF',
+ background: theme.palette.secondary.light,
+ display: 'inline-block'
+ }
+ }
+ }
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/Widget/AlbumWidget.js b/front/odiparpack/app/components/Widget/AlbumWidget.js
new file mode 100644
index 0000000..d691361
--- /dev/null
+++ b/front/odiparpack/app/components/Widget/AlbumWidget.js
@@ -0,0 +1,55 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import InfoIcon from '@material-ui/icons/Info';
+import imgData from 'ba-api/imgData';
+import { GridList, GridListTile, GridListTileBar, IconButton } from '@material-ui/core';
+import PapperBlock from '../PapperBlock/PapperBlock';
+import styles from './widget-jss';
+
+
+class AlbumWidget extends React.Component {
+ render() {
+ const { classes } = this.props;
+ return (
+
+
+
+ {
+ imgData.map((tile, index) => {
+ if (index >= 4) {
+ return false;
+ }
+ return (
+
+
+
+by:
+ {tile.author}
+
+ )}
+ actionIcon={(
+
+
+
+ )}
+ />
+
+ );
+ })
+ }
+
+
+
+ );
+ }
+}
+
+AlbumWidget.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(AlbumWidget);
diff --git a/front/odiparpack/app/components/Widget/AreaChartWidget.js b/front/odiparpack/app/components/Widget/AreaChartWidget.js
new file mode 100644
index 0000000..8e17311
--- /dev/null
+++ b/front/odiparpack/app/components/Widget/AreaChartWidget.js
@@ -0,0 +1,147 @@
+import React, { PureComponent } from 'react';
+import PropTypes from 'prop-types';
+import { withStyles, createMuiTheme } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import CardGiftcard from '@material-ui/icons/CardGiftcard';
+import FilterVintage from '@material-ui/icons/FilterVintage';
+import LocalCafe from '@material-ui/icons/LocalCafe';
+import Style from '@material-ui/icons/Style';
+import themePallete from 'ba-api/themePalette';
+import {
+ AreaChart,
+ Area,
+ XAxis,
+ YAxis,
+ CartesianGrid,
+ Tooltip,
+ ResponsiveContainer
+} from 'recharts';
+import messageStyles from 'ba-styles/Messages.scss';
+import { data1 } from 'ba-api/chartData';
+import Type from 'ba-styles/Typography.scss';
+import { purple } from '@material-ui/core/colors';
+import { Grid, Chip, Avatar, Divider, CircularProgress, Typography } from '@material-ui/core';
+import styles from './widget-jss';
+import PapperBlock from '../PapperBlock/PapperBlock';
+
+
+const theme = createMuiTheme(themePallete('magentaTheme'));
+const color = ({
+ primary: theme.palette.primary.main,
+ primarydark: theme.palette.primary.dark,
+ secondary: theme.palette.secondary.main,
+ secondarydark: theme.palette.secondary.dark,
+ third: purple[500],
+ thirddark: purple[900],
+});
+
+class AreaChartWidget extends PureComponent {
+ render() {
+ const {
+ classes,
+ } = this.props;
+ return (
+
+
+
+
+ -
+
+
+
+
+ 4321
+ Gift Card
+
+
+ -
+
+
+
+
+ 9876
+ Flowers
+
+
+ -
+
+
+
+
+ 345
+ Cups
+
+
+ -
+
+
+
+
+ 996
+ Name Cards
+
+
+
+
+
+
+ Performance Listing
+
+
+
+ Giftcard Quality
+
+
+
+
+ Monitoring Quality
+
+
+
+
+ Project Complete
+
+
+
+
+ Deploy Progress
+
+
+
+
+
+
+
+ );
+ }
+}
+
+AreaChartWidget.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(AreaChartWidget);
diff --git a/front/odiparpack/app/components/Widget/BigChartWidget.js b/front/odiparpack/app/components/Widget/BigChartWidget.js
new file mode 100644
index 0000000..718af45
--- /dev/null
+++ b/front/odiparpack/app/components/Widget/BigChartWidget.js
@@ -0,0 +1,142 @@
+import React, { PureComponent } from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import Dvr from '@material-ui/icons/Dvr';
+import Explore from '@material-ui/icons/Explore';
+import Healing from '@material-ui/icons/Healing';
+import LocalActivity from '@material-ui/icons/LocalActivity';
+import {
+ ComposedChart,
+ Line,
+ Area,
+ Bar,
+ XAxis,
+ YAxis,
+ CartesianGrid,
+ Tooltip,
+ ResponsiveContainer
+} from 'recharts';
+import { data1 } from 'ba-api/chartData';
+import Type from 'ba-styles/Typography.scss';
+import colorfull from 'ba-api/colorfull';
+import { Grid, Avatar, Divider, LinearProgress, Typography } from '@material-ui/core';
+import styles from './widget-jss';
+import PapperBlock from '../PapperBlock/PapperBlock';
+
+
+const color = ({
+ main: colorfull[5],
+ maindark: '#2196F3',
+ secondary: colorfull[3],
+ third: colorfull[0],
+});
+
+class BigChartWidget extends PureComponent {
+ render() {
+ const {
+ classes,
+ } = this.props;
+ return (
+
+
+
+
+ -
+
+
+
+
+ 1234
+ Monitors
+
+
+ -
+
+
+
+
+ 5678
+ Compas
+
+
+ -
+
+
+
+
+ 910
+ Badges
+
+
+ -
+
+
+
+
+ 1112
+ Tickets
+
+
+
+
+
+
+ Performance Listing
+
+
+ -
+ Monitoring Quality
+
+
+ -
+ Compas Speed
+
+
+ -
+ Total Badges
+
+
+ -
+ Sold Ticket
+
+
+ -
+ App Performance
+
+
+
+
+
+
+ );
+ }
+}
+
+BigChartWidget.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(BigChartWidget);
diff --git a/front/odiparpack/app/components/Widget/CarouselWidget.js b/front/odiparpack/app/components/Widget/CarouselWidget.js
new file mode 100644
index 0000000..51dd522
--- /dev/null
+++ b/front/odiparpack/app/components/Widget/CarouselWidget.js
@@ -0,0 +1,117 @@
+import React from 'react';
+import Slider from 'react-slick';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import ArrowForward from '@material-ui/icons/ArrowForward';
+import ArrowBack from '@material-ui/icons/ArrowBack';
+import carouselData from 'ba-api/carouselData';
+import 'ba-styles/vendors/slick-carousel/slick-carousel.css';
+import 'ba-styles/vendors/slick-carousel/slick.css';
+import 'ba-styles/vendors/slick-carousel/slick-theme.css';
+import { Typography, IconButton, Icon } from '@material-ui/core';
+import styles from './widget-jss';
+
+
+function SampleNextArrow(props) {
+ const { onClick } = props;
+ return (
+
+
+
+ );
+}
+
+SampleNextArrow.propTypes = {
+ onClick: PropTypes.func,
+};
+
+SampleNextArrow.defaultProps = {
+ onClick: undefined,
+};
+
+function SamplePrevArrow(props) {
+ const { onClick } = props;
+ return (
+
+
+
+ );
+}
+
+SamplePrevArrow.propTypes = {
+ onClick: PropTypes.func,
+};
+
+SamplePrevArrow.defaultProps = {
+ onClick: undefined,
+};
+
+class CarouselWidget extends React.Component {
+ render() {
+ const { classes } = this.props;
+ const settings = {
+ dots: true,
+ infinite: true,
+ centerMode: false,
+ speed: 500,
+ autoplaySpeed: 5000,
+ pauseOnHover: true,
+ autoplay: true,
+ slidesToShow: 3,
+ slidesToScroll: 1,
+ responsive: [
+ {
+ breakpoint: 960,
+ settings: {
+ slidesToShow: 2,
+ slidesToScroll: 1,
+ infinite: true,
+ dots: true
+ }
+ },
+ {
+ breakpoint: 600,
+ settings: {
+ slidesToShow: 1,
+ slidesToScroll: 1,
+ infinite: true,
+ dots: true
+ }
+ },
+ ],
+ cssEase: 'ease-out',
+ nextArrow: ,
+ prevArrow:
+ };
+ return (
+
+
+ {carouselData.map((item, index) => (
+
+
+ {item.icon}
+
+ {item.icon}
+ {item.title}
+
+ {item.desc}
+
+
+ ))}
+
+
+ );
+ }
+}
+
+CarouselWidget.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(CarouselWidget);
diff --git a/front/odiparpack/app/components/Widget/CounterGroupWidget.js b/front/odiparpack/app/components/Widget/CounterGroupWidget.js
new file mode 100644
index 0000000..92856b8
--- /dev/null
+++ b/front/odiparpack/app/components/Widget/CounterGroupWidget.js
@@ -0,0 +1,101 @@
+import React, { PureComponent } from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import {
+ BarChart, Bar,
+ AreaChart, Area,
+ LineChart, Line,
+ PieChart, Pie, Cell
+} from 'recharts';
+import { data1, data2 } from 'ba-api/chartMiniData';
+import colorfull from 'ba-api/colorfull';
+import { red, blue, cyan, lime } from '@material-ui/core/colors';
+import { Grid } from '@material-ui/core';
+import CounterWidget from '../Counter/CounterWidget';
+import styles from './widget-jss';
+
+
+const colors = [red[300], blue[300], cyan[300], lime[300]];
+
+class CounterGroupWidget extends PureComponent {
+ render() {
+ const { classes } = this.props;
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ data2.map((entry, index) => | )
+ }
+
+
+
+
+
+
+ );
+ }
+}
+
+CounterGroupWidget.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(CounterGroupWidget);
diff --git a/front/odiparpack/app/components/Widget/CounterIconsWidget.js b/front/odiparpack/app/components/Widget/CounterIconsWidget.js
new file mode 100644
index 0000000..9c10850
--- /dev/null
+++ b/front/odiparpack/app/components/Widget/CounterIconsWidget.js
@@ -0,0 +1,74 @@
+import React, { PureComponent } from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import AccountBox from '@material-ui/icons/AccountBox';
+import ImportContacts from '@material-ui/icons/ImportContacts';
+import Pets from '@material-ui/icons/Pets';
+import Star from '@material-ui/icons/Star';
+import colorfull from 'ba-api/colorfull';
+import { Grid } from '@material-ui/core';
+import CounterWidget from '../Counter/CounterWidget';
+import styles from './widget-jss';
+
+
+class CounterIconWidget extends PureComponent {
+ render() {
+ const { classes } = this.props;
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+CounterIconWidget.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(CounterIconWidget);
diff --git a/front/odiparpack/app/components/Widget/MapWidget.js b/front/odiparpack/app/components/Widget/MapWidget.js
new file mode 100644
index 0000000..37204e9
--- /dev/null
+++ b/front/odiparpack/app/components/Widget/MapWidget.js
@@ -0,0 +1,60 @@
+import React from 'react';
+import { compose } from 'recompose';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import dummy from 'ba-api/dummyContents';
+import {
+ withScriptjs,
+ withGoogleMap,
+ GoogleMap,
+ Marker,
+} from 'react-google-maps';
+import { Paper } from '@material-ui/core';
+import IdentityCard from '../CardPaper/IdentityCard';
+import styles from './widget-jss';
+
+const MapWithAMarker = compose(
+ withScriptjs,
+ withGoogleMap
+)(props => (
+
+
+
+));
+
+class MapWidget extends React.Component {
+ render() {
+ const { classes } = this.props;
+ return (
+
+ }
+ containerElement={}
+ mapElement={}
+ />
+
+
+
+
+ );
+ }
+}
+
+MapWidget.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(MapWidget);
diff --git a/front/odiparpack/app/components/Widget/ProfileWidget.js b/front/odiparpack/app/components/Widget/ProfileWidget.js
new file mode 100644
index 0000000..3ae3690
--- /dev/null
+++ b/front/odiparpack/app/components/Widget/ProfileWidget.js
@@ -0,0 +1,54 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import LocalPhone from '@material-ui/icons/LocalPhone';
+import DateRange from '@material-ui/icons/DateRange';
+import LocationOn from '@material-ui/icons/LocationOn';
+import {
+ List, ListItem, ListItemAvatar,
+ ListItemText, Divider, Avatar
+} from '@material-ui/core';
+import PapperBlock from '../PapperBlock/PapperBlock';
+import styles from './widget-jss';
+
+
+function ProfileWidget(props) {
+ const { classes } = props;
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+ProfileWidget.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(ProfileWidget);
diff --git a/front/odiparpack/app/components/Widget/ProgressWidget.js b/front/odiparpack/app/components/Widget/ProgressWidget.js
new file mode 100644
index 0000000..c0e6db6
--- /dev/null
+++ b/front/odiparpack/app/components/Widget/ProgressWidget.js
@@ -0,0 +1,39 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Type from 'ba-styles/Typography.scss';
+import Check from '@material-ui/icons/Check';
+import { withStyles } from '@material-ui/core/styles';
+import { LinearProgress, Paper, Typography, Grid, Avatar, Chip } from '@material-ui/core';
+import styles from './widget-jss';
+
+
+function ProgressWidget(props) {
+ const { classes } = props;
+ return (
+
+
+ Profile Strength:
+ Intermediate
+
+
+
+
+
+ )}
+ label="60% Progress"
+ className={classes.chipProgress}
+ color="primary"
+ />
+
+
+
+ );
+}
+
+ProgressWidget.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(ProgressWidget);
diff --git a/front/odiparpack/app/components/Widget/SliderWidget.js b/front/odiparpack/app/components/Widget/SliderWidget.js
new file mode 100644
index 0000000..1f53780
--- /dev/null
+++ b/front/odiparpack/app/components/Widget/SliderWidget.js
@@ -0,0 +1,69 @@
+import React from 'react';
+import Type from 'ba-styles/Typography.scss';
+import Slider from 'react-animated-slider';
+import 'ba-styles/vendors/react-animated-slider/react-animated-slider.css';
+import imgApi from 'ba-api/images';
+import avatarApi from 'ba-api/avatars';
+
+import { Button, Typography } from '@material-ui/core';
+
+const content = [
+ {
+ title: 'Vulputate Mollis Ultricies',
+ description:
+ 'Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum.',
+ button: 'Read More',
+ image: imgApi[3],
+ user: 'Luanda Gjokaj',
+ userProfile: avatarApi[1]
+ },
+ {
+ title: 'Tortor Dapibus',
+ description:
+ 'Cras mattis consectetur purus sit amet fermentum.',
+ button: 'Discover',
+ image: imgApi[15],
+ user: 'Erich Behrens',
+ userProfile: avatarApi[8]
+ },
+ {
+ title: 'Phasellus volutpat',
+ description:
+ 'Lorem ipsum dolor sit amet',
+ button: 'Buy now',
+ image: imgApi[29],
+ user: 'Bruno Vizovskyy',
+ userProfile: avatarApi[10]
+ }
+];
+
+const SliderWidget = () => (
+
+
+ {content.map((item, index) => (
+
+
+ {item.title}
+
+
+
+
+
+ Posted by
+ {' '}
+ {item.user}
+
+
+
+ ))}
+
+
+);
+
+export default SliderWidget;
diff --git a/front/odiparpack/app/components/Widget/TableWidget.js b/front/odiparpack/app/components/Widget/TableWidget.js
new file mode 100644
index 0000000..47bb7ba
--- /dev/null
+++ b/front/odiparpack/app/components/Widget/TableWidget.js
@@ -0,0 +1,124 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import messageStyles from 'ba-styles/Messages.scss';
+import progressStyles from 'ba-styles/Progress.scss';
+import avatarApi from 'ba-api/avatars';
+import {
+ Typography,
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableRow,
+ Chip,
+ LinearProgress,
+ Avatar,
+ Icon,
+} from '@material-ui/core';
+import PapperBlock from '../PapperBlock/PapperBlock';
+import styles from './widget-jss';
+
+
+let id = 0;
+function createData(name, avatar, title, type, taskNumber, taskTitle, progress, status) {
+ id += 1;
+ return {
+ id,
+ name,
+ avatar,
+ title,
+ type,
+ taskNumber,
+ taskTitle,
+ progress,
+ status,
+ };
+}
+
+const data = [
+ createData('John Doe', avatarApi[6], 'Front End Developer', 'bug_report', 2214, 'Vivamus sit amet interdum elit', 30, 'Error'),
+ createData('Jim Doe', avatarApi[8], 'System Analyst', 'flag', 2455, 'Nam sollicitudin dignissim nunc', 70, 'Success'),
+ createData('Jane Doe', avatarApi[2], 'Back End Developer', 'whatshot', 3450, 'Quisque ut metus sit amet augue rutrum', 50, 'Warning'),
+ createData('Jack Doe', avatarApi[9], 'CTO', 'settings', 4905, 'Cras convallis lacus orci', 85, 'Info'),
+ createData('Jessica Doe', avatarApi[5], 'Project Manager', 'book', 4118, 'Aenean sit amet magna vel magna', 33, 'Default'),
+];
+
+function TableWidget(props) {
+ const { classes } = props;
+ const getStatus = status => {
+ switch (status) {
+ case 'Error': return messageStyles.bgError;
+ case 'Warning': return messageStyles.bgWarning;
+ case 'Info': return messageStyles.bgInfo;
+ case 'Success': return messageStyles.bgSuccess;
+ default: return messageStyles.bgDefault;
+ }
+ };
+ const getProgress = status => {
+ switch (status) {
+ case 'Error': return progressStyles.bgError;
+ case 'Warning': return progressStyles.bgWarning;
+ case 'Info': return progressStyles.bgInfo;
+ case 'Success': return progressStyles.bgSuccess;
+ default: return progressStyles.bgDefault;
+ }
+ };
+ const getType = type => {
+ switch (type) {
+ case 'bug_report': return classes.red;
+ case 'flag': return classes.indigo;
+ case 'whatshot': return classes.orange;
+ case 'settings': return classes.lime;
+ default: return classes.purple;
+ }
+ };
+ return (
+
+
+
+
+
+ Name
+ Task
+
+
+
+ {data.map(n => ([
+
+
+
+
+
+ {n.name}
+ {n.title}
+
+
+
+
+
+
+
+
+ ])
+ )}
+
+
+
+
+ );
+}
+
+TableWidget.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(TableWidget);
diff --git a/front/odiparpack/app/components/Widget/TaskWidget.js b/front/odiparpack/app/components/Widget/TaskWidget.js
new file mode 100644
index 0000000..4136544
--- /dev/null
+++ b/front/odiparpack/app/components/Widget/TaskWidget.js
@@ -0,0 +1,83 @@
+import React, { Fragment } from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import classNames from 'classnames';
+import CommentIcon from '@material-ui/icons/Comment';
+import {
+ List,
+ ListItem,
+ ListItemSecondaryAction,
+ ListItemText,
+ Checkbox,
+ IconButton,
+} from '@material-ui/core';
+import PapperBlock from '../PapperBlock/PapperBlock';
+import styles from './widget-jss';
+
+
+class TaskWidget extends React.Component {
+ state = {
+ checked: [0],
+ };
+
+ handleToggle = value => () => {
+ const { checked } = this.state;
+ const currentIndex = checked.indexOf(value);
+ const newChecked = [...checked];
+
+ if (currentIndex === -1) {
+ newChecked.push(value);
+ } else {
+ newChecked.splice(currentIndex, 1);
+ }
+
+ this.setState({
+ checked: newChecked,
+ });
+ };
+
+ render() {
+ const { classes } = this.props;
+ return (
+
+
+ {[0, 1, 2, 3, 4].map(value => (
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+ );
+ }
+}
+
+TaskWidget.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(TaskWidget);
diff --git a/front/odiparpack/app/components/Widget/widget-jss.js b/front/odiparpack/app/components/Widget/widget-jss.js
new file mode 100644
index 0000000..d553eaa
--- /dev/null
+++ b/front/odiparpack/app/components/Widget/widget-jss.js
@@ -0,0 +1,294 @@
+import colorfull from 'ba-api/colorfull';
+
+const styles = theme => ({
+ rootCounter: {
+ flexGrow: 1,
+ padding: theme.spacing(1.5),
+ [theme.breakpoints.up('lg')]: {
+ padding: `${theme.spacing(1.5)}px ${theme.spacing(1)}px`,
+ },
+ [theme.breakpoints.down('xs')]: {
+ margin: `0 ${theme.spacing(1) * -1.5}px`,
+ }
+ },
+ rootCounterFull: {
+ flexGrow: 1,
+ },
+ divider: {
+ margin: `${theme.spacing(3)}px 0`
+ },
+ dividerBig: {
+ margin: `${theme.spacing(2)}px 0`
+ },
+ centerItem: {},
+ secondaryWrap: {
+ background: theme.palette.grey[100],
+ padding: 20,
+ borderRadius: 4,
+ justifyContent: 'space-around',
+ '& > $centerItem': {
+ position: 'relative',
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ },
+ '& li': {
+ marginBottom: 30
+ },
+ '& $chip': {
+ top: 70,
+ position: 'absolute',
+ fontSize: 11,
+ fontWeight: 400,
+ }
+ },
+ bigResume: {
+ marginBottom: 20,
+ justifyContent: 'space-between',
+ display: 'flex',
+ [theme.breakpoints.down('sm')]: {
+ height: 160,
+ display: 'block',
+ },
+ '& li': {
+ paddingRight: 20,
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'flex-start',
+ [theme.breakpoints.down('sm')]: {
+ width: '50%',
+ float: 'left'
+ },
+ },
+ },
+ avatar: {
+ width: 50,
+ height: 50,
+ marginRight: 10,
+ '& svg': {
+ fontSize: 32
+ }
+ },
+ pinkAvatar: {
+ margin: 10,
+ color: '#fff',
+ backgroundColor: colorfull[0],
+ },
+ purpleAvatar: {
+ margin: 10,
+ color: '#fff',
+ backgroundColor: colorfull[1],
+ },
+ blueAvatar: {
+ margin: 10,
+ color: '#fff',
+ backgroundColor: colorfull[2],
+ },
+ tealAvatar: {
+ margin: 10,
+ color: '#fff',
+ backgroundColor: colorfull[3],
+ },
+ pinkProgress: {
+ color: colorfull[0],
+ '& div': {
+ backgroundColor: colorfull[0],
+ }
+ },
+ greenProgress: {
+ color: colorfull[5],
+ '& div': {
+ backgroundColor: colorfull[5],
+ }
+ },
+ orangeProgress: {
+ color: colorfull[4],
+ '& div': {
+ backgroundColor: colorfull[4],
+ }
+ },
+ purpleProgress: {
+ color: colorfull[1],
+ '& div': {
+ backgroundColor: colorfull[1],
+ }
+ },
+ blueProgress: {
+ color: colorfull[2],
+ '& div': {
+ backgroundColor: colorfull[2],
+ }
+ },
+ root: {
+ width: '100%',
+ marginTop: theme.spacing(3),
+ overflowX: 'auto',
+ },
+ chip: {
+ margin: '8px 0 8px auto',
+ color: '#FFF'
+ },
+ table: {
+ minWidth: 500,
+ '& td': {
+ padding: 10,
+ }
+ },
+ user: {
+ display: 'flex',
+ },
+ textCenter: {
+ textAlign: 'center'
+ },
+ red: {},
+ orange: {},
+ indigo: {},
+ purple: {},
+ lime: {},
+ taskIcon: {
+ display: 'block',
+ textAlign: 'center',
+ margin: '0 10px',
+ '&$red': {
+ color: colorfull[0],
+ },
+ '&$orange': {
+ color: colorfull[2],
+ },
+ '&$purple': {
+ color: colorfull[1],
+ },
+ '&$lime': {
+ color: colorfull[3],
+ },
+ '&$indigo': {
+ color: colorfull[4],
+ }
+ },
+ done: {},
+ listItem: {
+ padding: 5,
+ background: theme.palette.common.white,
+ boxShadow: theme.shadows[3],
+ '&:hover': {
+ backgroundColor: theme.palette.grey[200]
+ },
+ '&$done': {
+ textDecoration: 'line-through'
+ }
+ },
+ title: {},
+ subtitle: {},
+ styledPaper: {
+ backgroundColor: theme.palette.secondary.main,
+ padding: 20,
+ '& $title, & $subtitle': {
+ color: theme.palette.common.white
+ }
+ },
+ progressWidget: {
+ marginTop: 20,
+ background: theme.palette.secondary.dark,
+ '& div': {
+ background: theme.palette.primary.light,
+ }
+ },
+ chipProgress: {
+ marginTop: 20,
+ background: theme.palette.primary.light,
+ color: theme.palette.secondary.main,
+ '& div': {
+ background: colorfull[4],
+ color: theme.palette.common.white
+ }
+ },
+ taskStatus: {
+ display: 'flex',
+ alignItems: 'center',
+ '& a': {
+ textDecoration: 'none',
+ color: theme.palette.primary.main
+ }
+ },
+ counterIcon: {
+ color: theme.palette.common.white,
+ opacity: 0.7,
+ fontSize: 84
+ },
+ progressCircle: {
+ margin: 20
+ },
+ itemCarousel: {
+ textAlign: 'center',
+ '& img': {
+ margin: '10px auto'
+ }
+ },
+ albumRoot: {
+ display: 'flex',
+ flexWrap: 'wrap',
+ justifyContent: 'space-around',
+ overflow: 'hidden',
+ backgroundColor: theme.palette.background.paper,
+ },
+ gridList: {
+ height: 'auto',
+ [theme.breakpoints.up('sm')]: {
+ width: 500,
+ },
+ },
+ icon: {
+ color: 'rgba(255, 255, 255, 0.54)',
+ },
+ img: {
+ maxWidth: 'none'
+ },
+ mapWrap: {
+ position: 'relative'
+ },
+ address: {
+ [theme.breakpoints.up('md')]: {
+ top: '50%',
+ right: 60,
+ position: 'absolute',
+ transform: 'translate(0, -50%)'
+ },
+ },
+ carouselItem: {
+ margin: '0 5px',
+ boxShadow: theme.shadows[3],
+ borderRadius: 4,
+ overflow: 'hidden',
+ height: 250,
+ padding: '60px 20px',
+ position: 'relative'
+ },
+ iconBg: {
+ color: theme.palette.common.white,
+ opacity: 0.25,
+ position: 'absolute',
+ bottom: 10,
+ right: 10,
+ fontSize: 96
+ },
+ carouselTitle: {
+ color: theme.palette.common.white,
+ display: 'flex',
+ flexDirection: 'column',
+ fontWeight: 500,
+ fontSize: 20
+ },
+ carouselDesc: {
+ color: theme.palette.common.white
+ },
+ chartWrap: {
+ overflow: 'auto',
+ },
+ chartFluid: {
+ width: '100%',
+ minWidth: 400,
+ height: 300,
+ }
+});
+
+export default styles;
diff --git a/front/odiparpack/app/components/index.js b/front/odiparpack/app/components/index.js
new file mode 100644
index 0000000..9d7bdb3
--- /dev/null
+++ b/front/odiparpack/app/components/index.js
@@ -0,0 +1,87 @@
+export Header from './Header/Header';
+export Sidebar from './Sidebar/Sidebar';
+export BreadCrumb from './BreadCrumb/BreadCrumb';
+export SourceReader from './SourceReader/SourceReader';
+export PapperBlock from './PapperBlock/PapperBlock';
+// Dashboard and Widget
+export CounterWidget from './Counter/CounterWidget';
+export SliderWidget from './Widget/SliderWidget';
+export CounterGroupWidget from './Widget/CounterGroupWidget';
+export CounterIconsWidget from './Widget/CounterIconsWidget';
+export BigChartWidget from './Widget/BigChartWidget';
+export TableWidget from './Widget/TableWidget';
+export TaskWidget from './Widget/TaskWidget';
+export ProfileWidget from './Widget/ProfileWidget';
+export ProgressWidget from './Widget/ProgressWidget';
+export AreaChartWidget from './Widget/AreaChartWidget';
+export CarouselWidget from './Widget/CarouselWidget';
+export AlbumWidget from './Widget/AlbumWidget';
+export MapWidget from './Widget/MapWidget';
+// Table Components
+export TreeTable from './Tables/TreeTable';
+export CrudTable from './Tables/CrudTable';
+export CrudTableForm from './Tables/CrudTableForm';
+export AdvTable from './Tables/AdvTable';
+export EmptyData from './Tables/EmptyData';
+// Form
+export Notification from './Notification/Notification';
+export MaterialDropZone from './Forms/MaterialDropZone';
+export LoginForm from './Forms/LoginForm';
+export RegisterForm from './Forms/RegisterForm';
+export ResetForm from './Forms/ResetForm';
+export LockForm from './Forms/LockForm';
+// UI
+export LimitedBadges from './Badges/LimitedBadges';
+export Quote from './Quote/Quote';
+export Pagination from './Pagination/Pagination';
+export ImageLightbox from './ImageLightbox/ImageLightbox';
+export Rating from './Rating/Rating';
+// Social Media
+export Cover from './SocialMedia/Cover';
+export Timeline from './SocialMedia/Timeline';
+export SideSection from './SocialMedia/SideSection';
+export WritePost from './SocialMedia/WritePost';
+// Profile
+export About from './Profile/About';
+export Albums from './Profile/Albums';
+export Connection from './Profile/Connection';
+export Favorites from './Profile/Favorites';
+// Card
+export ProfileCard from './CardPaper/ProfileCard';
+export GeneralCard from './CardPaper/GeneralCard';
+export NewsCard from './CardPaper/NewsCard';
+export PlayerCard from './CardPaper/PlayerCard';
+export PostCard from './CardPaper/PostCard';
+export ProductCard from './CardPaper/ProductCard';
+export VideoCard from './CardPaper/VideoCard';
+export IdentityCard from './CardPaper/IdentityCard';
+// Search
+export SearchProduct from './Search/SearchProduct';
+// Gallery
+export ProductGallery from './Gallery/ProductGallery';
+export PhotoGallery from './Gallery/PhotoGallery';
+// Panel
+export FloatingPanel from './Panel/FloatingPanel';
+export Cart from './Cart/Cart';
+// Contact
+export AddContact from './Contact/AddContact';
+export ContactList from './Contact/ContactList';
+export ContactHeader from './Contact/ContactHeader';
+export ContactDetail from './Contact/ContactDetail';
+// Chat
+export ChatHeader from './Chat/ChatHeader';
+export ChatRoom from './Chat/ChatRoom';
+// Email
+export EmailHeader from './Email/EmailHeader';
+export EmailSidebar from './Email/EmailSidebar';
+export EmailList from './Email/EmailList';
+export ComposeEmail from './Email/ComposeEmail';
+// Calendar
+export EventCalendar from './Calendar/EventCalendar';
+export DetailEvent from './Calendar/DetailEvent';
+export AddEvent from './Calendar/AddEvent';
+export AddEventForm from './Calendar/AddEventForm';
+// Error
+export ErrorWrap from './Error/ErrorWrap';
+// Template Settings
+export TemplateSettings from './TemplateSettings';
--
cgit v1.2.3