Skip to content

Commit

Permalink
Merge branch 'master' into spicer/shop-full-page-sept
Browse files Browse the repository at this point in the history
  • Loading branch information
spicermatthews authored Sep 6, 2023
2 parents 2850fbc + ff7b622 commit 2f8c6a2
Show file tree
Hide file tree
Showing 14 changed files with 796 additions and 22 deletions.
52 changes: 47 additions & 5 deletions src/components/AddShortcut.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ const addProtocolToURLIfNeeded = (url) => {
return url
}

const isValidUrl = (urlString) => {
try {
return Boolean(new URL(urlString))
} catch (e) {
return false
}
}

const AddShortcut = ({
onCancel,
onSave,
Expand All @@ -59,6 +67,8 @@ const AddShortcut = ({
}) => {
const [name, setName] = useState(existingName)
const [url, setUrl] = useState(existingUrl)
const [nameError, setNameError] = useState(null)
const [urlError, setUrlError] = useState(null)
useEffect(() => setName(existingName), [existingName])
useEffect(() => setUrl(existingUrl), [existingUrl])
const classes = useStyles()
Expand All @@ -67,17 +77,49 @@ const AddShortcut = ({
setUrl(existingUrl)
onCancel()
}
const validateName = (newName) => {
let newNameError
if (newName.length === 0) {
newNameError = 'Shortcut name cannot be empty.'
} else {
newNameError = null
}
setNameError(newNameError)
return newNameError
}
const validateUrl = (newUrl) => {
let newUrlError
if (newUrl.length === 0) {
newUrlError = 'URL cannot be empty.'
} else if (!isValidUrl(addProtocolToURLIfNeeded(newUrl))) {
newUrlError = 'URL is not valid.'
} else {
newUrlError = null
}
setUrlError(newUrlError)
return newUrlError
}
const validateForm = () => {
const newNameError = validateName(name)
const newUrlError = validateUrl(url)
return newNameError || newUrlError
}
const onSaveClick = () => {
const newUrl = addProtocolToURLIfNeeded(url)
if (validateForm()) {
return
}
onSave(existingId, name, newUrl)
setName(name)
setUrl(newUrl)
}
const changeName = (e) => {
setName(e.target.value)
validateName(e.target.value)
}
const changeUrl = (e) => {
setUrl(e.target.value)
validateUrl(e.target.value)
}
return (
<Notification
Expand All @@ -100,8 +142,8 @@ const AddShortcut = ({
label="Name"
value={name}
onChange={changeName}
error={name.length === 0}
helperText={name.length === 0 && 'Bookmark name cannot be empty.'}
error={!!nameError}
helperText={nameError}
className={classes.textField}
/>
<TextField
Expand All @@ -110,8 +152,8 @@ const AddShortcut = ({
label="URL"
value={url}
onChange={changeUrl}
error={url.length === 0}
helperText={url.length === 0 && 'URL cannot be empty.'}
error={!!urlError}
helperText={urlError}
className={classes.textField}
/>
</div>
Expand All @@ -130,8 +172,8 @@ const AddShortcut = ({
onClick={onSaveClick}
className={classes.yesButton}
variant="contained"
disabled={name.length === 0 || url.length === 0}
disableElevation
disabled={!!(nameError || urlError)}
>
Save
</Button>
Expand Down
2 changes: 2 additions & 0 deletions src/components/FrontpageShortcutList.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const useStyles = makeStyles((theme) => ({
flexDirection: 'row',
maxWidth: '550px',
flexWrap: 'wrap',
zIndex: 1.4e3,
},
addCircle: {
marginTop: '24px',
Expand All @@ -61,6 +62,7 @@ const useStyles = makeStyles((theme) => ({
},
addShortcutWrapper: {
width: '400px',
position: 'relative',
},
}))
const FrontpageShortcutList = ({ openHandler, user }) => {
Expand Down
23 changes: 22 additions & 1 deletion src/components/groupImpactComponents/GroupImpact.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const GroupImpactWrapper = ({ user }) => {
}

const GroupImpact = ({ user }) => {
const { cause, leaderboard, id: userId } = user
const { cause, leaderboard, id: userId, groupImpactHistory } = user
const { groupImpactMetric, groupImpactMetricCount } = cause
const classes = useStyles()

Expand Down Expand Up @@ -178,6 +178,7 @@ const GroupImpact = ({ user }) => {
groupImpactMetricCount={groupImpactMetricCount}
leaderboard={leaderboard}
userId={userId}
groupImpactHistory={groupImpactHistory}
/>
{sidebarMode !== GROUP_IMPACT_SIDEBAR_STATE.NORMAL && (
<Slide direction="right" in={!sidebarOpen}>
Expand Down Expand Up @@ -231,6 +232,16 @@ GroupImpact.propTypes = {
referralDollarContribution: PropTypes.number,
}),
}),
groupImpactHistory: PropTypes.arrayOf(
PropTypes.shape({
dollarContribution: PropTypes.number.isRequired,
tabDollarContribution: PropTypes.number,
searchDollarContribution: PropTypes.number,
shopDollarContribution: PropTypes.number,
referralDollarContribution: PropTypes.number,
dateStarted: PropTypes.string,
})
),
}).isRequired,
}

Expand Down Expand Up @@ -266,6 +277,16 @@ GroupImpactWrapper.propTypes = {
referralDollarContribution: PropTypes.number,
}),
}),
groupImpactHistory: PropTypes.arrayOf(
PropTypes.shape({
dollarContribution: PropTypes.number.isRequired,
tabDollarContribution: PropTypes.number,
searchDollarContribution: PropTypes.number,
shopDollarContribution: PropTypes.number,
referralDollarContribution: PropTypes.number,
dateStarted: PropTypes.string,
})
),
}).isRequired,
}

Expand Down
177 changes: 177 additions & 0 deletions src/components/groupImpactComponents/GroupImpactContributionRow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import React from 'react'
import { makeStyles } from '@material-ui/core/styles'
import PropTypes from 'prop-types'
import Typography from '@material-ui/core/Typography'
import TabIcon from '@material-ui/icons/Tab'
import SearchIcon from '@material-ui/icons/Search'
import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'
import GroupAddIcon from '@material-ui/icons/GroupAdd'
import clsx from 'clsx'
import defaultTheme from 'src/utils/theme'
import { lighten } from '@material-ui/core'
import Tooltip from '@material-ui/core/Tooltip'
import { getEstimatedMoneyRaisedPerTab } from 'src/utils/misc'
import moment from 'moment'

const useStyles = makeStyles((theme) => ({
wrapper: {
display: 'flex',
flexDirection: 'row',
width: '100%',
justifyContent: 'flex-start',
padding: theme.spacing(2),
alignItems: 'center',
},
column: {
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
},
center: {
alignItems: 'center',
},
iconWrapper: {
height: '24px',
width: '24px',
borderRadius: '24px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
marginRight: theme.spacing(0.5),
},
tabIcon: {
backgroundColor: defaultTheme.palette.colors.tab,
},
searchIcon: {
backgroundColor: defaultTheme.palette.colors.search,
},
shopIcon: {
backgroundColor: defaultTheme.palette.colors.shop,
},
referralIcon: {
backgroundColor: defaultTheme.palette.colors.referral,
},
impactIcons: {
display: 'flex',
flexDirection: 'row',
},
icon: {
height: '16px',
width: '16px',
color: 'white',
},
bold: {
fontWeight: '700',
},
impactPoints: {
marginLeft: 'auto',
},
selected: {
backgroundColor: lighten(theme.palette.primary.main, 0.62),
},
tooltips: {
zIndex: '9999999 !important',
},
position: {
marginRight: theme.spacing(2),
width: '100px',
},
}))
const GroupImpactContributionRow = ({ userGroupImpactMetricLog }) => {
const estimatedMicroUsdsPerTab = Math.max(
getEstimatedMoneyRaisedPerTab() * 10 ** 6,
1
)
const classes = useStyles()
const {
dateStarted,
dollarContribution,
tabDollarContribution,
searchDollarContribution,
shopDollarContribution,
referralDollarContribution,
} = userGroupImpactMetricLog
const impactPoints = Math.ceil(
dollarContribution / estimatedMicroUsdsPerTab
).toLocaleString('en-US')
const dateStartedString = moment(dateStarted).format('l')
return (
<div className={classes.wrapper}>
<Typography className={classes.position} variant="h6">
{dateStartedString}
</Typography>
<div className={classes.column}>
<div className={classes.impactIcons}>
{(tabDollarContribution || tabDollarContribution > 0) && (
<div className={clsx(classes.tabIcon, classes.iconWrapper)}>
<Tooltip
title="This user uses Tab for a Cause"
classes={{
popper: classes.tooltips,
}}
>
<TabIcon className={classes.icon} />
</Tooltip>
</div>
)}
{(searchDollarContribution || searchDollarContribution > 0) && (
<div className={clsx(classes.searchIcon, classes.iconWrapper)}>
<Tooltip
title="This user uses Search for a Cause"
classes={{
popper: classes.tooltips,
}}
>
<SearchIcon className={classes.icon} />
</Tooltip>
</div>
)}
{(shopDollarContribution || shopDollarContribution > 0) && (
<div className={clsx(classes.shopIcon, classes.iconWrapper)}>
<Tooltip
title="This user uses Shop for a Cause"
classes={{
popper: classes.tooltips,
}}
>
<ShoppingCartIcon className={classes.icon} />
</Tooltip>
</div>
)}
{(referralDollarContribution || referralDollarContribution > 0) && (
<div className={clsx(classes.referralIcon, classes.iconWrapper)}>
<Tooltip
title="This user has referred other users"
classes={{
popper: classes.tooltips,
}}
>
<GroupAddIcon className={classes.icon} />
</Tooltip>
</div>
)}
</div>
</div>
<div
className={clsx(classes.impactPoints, classes.column, classes.center)}
>
<Typography className={classes.bold} variant="h6">
{impactPoints}
</Typography>
</div>
</div>
)
}

GroupImpactContributionRow.propTypes = {
userGroupImpactMetricLog: PropTypes.shape({
dollarContribution: PropTypes.number.isRequired,
tabDollarContribution: PropTypes.number,
searchDollarContribution: PropTypes.number,
shopDollarContribution: PropTypes.number,
referralDollarContribution: PropTypes.number,
dateStarted: PropTypes.string,
}).isRequired,
}

export default GroupImpactContributionRow
Loading

0 comments on commit 2f8c6a2

Please sign in to comment.