syncUP is social media platform focused on themed groups and event planning inspired by Meetup.
git clone
this repositorycd
into the local repository- Install python dependencies:
pipenv install --dev -r dev-requirements.txt && pipenv install -r requirements.txt
- Create a .env file based on our example
- Shell into the python env:
pipenv shell
- Apply database migrations:
flask db upgrade
- Seed database:
flask seed all
- Run backend:
flask run
git clone
this repository if you haven't alreadycd
into the local repositorycd
into/react-app
npm install
the front end dependencies- Run the front end:
npm start
Note: By default the back end will be running on port 5000, and the front end will be running on port 3000.
- JavaScript
- python
- CSS
- HTML 5
- SQL
Some of the challenges we faced in the development of syncUp include the following:
- During the development of an Event Page, we needed to conditionally render buttons based on the permission level of the user accessing the page. Using boolean logic and React state management we wrote mutually exclusive scenarios that allow the owner of a Group or the organizer of an Event to edit or delete the event. It also allowed for other users to attend an event if they are part of a group, show a notification to join the group before being able to attend, and leave an event they are already attending.
- AWS S3 implementation varies according to the approach and technologies being used in the front- and back-end. Early on in the project we dedicated our effort to adapting the Express backend implementation we had previously worked with, to the Flask backend environment we used for syncUP.
PLACEHOLDER FOR DESCRIPTION
const SearchForm = () => {
const dispatch = useDispatch()
const [searchValue, setSearchValue] = useState('');
const [searchMyGroups, setSearchMyGroups] = useState(false)
const [startDate, setStartDate] = useState(new Date().toISOString().split('T')[0]);
const [endDate, setEndDate] = useState(new Date(2050, 1).toISOString().split('T')[0])
const user = useSelector(state => state.session.user)
const onClick = async (e) => {
e.preventDefault()
dispatch(removeEvents())
dispatch(removeTags())
const groups = user ? user.groups : null
dispatch(searchEvents(searchValue, searchMyGroups, groups, startDate, endDate))
dispatch(searchTags(searchValue))
}
return (
<div className= 'search__container'>
<h1 className= 'form__title'>Search for your next syncUP event</h1>
<div className='search-form__container'>
<form className='search_form'>
<div className='search__input-container'>
<label className='search__label'>
<FontAwesomeIcon className='search__icon' icon={faSearch}/>
</label>
<input
type='text'
className='search__input'
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
/>
</div>
{user &&
<div className='search__input-container'>
<label className="search__label">Search only my groups</label>
<input
type='checkbox'
className='search__input'
checked={Boolean(searchMyGroups)}
onChange={(e) => setSearchMyGroups(!searchMyGroups)}
/>
</div>
}
<div className='search__input-container'>
<label className="search__label">Start Date</label>
<input
type='date'
className='search__input'
value={startDate}
onChange={(e) => setStartDate(e.target.value)}
/>
</div>
<div className='search__input-container'>
<label className="search__label">End Date</label>
<input
type='date'
className='search__input'
value={endDate}
onChange={(e) => setEndDate(e.target.value)}
/>
</div>
<div className='search__input-container'>
<button id='button' className='form__field form__button search__input' onClick={onClick} type="submit">Search</button>
</div>
</form>
</div>
</div>
)
}
Modular updatable user profile inputs
function ProfileBox({label, content, userFile}) {
const dispatch = useDispatch();
const { id } = useParams();
let user = useSelector(state => state.user)
let [buttonText, setButtonText] = useState('Edit');
let [value, setValue] = useState(content);
let [formDisabled, setFormDisabled] = useState(true);
let [imageFile, setImageFile] = useState(null)
const initialValue = content;
useEffect(() => {
setValue(content)
}, [content])
const buttonClick = () => {
const imgButton = document.getElementById('imageButton');
//The editor is not active
if(formDisabled) {
setFormDisabled(false);
if(userFile) {
imgButton.disabled = false;
}
setButtonText('Confirm');
} else {
//The editor is active - Dispatch a thunk to mutate the database
setFormDisabled(true);
setButtonText('Edit');
if(userFile) {
imgButton.disabled = true;
}
if(value === initialValue) return;
dispatch(editUser(user.id, label, value))
}
}
const userFileSubmit = (e) => {
setImageFile(e.target.files[0]);
}
//Check if image file changed - If so, upload to S3
useEffect(() => {
async function fetchUrl() {
const imgUrl = await dispatch(uploadUserImage(imageFile))
setValue(imgUrl);
}
if(imageFile)
fetchUrl();
},[imageFile, dispatch])
function userFileButton() {
return (
<>
<span> </span>
<input
type='file'
onChange={userFileSubmit}
name='imageFile'
id='imageButton'
/>
</>
);
};
return (
<>
<input class='profile__field' type='text' value={value} disabled={formDisabled} onChange={e => setValue(e.target.value)}/>
{ id === 'me' &&
<>
<span> </span>
<input type='button' value={buttonText} onClick={buttonClick}/>
{userFile && userFileButton()}
</>}
</>
);
};