Together we can create a better world. One individual piece at a time.
An item exchange app to get local retirees swapping puzzles to keep their minds sharp & meet others in their local neighborhood. So if you have a collection of puzzles and are eager for new challenges, you're in the right spot: Join our community of fellow puzzle enthusiasts and dive into a world of delightful exchanges. Happy Puzzle-ing!
Table of Contents
đź’ś Production Website
đź’ś Backend Service
đź’ś Front End Repository
đź’ś Submission Presentation
The Missing Piece was created by an international team of both FrontEnd and BackEnd developers for the Women Who Code (WWC) Hackathon for Social Good 2023.
The Women Who Code Hackathon for Social Good requires that every project aims to support a target group: The Missing Piece Team chose to address is the emotional and mental wellbeing of retirees and seniors by creating an app to facilitate the sharing of items. For our MVP (minimum viable product) we chose to focus on one item type: puzzles.
Why puzzles? Well, puzzles tend to accumulate over a lifetime and end up in boxes in the attic, never to be seen again. They're purchased, completed a few times, and then put away. A puzzle is a type of item that can be exchanged and returned over and over... plus it's fun to work on new ones without having to buy yet another puzzle!
-
Challenge Statement
According to the National Institute of Health, prior to the COVID outbreak, investigators found out that 24% of American individuals 65 and over were socially isolated. That's approximately 7.7 million people. The U.S. Health and Retirement Study reports that 43% of Americans aged 60 or older also reported feeling lonely, which can lead to debilitating issues like depression and anxiety for our loved ones... now think about this on a global scale! ref
In an already isolated world, how do we use technology to connect our seniors through a common interest that is simple to use and can lead to actual genuine connections? By joining the Missing Piece!
-
Solution Statement
The Missing Piece aims to connect retirees through a common interest with an easy, low-pressure opportunity to share puzzles with other locals (without the pressure of attending an event). Not only will this app encourage them get out of the house in order to swap puzzles with others but could hopefully lead to building genuine friendships with people in their local neighborhood. Who knows, maybe they'll start puzzle-parties and work together too!On top of this, studies have shown that solving puzzles keep our minds sharp! It reduces the rate of cognitive decline and increases short-term memory, concentration, and visual-spatial reasoning. Plus, through the simple act of solving a little puzzle, improvements in mood and lower stress-levels have been found.
With the Missing Piece app, users can search for puzzles in their local area. Then they can sign up or login to see their dashboard where they'll be able to upload their own collection of puzzles, see who's requested to borrow a puzzle, and track their own requests to borrow. Once an owner of a puzzle accepts the request, both user's email and phone number are provided so they can coordinate when and where to exchange the puzzle. Once a puzzle has been returned, it is then ready to be borrowed again!
If you'd like to demo this API on your local machine:
- Ensure you have the prerequisites
- Clone this repo:
git@github.com:WWC-Hackathon-2023/missing_piece_api.git
- Navigate to the root folder:
cd missing_piece_api
- Run:
bundle install
- Run:
rails db:{create,migrate}
- Inspect the
/db/schema.rb
and compare to the 'Schema' section below to ensure migration has been done successfully - Run:
rails s
- Visit http://localhost:3000/
- Ruby Version 3.1.1
- Rails Version 7.0.8.x
- Bundler Version 2.4.13
To test the entire spec suite, run bundle exec rspec
.
All tests should be passing.
Happy path and sad path testing were considered and tested. When a request cannot be completed, an error object is returned.
Error Objects
{
"errors": [
{
"status": "404"
"title": "Invalid Request",
"detail": [
"Couldn't find User with 'id'="
]
}
]
}
{
"error": "Unable to update loan status"
}
PUT "/api/v1/puzzles"
Request Body:
{
"zip_code": 12345
}
Response:
Status: 200
{
"data": [
{
"id": "1",
"type": "puzzle",
"attributes": {
"user_id": 1,
"status": "Available",
"title": "Flower Cycle",
"description": "A flower collage by Rosalind Wise",
"total_pieces": 1000,
"notes": "Very Difficult! Only for the brave of heart!",
"puzzle_image_url": "https://cloudinary.com/image/Flower_Cycle.jpg"
}
},
{
"id": "2",
"type": "puzzle",
"attributes": {
"user_id": 1,
"status": "Available",
"title": "Mountain Chalet",
"description": "Cabin near lake and mountains",
"total_pieces": 1000,
"notes": "Relaxing, feels like you're in Colorado!",
"puzzle_image_url": "https://cloudinary.com/image/Mountain_Chalet.jpg"
}
}, {...}
]
}
POST "/api/v1/users"
Request Body:
{
"full_name": "Diana Puzzler",
"email": "d.puzzle@gmail.com",
"password": "PuzzleQueen1",
"password_confirmation": "PuzzleQueen1",
"zip_code": 12345,
"phone_number": 5051230000
}
Response:
Status: 201
{
"data": {
"id": "1",
"type": "user",
"attributes": {
"full_name": "Diana Puzzler",
"email": "d.puzzle@gmail.com",
"zip_code": 12345,
"phone_number": "(505) 123-0000"
}
}
}
POST "/api/v1/login"
Request Body:
{
"email": "d.puzzle@gmail.com",
"password": "PuzzleQueen1"
}
Response:
Status: 201
{
"data": {
"id": "1",
"type": "user",
"attributes": {
"full_name": "Diana Puzzler",
"email": "d.puzzle@gmail.com",
"zip_code": 12345,
"phone_number": "(505) 123-0000"
}
}
}
DELETE "/api/v1/users/:id/logout"
Response:
Status: 204
GET "/api/v1/users/:id"
Response:
Status: 200
{
"data": {
"id": "1",
"type": "user",
"attributes": {
"full_name": "Diana Puzzler",
"email": "d.puzzle@gmail.com",
"zip_code": 12345,
"phone_number": "(505) 123-0000"
}
}
}
GET "/api/v1/users/:id/dashboard"
Response:
Status: 200
{
"data": {
"id": "1",
"type": "dashboard",
"attributes": {
"user_info": {
"full_name": "Diana Puzzler",
"email": "d.puzzle@gmail.com",
"zip_code": 12345,
"phone_number": "(505) 123-0000"
},
"owner_loans": [
{
"loan_id": 1,
"owner_id": 1,
"borrower_id": 2,
"loan_status": "Pending",
"loan_created_at": "2023-10-21T02:52:18.777Z",
"puzzle_id": 1,
"puzzle_image_url": "https://cloudinary.com/imageFlower_Cycle.jpg",
"puzzle_title": "Flower Cycle",
"puzzle_status": "Pending"
}, {...}
],
"borrower_loans": [
{
"loan_id": 5,
"owner_id": 2,
"borrower_id": 1,
"loan_status": "Accepted",
"loan_created_at": "2023-10-21T17:01:40.848Z",
"puzzle_id": 55,
"puzzle_image_url": "https://cloudinary.com/image/Maroon_Lake.jpg",
"puzzle_title": "Maroon Lake",
"puzzle_status": "Not Available"
}, {...}
]
}
}
}
POST "/api/v1/users/:id/puzzles"
Request Body:
{
"title": "Wild Beauty",
"description": "Horses running in the snow by Chris Cummings.",
"total_pieces": 1000,
"notes": "Lots of white snow...beware!",
"puzzle_image_url": "https://cloudinary.com/image/Wild_Beauty.jpg"
}
Response:
Status: 201
{
"data": {
"id": "3",
"type": "puzzle",
"attributes": {
"user_id": 1,
"status": "Available",
"title": "Wild Beauty",
"description": "Horses running in the snow by Chris Cummings.",
"total_pieces": 1000,
"notes": Lots of white snow...beware!",
"puzzle_image_url": "https://cloudinary.com/image/Wild_Beauty.jpg"
}
}
}
GET "/api/v1/users/:id/puzzles"
Response:
Status: 200
{
"data": [
{
"id": "4",
"type": "puzzle",
"attributes": {
"user_id": 1,
"status": "Available",
"title": "Humming Bird & Flowers",
"description": "Hummingbirds investigating some pretty flowers.",
"total_pieces": 1000,
"notes": "Not as hard as you might think!",
"puzzle_image_url": "https://res.cloudinary.com/image/info/Hummingbirds_Flowers.jpg"
}
},
{
"id": "5",
"type": "puzzle",
"attributes": {
"user_id": 1,
"status": "Available",
"title": "Durango Silverton",
"description": "Train coming around the bend!",
"total_pieces": 1000,
"notes": "Feels like a step back in time!",
"puzzle_image_url": "https://res.cloudinary.com/image/info/Durango_Silverton.jpg"
}
}, {...}
]
}
GET "/api/v1/users/:id/puzzles/:id"
Response:
Status: 200
{
"data": {
"id": "1",
"type": "puzzle",
"attributes": {
"user_id": 1,
"status": "Available",
"title": "Flower Cycle",
"description": "A flower collage by Rosalind Wise",
"total_pieces": 1000,
"notes": "Very Difficult! Only for the brave of heart!",
"puzzle_image_url": "https://cloudinary.com/image/Flower_Cycle.jpg"
}
}
}
PATCH "/api/v1/users/:id/puzzles/:id"
Request Body:
{
"status": 2,
"title": "Rosalind Wise Flower Cycle",
"description": "A colorful flower collage",
"total_pieces": 2000,
"notes": "Challenging but not too much. The brave of heart can do it!"
}
Response:
Status: 200
{
"data": {
"id": "1",
"type": "puzzle",
"attributes": {
"user_id": 1,
"status": "Not Available",
"title": "Rosalind Wise Flower Cycle",
"description": "A colorful flower collage",
"total_pieces": 2000,
"notes": "Challenging but not too much. The brave of heart can do it!",
"puzzle_image_url": "https://res.cloudinary.com/image/info/Flower_Cycle.jpg"
}
}
}
POST "/api/v1/users/:id/loans"
Request Body:
{
"borrower_id": 2,
"puzzle_id": 2
}
Response:
Status: 201
{
"data": {
"id": "7",
"type": "loan",
"attributes": {
"owner_id": 1,
"borrower_id": 2,
"puzzle_id": 2,
"status": "Pending"
}
}
}
PATCH "/api/v1/users/:id/loans/:id"
owner clicks accept
OR borrower clicks withdraw
OR owner clicks deny
OR when loan is complete
Request Body:
{
"action_type": "accept"
}
Response:
Status: 200
{
"data": {
"id": "7",
"type": "loan",
"attributes": {
"owner_id": 1,
"borrower_id": 2,
"puzzle_id": 2,
"status": "Accepted"
}
}
}
All endpoints can also be found in the JSON Contract
Cloudinary was used to allow users to seamlessly upload images for their puzzles.
Amazon Web Services s3 Bucket was attempted for image upload/cloud storage.
Passage by 1Password was attempted to authenticate users and ensure login was easy, safe, and quick.
Refactor/Changes
- Conduct a solid refactor of existing code
- - With the limited time of the Hackathon, reviewing the code slower would be first
- Allow a loan to have more than one puzzle
- - This was the original idea for the schema and would involve a join table
- Refactor our connectiong with Cloudinary for multiple image upload & profile images
- - This would possibly require an new table to store puzzle image urls
- Include other item types that could be borrowed
- - Deciding which other item types might be most suited for this app
- - And we'd create even more tables in the database
- Incorporate internal communication tools
- - Internal or external emails and/or text messages might be concidered
- - If so we'd look to Twilio/Send Grid as a start
- Allow users to create a 'Puzzle Party' and invite others
- - Additional tables in the database would be required: parties and user_parties
- - The user table might also become a self-referential table so users can store "friends"
- Add functionality so users can share images or 'Puzzle Parties' on social media
- - Examples would include Facebook and Instagram
- Scale up for international participation
- - Changes would need to be made to both FE form input and BE database validations to allow for multi-formatted and multi-length input of zip codes and phone numbers.
- - As a website scales up run times become a consideration so we would also aim to implement background workers and caching
Andrea Ramirez | Carmen Luna | Natalia Torrejon | Kemi Thomas | Bisrat Melak | Melony Erin Franchini |
FrontEnd | FrontEnd | FrontEnd | FullStack | BackEnd | BackEnd & Team Lead |
GitHub | GitHub | GitHub | GitHub | GitHub | GitHub |