diff --git a/.gitignore b/.gitignore index 2a30de6e..460313f6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,12 @@ node_modules dist .idea debug.log -build-storybook.log -*.bundle.* -storybook-static/* \ No newline at end of file +src/Containers/HorizontalScrollList/HorizontalScrollList.tsx +src/Containers/MiddleCanvas/DroppableElement.tsx +src/Containers/Modal/Modal.tsx +src/Containers/QuestionMarkHover/QuestionMarkHover.stories.tsx +src/Containers/QuestionMarkHover/QuestionMarkHover.tsx +src/Containers/SelectList/SelectList.tsx +src/Fragments/InputFragment.tsx +src/Inputs/Button/Button.tsx + diff --git a/src/Containers/MenuItemCard/MenuItemCard.stories.tsx b/src/Containers/MenuItemCard/MenuItemCard.stories.tsx new file mode 100644 index 00000000..080a59f5 --- /dev/null +++ b/src/Containers/MenuItemCard/MenuItemCard.stories.tsx @@ -0,0 +1,68 @@ +import React from 'react'; +import { Meta, Story } from '@storybook/react'; +import { MenuItemCard, MenuItemCardProps, LoyaltyPoints, LimitedTimeBanner, SaleTag} from '../../index'; +import { createStoryTitle } from '../../Constants'; + +export default { + title: createStoryTitle('MenuItemCard'), + component: MenuItemCard, + subcomponents: {LoyaltyPoints, LimitedTimeBanner, SaleTag}, + argTypes: { onClick: { action: 'Link to next step' } }, +} as Meta; + +const Template: Story = (args) => + ; + +export const MenuItemCardBasic = Template.bind({}); +MenuItemCardBasic.args = { + itemImage: 'https://keyassets-p2.timeincuk.net/wp/prod/wp-content/uploads/sites/63/2007/09/Ricotta-cheese-pancakes-with-blackberry-butter.jpg', + itemName: 'Blackberry Pancakes', + itemPrice: 15.99, + itemPriceLimit: 1000, + saleAmount: 5, + loyaltyamount: 20, + loyaltypointlimit: 100, + minsRemaining: 120, + sale: false, + soldOut: false, + widthFitContent: false, + heightFitContent: false, + animated: true, + flat: false, +}; + +export const MenuItemCardStates = Template.bind({}); +MenuItemCardStates.args = { + itemImage: 'https://keyassets-p2.timeincuk.net/wp/prod/wp-content/uploads/sites/63/2007/09/Ricotta-cheese-pancakes-with-blackberry-butter.jpg', + itemName: 'Blackberry Pancakes', + itemPrice: 15.99, + itemPriceLimit: 1000, + saleAmount: 5, + loyaltyamount: 0, + loyaltypointlimit: 100, + minsRemaining: 0, + sale: true, + soldOut: true, + widthFitContent: false, + heightFitContent: true, + animated: false, + flat: false, +}; + +export const MenuItemCardLongText = Template.bind({}); +MenuItemCardLongText.args = { + itemImage: 'https://keyassets-p2.timeincuk.net/wp/prod/wp-content/uploads/sites/63/2007/09/Ricotta-cheese-pancakes-with-blackberry-butter.jpg', + itemName: 'Super crazy long combo special order with extra sides and drinks blackberry pancakes', + itemPrice: 2560, + itemPriceLimit: 1000, + saleAmount: 200, + loyaltyamount: 150, + loyaltypointlimit: 100, + minsRemaining: 24000, + sale: false, + soldOut: false, + widthFitContent: false, + heightFitContent: true, + animated: true, + flat: false, +}; \ No newline at end of file diff --git a/src/Containers/MenuItemCard/MenuItemCard.tsx b/src/Containers/MenuItemCard/MenuItemCard.tsx new file mode 100644 index 00000000..3881b460 --- /dev/null +++ b/src/Containers/MenuItemCard/MenuItemCard.tsx @@ -0,0 +1,189 @@ +import React from 'react'; +import styled from 'styled-components'; +import { Main, MainInterface, Responsive, ResponsiveInterface } from '@Utils/BaseStyles'; +import { transition } from '@Utils/Mixins'; +// eslint-disable-next-line import/no-cycle +import { LoyaltyPoints, LimitedTimeBanner, SaleTag } from '../../index'; + +export interface MenuItemCardProps + extends MainInterface, ResponsiveInterface, React.HTMLAttributes { + LoyaltyPoints?: React.ReactElement; + LimitedTimeBanner?: React.ReactElement; + SaleTag?: React.ReactElement; + itemImage: string, // Image for the Menu Item Card, hosted on any server and converted to 350px x 200px + itemName: string, // Item name, heightFitContent adjusts the item card size to fit any text (keeping min size of 350px) + itemPrice: number, // Price of the item, will be adjusted by saleAmount and will reduce + round if above 1000 + itemPriceLimit: number, // Limit before item price is divided by 1000, should be greater than 1000 + animated?: boolean; + flat?: boolean; + widthFitContent?: boolean; + heightFitContent?: boolean; + sale?: boolean; + soldOut?: boolean; + loyaltyamount: number, + loyaltypointlimit: number, + saleAmount: number, + minsRemaining: number, +} + +export const ItemCard: React.FC = ({ + children, + ...props +}): React.ReactElement => {children}; + +export const MenuItemCard: React.FC = ({ + animated, + flat, + widthFitContent, + heightFitContent, + sale, + soldOut, + itemImage, + itemName, + itemPrice, + itemPriceLimit, + saleAmount, + loyaltyamount, + loyaltypointlimit, + minsRemaining, + ...props + +}): React.ReactElement => ( + + + {itemName} + {itemName} + { soldOut && Sold Out} + + { !!minsRemaining && + } + + { !!loyaltyamount && + } + + { !!saleAmount && + } + + { sale + ? <> + ${itemPrice} + ${itemPrice - saleAmount} + + : [ itemPrice >= itemPriceLimit + ? <> ${getItemValue(itemPrice, itemPriceLimit)}K + : ${itemPrice} + ] + } + +); +/** +* Checks if the Item Price is greater than or equal to the item price limit +* Returns item price to the nearest tenth rounded +* Also reduces the amount by 1000, the K is added in the component +*/ +function getItemValue(itemPrice: number, itemPriceLimit: number){ + if(itemPrice >= itemPriceLimit) + return ((Math.round(itemPrice * .1) / .1) / 1000) + return itemPriceLimit }; + +const MenuItemCardBox = styled.div` + position: relative; + width: 350px; + height: 350px; + min-height: 350px; + z-index: 1; + cursor: pointer; + box-shadow: ${({ flat, theme }): string => theme.depth[flat ? 0 : 1]}; + + ${({ theme, ...props }): string => ` + border-radius: ${theme.dimensions.radius}; + font-family: ${theme.colors.background}}; + background-color: white; + ${Main({ + ...props, + })} `} + + ${({ animated, flat, theme }): string => + animated ? ` ${transition(['box-shadow'])} &:hover { + box-shadow: ${theme.depth[flat ? 1 : 2]}; + }` : ''} + + ${Responsive} + + ${({ soldOut }) => + soldOut && ` + opacity: 0.5; + cursor: not-allowed; `} + + ${({ widthFitContent }): string => `${widthFitContent ? 'width:fit-content;' : ''} `} + ${({ heightFitContent }): string => `${heightFitContent ? 'height:fit-content;' : ''} `} +`; +const SoldOutBox = styled.div` + ${({theme}):string => ` + font-family: ${theme.font.family}; + color: ${theme.colors.background}}; + `} + position: absolute; + z-index: 2; + top: 0px; + background-color: rgba(48,48,48,48.0); + font-size: 25px; + text-align: center; + width: 350px; + height: 40px; + padding-top: 15px; +`; +const ItemHeader = styled.header` + font-weight: bold; + padding-left: 10px; + padding-bottom: 10px; + padding-right: 120px; + font-size: 35px; +` +const SaleProps = styled.header` + font-weight: bold; + position: absolute; + font-size: 30px; + text-align: right; + right: 10px; + top: 300px; +` +const SalePropsSlash = styled(SaleProps)` + text-decoration: line-through; + font-size: 25px; + opacity: .6; + top: 230px; +` +const SaleProps1k = styled(SaleProps)` + font-size: 35px; + ${({theme}):string => ` + color: ${theme.colors.ItemCardSaleGreen}; + `} +` +const OnSale = styled.header` + position: absolute; + font-size: 35px; + text-align: right; + font-weight: bold; + ${({theme}):string => ` + color: ${theme.colors.primary}; + `} + padding-right: 10px; + right: 10px; + top: 300px; +` +const LoyaltyPointsPosition = styled.div` + position: absolute; + top: 30px; +` +const SaleTagPosition = styled.div` + position: absolute; + right: 10px; + top: 53px; +` +const LimitedTimeBannerPosition = styled.div` + position: absolute; + top: 160px; +` \ No newline at end of file diff --git a/src/Containers/index.ts b/src/Containers/index.ts index 53d32867..57f23f04 100644 --- a/src/Containers/index.ts +++ b/src/Containers/index.ts @@ -86,3 +86,9 @@ export * from './TableHeaderCell/TableHeaderCell'; export * from './CustomerProfile/CustomerProfile'; export * from './CreatedDate/CreatedDate'; export * from './CRMRow/CRMRow'; +// eslint-disable-next-line import/no-cycle +export * from './MenuItemCard/MenuItemCard' +// eslint-disable-next-line import/no-cycle +export * from './LimitedTimeBanner/LimitedTimeBanner' +export * from './LoyaltyPoints/LoyaltyPoints' +export * from './SaleTag/SaleTag' diff --git a/src/Themes/MainTheme.ts b/src/Themes/MainTheme.ts index 3df8d5fc..cfdf195d 100644 --- a/src/Themes/MainTheme.ts +++ b/src/Themes/MainTheme.ts @@ -4,6 +4,9 @@ export interface MainThemeInterface extends ThemeTemplateInterface { colors: { primary: string; text: string; + loyaltyText: string; + bannerBackgroundColor: string; + ItemCardSaleGreen: string; input: { default: string; success: string; @@ -31,6 +34,7 @@ export interface MainThemeInterface extends ThemeTemplateInterface { chairTableBackground: string; chairOccupiedBackground: string; chairTableEditBackground: string; + }; } @@ -60,6 +64,9 @@ export const MainTheme: MainThemeInterface = { chairTableBackground: '#6c757d', chairOccupiedBackground: '#EE2434', chairTableEditBackground: '#C4C4C4', + loyaltyText: '#0000FF', + bannerBackgroundColor: 'rgba(0,0,0,0.5)', + ItemCardSaleGreen: '#04CC00', PieChartColors: { Red: '#FF0000', Green: '#008000', diff --git a/src/Utils/positiveRounding.ts b/src/Utils/positiveRounding.ts new file mode 100644 index 00000000..9ca2b675 --- /dev/null +++ b/src/Utils/positiveRounding.ts @@ -0,0 +1,6 @@ +/** + * Rounds amount to the nearest positive integer above 0 + * @param {number} amount - Number to round + * @returns {number} - Rounded amount above 0 + */ +export const positiveRounding = (amount:number): number => (amount >= 1 ? Math.round(amount) : 1); \ No newline at end of file