diff --git a/.env b/.env index 57d39a6..3f0f181 100644 --- a/.env +++ b/.env @@ -1,2 +1,4 @@ REACT_APP_VERSION=$npm_package_version +REACT_APP_PINATA_API_KEY=f705de5115120b3cabac +REACT_APP_PINATA_API_SECRET=5cbc25412c0332187724e88b337bb239547f32be4aaad7a4cc9772af805ff0c0 SKIP_PREFLIGHT_CHECK=true \ No newline at end of file diff --git a/src/constants/space.ts b/src/constants/space.ts index b764277..89dc385 100644 --- a/src/constants/space.ts +++ b/src/constants/space.ts @@ -9,4 +9,6 @@ export const DATA = `${SPACE_NAME}_DATA`; export const MENU = `${SPACE_NAME}_MENU`; +export const ORDER_LIST = `${SPACE_NAME}_ORDER_LIST`; + export const ORDER_ID = `${SPACE_NAME}_ORDER_ID`; diff --git a/src/helpers/business.ts b/src/helpers/business.ts index 431aba9..69f8dba 100644 --- a/src/helpers/business.ts +++ b/src/helpers/business.ts @@ -1,5 +1,12 @@ import { IData, IProfile, ISettings, IMenu } from "../helpers/types"; -import { openBox, openSpace, setSpacePrivate, getSpacePrivate } from "./box"; +import { + openBox, + openSpace, + setSpacePrivate, + getSpacePrivate, + setSpacePublic, + getSpacePublic +} from "./box"; import { DATA, MENU } from "../constants/space"; import demo from "../demo"; @@ -71,12 +78,12 @@ export async function getData(): Promise { } export async function setMenu(menu: IMenu): Promise { - await setSpacePrivate(MENU, menu); + await setSpacePublic(MENU, menu); return menu; } export async function getMenu(): Promise { - const menu = await getSpacePrivate(MENU); + const menu = await getSpacePublic(MENU); return menu; } diff --git a/src/helpers/order.ts b/src/helpers/order.ts index f9ce442..e18e99a 100644 --- a/src/helpers/order.ts +++ b/src/helpers/order.ts @@ -1,13 +1,13 @@ import { ICheckoutDetails, ISettings, - IOrderItem, + IOrderDetails, IOrderJson } from "../helpers/types"; import { getSpacePrivate, setSpacePrivate } from "./box"; import { uuid } from "../helpers/utilities"; import { PAYMENT_PENDING } from "../constants/paymentStatus"; -import { ORDER_ID } from "../constants/space"; +import { ORDER_ID, ORDER_LIST } from "../constants/space"; import { convertStringToNumber, convertNumberToString, @@ -51,14 +51,34 @@ export function formatCheckoutDetails( return checkout; } +export async function setOrderList(orderList: string[]): Promise { + await setSpacePrivate(ORDER_LIST, orderList); + return orderList; +} + +export async function getOrderList(): Promise { + const orderList = await getSpacePrivate(ORDER_LIST); + return orderList; +} + +export async function updateOrderList(orderId: string): Promise { + let orderList: string[] = []; + + const result = await getOrderList(); + if (result) { + orderList = [...result, orderId]; + } + + await setOrderList(orderList); + + return orderList; +} + function formatOrderKey(orderId: string): string { return `${ORDER_ID}_${orderId}`; } -export async function createOrderJson(orderDetails: { - items: IOrderItem[]; - checkout: ICheckoutDetails; -}): Promise { +function formatOrderJson(orderDetails: IOrderDetails): IOrderJson { const orderId = uuid(); const orderJson: IOrderJson = { @@ -71,10 +91,29 @@ export async function createOrderJson(orderDetails: { result: "" } }; + return orderJson; +} + +export async function setOrderJson(orderJson: IOrderJson): Promise { + const key = formatOrderKey(orderJson.id); + await setSpacePrivate(key, JSON.stringify(orderJson)); + return orderJson.id; +} +export async function getOrderJson(orderId: string): Promise { const key = formatOrderKey(orderId); + const orderJson = await getSpacePrivate(key); + return orderJson; +} - await setSpacePrivate(key, JSON.stringify(orderJson)); +export async function createOrderJson( + orderDetails: IOrderDetails +): Promise { + const orderJson = formatOrderJson(orderDetails); + + const orderId = await setOrderJson(orderJson); + + await updateOrderList(orderId); return orderId; } @@ -83,14 +122,26 @@ export async function updateOrderJson( orderId: string, updatedOrderJson: any ): Promise { - const key = formatOrderKey(orderId); - - const orderJson = await getSpacePrivate(key); + const orderJson = await getOrderJson(orderId); const newOrderJson: IOrderJson = { ...orderJson, ...updatedOrderJson }; - await setSpacePrivate(key, JSON.stringify(newOrderJson)); + await setOrderJson(newOrderJson); +} + +export async function getAllOrders(): Promise { + const orderList = await getOrderList(); + + let orders: IOrderJson[] = []; + + if (orderList) { + orders = await Promise.all( + orderList.map((orderId: string) => getOrderJson(orderId)) + ); + } + + return orders; } diff --git a/src/helpers/types.ts b/src/helpers/types.ts index 53e1b47..211f410 100644 --- a/src/helpers/types.ts +++ b/src/helpers/types.ts @@ -107,6 +107,11 @@ export interface IPayment { result: any; } +export interface IOrderDetails { + items: IOrderItem[]; + checkout: ICheckoutDetails; +} + export interface IOrderJson { id: string; timestamp: number; diff --git a/src/helpers/utilities.ts b/src/helpers/utilities.ts index 266f7e8..8ef8f1d 100644 --- a/src/helpers/utilities.ts +++ b/src/helpers/utilities.ts @@ -323,3 +323,19 @@ export function formatItemId(name: string): string { export function sanitizeImgSrc(image: string): string { return isIpfsHash(image) ? getIpfsUrl(image) : image; } + +export function getCurrentPathname() { + const current = + typeof window !== "undefined" ? sanitizeUrl(window.location.pathname) : ""; + return current || "/"; +} + +export function formatPathname(path: string, match: any) { + return sanitizeUrl(`${match.url}${path}`); +} + +export function isActivePath(path: string, match: any) { + const pathname = formatPathname(path, match); + const current = getCurrentPathname(); + return current === pathname; +} diff --git a/src/layouts/Dashboard/Sidebar.tsx b/src/layouts/Dashboard/Sidebar.tsx index e1a41f0..08cfe73 100644 --- a/src/layouts/Dashboard/Sidebar.tsx +++ b/src/layouts/Dashboard/Sidebar.tsx @@ -3,7 +3,7 @@ import styled from "styled-components"; import { Link } from "react-router-dom"; import Icon from "../../components/Icon"; import { colors } from "../../styles"; -import { sanitizeUrl } from "../../helpers/utilities"; +import { formatPathname, isActivePath } from "../../helpers/utilities"; import { APP_LOGO, APP_NAME } from "../../constants/appMeta"; import overview from "../../assets/navigation/overview.svg"; @@ -109,12 +109,8 @@ const Sidebar = (props: any) => ( {navigation.map(item => { - const pathname = sanitizeUrl(`${props.match.url}${item.path}`); - const current = - typeof window !== "undefined" - ? sanitizeUrl(window.location.pathname) - : ""; - const active = current === pathname; + const pathname = formatPathname(item.path, props.match); + const active = isActivePath(item.path, props.match); return ( ` @@ -45,7 +47,9 @@ const SContentCard = styled.div` align-items: center; min-height: 100%; border-radius: 6px; - padding: ${CONTENT_PADDING * 2}px ${CONTENT_PADDING}px; + height: ${({ fixedScroll }) => (fixedScroll ? `100%` : `initial`)}; + padding: ${({ fixedScroll }) => + fixedScroll ? `0` : `${CONTENT_PADDING * 2}px ${CONTENT_PADDING}px`}; box-shadow: ${shadows.soft}; background: rgb(${colors.white}); color: rgb(${colors.dark}); @@ -71,17 +75,34 @@ const SContentLoading = styled(SCenter)` pointer-events: ${({ show }) => (show ? "auto" : "none")}; `; +const FixedScrollPaths = ["/inventory", "/orders"]; + +function isFixedScroll(match: any) { + let fixedScroll = false; + FixedScrollPaths.forEach((path: string) => { + if (!fixedScroll) { + const active = isActivePath(path, match); + if (active) { + fixedScroll = true; + } + } + }); + return fixedScroll; +} + const Dashboard = (props: any) => { - const { children, match, loading } = props; + const { children, match, settings, loading } = props; const balance = 35245; - const currency = "USD"; + const fixedScroll = isFixedScroll(match); return ( -
+
- {children} - + + {children} + + @@ -95,6 +116,7 @@ Dashboard.propTypes = { children: PropTypes.node.isRequired, match: PropTypes.object.isRequired, loading: PropTypes.bool.isRequired, + settings: PropTypes.object.isRequired, center: PropTypes.bool, maxWidth: PropTypes.number }; diff --git a/src/pages/Admin/Inventory.tsx b/src/pages/Admin/Inventory.tsx index cdae7ca..8a337aa 100644 --- a/src/pages/Admin/Inventory.tsx +++ b/src/pages/Admin/Inventory.tsx @@ -10,13 +10,21 @@ import Button from "../../components/Button"; import { CONTENT_PADDING } from "../../constants/dashboard"; const SButtonWrapper = styled.div` - position: absolute; + position: fixed; bottom: ${CONTENT_PADDING * 2}px; right: ${CONTENT_PADDING * 2}px; `; +const SInventoryList = styled(SColumnList)` + padding: 0; +`; + const SListItem = styled(ListItem)` - margin-bottom: 10px; + margin: 20px; + margin-bottom: 0; + &:last-child { + margin-bottom: 20px; + } `; interface IInventoryProps { @@ -27,12 +35,10 @@ interface IInventoryProps { const Inventory = (props: IInventoryProps) => { const { menu, settings } = props; - console.log("[OrderMenu] menu", menu); // tslint:disable-line - console.log("[OrderMenu] settings", settings); // tslint:disable-line return ( {menu && menu.length ? ( - + {menu.map((item: IMenuItem) => ( { onClick={() => props.adminShowInventoryModal(item)} /> ))} - + ) : ( )} diff --git a/src/pages/Admin/Orders.tsx b/src/pages/Admin/Orders.tsx index 3abd7a7..c8d8e71 100644 --- a/src/pages/Admin/Orders.tsx +++ b/src/pages/Admin/Orders.tsx @@ -1,10 +1,38 @@ import * as React from "react"; -import { SCenter } from "../../components/common"; +import { connect } from "react-redux"; +import { adminGetAllOrders } from "../../redux/_admin"; +import { IOrderJson } from "../../helpers/types"; +import { SColumnList } from "../../components/common"; +import EmptyState from "../../components/EmptyState"; -const Orders = (props: any) => ( - -

{"Orders"}

-
-); +class Orders extends React.Component { + public componentDidMount() { + this.props.adminGetAllOrders(); + } + public render() { + const { orders } = this.props; + return ( + + {orders && orders.length ? ( + + {orders.map((order: IOrderJson) => ( +

{order.id}

+ ))} +
+ ) : ( + + )} +
+ ); + } +} -export default Orders; +const reduxProps = (store: any) => ({ + orders: store.admin.orders, + settings: store.admin.settings +}); + +export default connect( + reduxProps, + { adminGetAllOrders } +)(Orders); diff --git a/src/pages/Admin/index.tsx b/src/pages/Admin/index.tsx index bca4144..bb36e2e 100644 --- a/src/pages/Admin/index.tsx +++ b/src/pages/Admin/index.tsx @@ -23,9 +23,9 @@ class Admin extends React.Component { } public render() { - const { loading, match } = this.props; + const { loading, match, settings } = this.props; return ( - + @@ -45,7 +45,8 @@ class Admin extends React.Component { const reduxProps = (store: any) => ({ loading: store.admin.loading, - address: store.admin.address + address: store.admin.address, + settings: store.admin.settings }); export default connect( diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 9c98e15..b32fb67 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -8,19 +8,19 @@ import Button from "../components/Button"; import { adminConnectWallet } from "../redux/_admin"; class Home extends React.Component { - public onConnect = (provider: any) => { - console.log("[onConnect] provider", provider); // tslint:disable-line - this.props.adminConnectWallet(provider); - }; - - public onClose = () => console.log("[onClose]"); // tslint:disable-line + public onConnect = (provider: any) => this.props.adminConnectWallet(provider); public render() { return (

{`WalletConnect Pay`}

- + { + // do nothing + }} + />

{`Order Menu`}

diff --git a/src/redux/_admin.ts b/src/redux/_admin.ts index d30cf0f..834447e 100644 --- a/src/redux/_admin.ts +++ b/src/redux/_admin.ts @@ -1,5 +1,9 @@ import Web3 from "web3"; -import { queryChainId, formatItemId, sanitizeUrl } from "../helpers/utilities"; +import { + queryChainId, + formatItemId, + getCurrentPathname +} from "../helpers/utilities"; import { IProfile, ISettings, IMenuItem } from "../helpers/types"; import { openBusinessBox, @@ -16,12 +20,17 @@ import { INVENTORY_ITEM } from "../constants/modals"; import { logRedux } from "../helpers/dev"; +import { getAllOrders } from "../helpers/order"; // -- Constants ------------------------------------------------------------- // const ADMIN_CONNECT_REQUEST = "admin/ADMIN_CONNECT_REQUEST"; const ADMIN_CONNECT_SUCCESS = "admin/ADMIN_CONNECT_SUCCESS"; const ADMIN_CONNECT_FAILURE = "admin/ADMIN_CONNECT_FAILURE"; +const ADMIN_GET_ALL_ORDERS_REQUEST = "admin/ADMIN_GET_ALL_ORDERS_REQUEST"; +const ADMIN_GET_ALL_ORDERS_SUCCESS = "admin/ADMIN_GET_ALL_ORDERS_SUCCESS"; +const ADMIN_GET_ALL_ORDERS_FAILURE = "admin/ADMIN_GET_ALL_ORDERS_FAILURE"; + const ADMIN_SUBMIT_SIGNUP_REQUEST = "admin/ADMIN_SUBMIT_SIGNUP_REQUEST"; const ADMIN_SUBMIT_SIGNUP_SUCCESS = "admin/ADMIN_SUBMIT_SIGNUP_SUCCESS"; const ADMIN_SUBMIT_SIGNUP_FAILURE = "admin/ADMIN_SUBMIT_SIGNUP_FAILURE"; @@ -67,6 +76,7 @@ export const adminConnectWallet = (provider: any) => async ( const address = (await web3.eth.getAccounts())[0]; const chainId = await queryChainId(web3); const { data, menu } = await openBusinessBox(address, provider); + const orders = await getAllOrders(); if (data) { const { profile, settings } = data; @@ -78,11 +88,12 @@ export const adminConnectWallet = (provider: any) => async ( chainId, profile, settings, - menu + menu, + orders } }); - const pathname = sanitizeUrl(window.browserHistory.location.pathname); - if (["/", "/signup"].includes(pathname)) { + const current = getCurrentPathname(); + if (["/", "/signup"].includes(current)) { window.browserHistory.push("/admin"); } } else { @@ -95,7 +106,8 @@ export const adminConnectWallet = (provider: any) => async ( chainId, profile, settings, - menu + menu, + orders } }); window.browserHistory.push("/signup"); @@ -107,6 +119,23 @@ export const adminConnectWallet = (provider: any) => async ( } }; +export const adminGetAllOrders = () => async (dispatch: any, getState: any) => { + const { address } = getState().admin; + if (!address) { + return; + } + dispatch({ type: ADMIN_GET_ALL_ORDERS_REQUEST }); + try { + const orders = await getAllOrders(); + + dispatch({ type: ADMIN_GET_ALL_ORDERS_SUCCESS, payload: orders }); + } catch (error) { + console.error(error); // tslint:disable-line + dispatch(notificationShow(error.message, true)); + dispatch({ type: ADMIN_GET_ALL_ORDERS_FAILURE }); + } +}; + export const adminSubmitSignUp = () => async (dispatch: any, getState: any) => { dispatch({ type: ADMIN_SUBMIT_SIGNUP_REQUEST }); try { @@ -245,6 +274,7 @@ const INITIAL_STATE = { address: "", chainId: 1, menu: [], + orders: [], profile: defaultProfile, settings: defaultSettings }; @@ -263,11 +293,22 @@ export default (state = INITIAL_STATE, action: any) => { address: action.payload.address, chainId: action.payload.chainId, menu: action.payload.menu || [], + orders: action.payload.orders || [], profile: action.payload.profile || defaultProfile, settings: action.payload.settings || defaultSettings }; case ADMIN_CONNECT_FAILURE: return { ...state, loading: false }; + case ADMIN_GET_ALL_ORDERS_REQUEST: + return { ...state, loading: true }; + case ADMIN_GET_ALL_ORDERS_SUCCESS: + return { + ...state, + loading: false, + orders: action.payload + }; + case ADMIN_GET_ALL_ORDERS_FAILURE: + return { ...state, loading: false }; case ADMIN_SUBMIT_SIGNUP_REQUEST: return { ...state, loading: true }; case ADMIN_SUBMIT_SIGNUP_SUCCESS: