{"id":38030058,"url":"https://github.com/phamv21/stvp-running-shape","last_synced_at":"2026-01-16T19:38:49.358Z","repository":{"id":169267034,"uuid":"536424173","full_name":"phamv21/stvp-running-shape","owner":"phamv21","description":"MapNRun is a web application inspired by MapMyRun, a social fitness website that allows users to create the running route. Users can log workout details, including duration, distance, and the map of their route. They can also see the activities of their friends.","archived":false,"fork":false,"pushed_at":"2022-10-29T17:20:31.000Z","size":21419,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-01-29T22:48:43.195Z","etag":null,"topics":["roads","running","workouts"],"latest_commit_sha":null,"homepage":"https://mapnrun.herokuapp.com","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/phamv21.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2022-09-14T05:18:49.000Z","updated_at":"2023-03-07T06:09:54.000Z","dependencies_parsed_at":null,"dependency_job_id":"fec0542d-be84-45f6-9a23-0d471df41e64","html_url":"https://github.com/phamv21/stvp-running-shape","commit_stats":{"total_commits":99,"total_committers":1,"mean_commits":99.0,"dds":0.0,"last_synced_commit":"b1dc76f7a19fc23c2bda099784e111cb7e67396c"},"previous_names":["phamv21/stvp-running-shape"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/phamv21/stvp-running-shape","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phamv21%2Fstvp-running-shape","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phamv21%2Fstvp-running-shape/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phamv21%2Fstvp-running-shape/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phamv21%2Fstvp-running-shape/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/phamv21","download_url":"https://codeload.github.com/phamv21/stvp-running-shape/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phamv21%2Fstvp-running-shape/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28481914,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T11:59:17.896Z","status":"ssl_error","status_checked_at":"2026-01-16T11:55:55.838Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["roads","running","workouts"],"created_at":"2026-01-16T19:38:48.467Z","updated_at":"2026-01-16T19:38:49.349Z","avatar_url":"https://github.com/phamv21.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# **MapNRun**\n----\n[MapNRun](https://mapnrun.heroku.com) is a web application inspired by MapMyRun, a social fitness website that allows users to create the running route. Users can log workout details, including duration, distance, and the map of their route. They can also see the activities of their friends. The app was created with React.js and Redux on the frontend and Ruby on Rails/PostgreSQL on the backend.\n\nThe project was designed and built in a week, adding some additional improvements later.\n---\n# **Hightlights**\n---\n* Integrates Google Maps API to create run routes, generate a static thumbnail, manage the route details and enable distance tracking based on geolocation\n* Using Amazon Simple Storage Service to storage map thumbnails \n* Adheres to React and ES6 best practices to generate a true single-page reactive web app experience\n* Developed a fluid UI to mimic the original website using HTML5\nImplemented user authentication with BCrypt password encryption\n\n![alt text][homeimg]\n\n# **Features**\n---\n## **Route creation**\n![alt text][createrouteimg]\n\n### - **Using Google Maps API to render route and calculate the distance**\n### - **Using Google Maps API to manage the pins' note(description)**\n\u003cdetails\u003e\n\u003csummary\u003eShow the Code\u003c/summary\u003e\n\n```javascript\nrenderRoute(){\n        let customIcon ={\n            path: \"M 0 -2 C -2 -2 -2 1 0 1 S 2 -2 0 -2\",\n            fillColor: \"blue\",\n            strokeWeight: 0,\n            fillOpacity: 0.8,\n            scale: 3,\n            \n        };\n        let customIconRed ={\n            path: \"M 0 -2 C -2 -2 -2 1 0 1 S 2 -2 0 -2\",\n            fillColor: 'red',\n            strokeWeight: 0,\n            fillOpacity: 0.8,\n            scale: 3,\n        };\n        // put the pin for the first marker\n        if(this.nodes.length \u003c 2){\n            this.start_point = new google.maps.Marker({\n                position: this.nodes[0].location,\n                icon: customIcon,\n                map: this.map,\n            })\n            \n        }\n        else{\n            //now we can draw map with these data\n            if(this.start_point != null){\n                this.start_point.setMap(null);\n            }\n            const nodesLength = this.nodes.length\n            const headTail = [{location:this.nodes[0].location},{location:this.nodes[nodesLength-1].location}]\n            let tmp = this.nodes.slice(1,-1)\n            const betweens = tmp.map(el =\u003e { return {\n                location:el.location,\n                stopover:true,\n            };});\n            return this.directionsService\n            .route({\n                origin: headTail[0],\n                destination: headTail[1],\n                waypoints: betweens,\n                optimizeWaypoints: true,\n                travelMode: google.maps.TravelMode.WALKING,\n            }).then(\n                response =\u003e{\n                    this.directionsRenderer.setOptions({\n                        directions:response,\n                        suppressMarkers: true,\n                        \n                    });\n                    \n                    this.route_steps = response.routes[0].legs;\n                    this.response = response;\n                    this.nodes.forEach((el,idx)=\u003e{\n                        if(idx == 0 || idx == nodesLength -1){\n                            this.createMarker(el.location,customIconRed,'headTail',idx,el.description);\n                        }else{\n                            this.createMarker(el.location,customIcon,'between',idx,el.description);\n                        }\n                    })\n                    \n                },  \n                errors =\u003e {\n                    console.log('render map errors',errors)\n                }\n            )\n\n        }\n        if(this.callFormListener == false){\n            // add listening for description\n            $('#map-container').on('submit','.info-description-form',(e)=\u003e{\n                e.preventDefault(); \n                let currentForm = e.currentTarget;\n                let formData = new FormData(currentForm);\n                let nId = parseInt(currentForm.id.slice(9));\n                this.nodes[nId]['description'] = formData.get('description');\n            });\n\n            //add the delete handler\n            $('#map-container').on('submit','.info-delete-form',(e)=\u003e{\n                e.preventDefault(); \n                let currentForm = e.currentTarget;\n                let nId = parseInt(currentForm.id.slice(16))\n                this.currentInfoWindow.close();\n                this.infoNodes.forEach(el =\u003e {\n                    google.maps.event.clearInstanceListeners(el);\n                    el.setMap(null)\n                })\n                this.infoNodes = [];\n                this.nodes.splice(nId,1);\n                this.renderRoute();\n\n            });\n            this.callFormListener = true\n        }\n    }\n```\n\n\u003c/details\u003e\n\n\n### - **Using Google Maps Static API to generate the static thumbnail for the route**\n![alt text][staticimg]\n\u003cdetails\u003e\n\u003csummary\u003eShow the Code\u003c/summary\u003e\n\n``` javascript\ngetPreviewURL(){\n        if (this.response != null){\n        let encodedPath = this.response.routes[0].overview_polyline\n        let url = `https://maps.googleapis.com/maps/api/staticmap?size=200x200\u0026path=weight:3%7Ccolor:red%7Cenc:${encodedPath}\u0026key=${keys.map}\u0026v=beta\u0026callback=initMap`\n        return url;\n        }else{\n            return null;\n        }\n        \n\n    }\n```\n\u003c/details\u003e\n\n### - **Using Amazon Simple Storage Service to store the static thumbnails**\n----\n## **User's Activities**\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eShow the personal summary and all of the activities has created by the user.\u003c/b\u003e\u003c/summary\u003e\n\n![alt text][activitiesimg]\n\u003c/details\u003e\n \n---\n## **Activity Feed**\n\u003cdetails\u003e\n\n\u003csummary\u003e\u003cb\u003eThe Feed showing friend's activities with like and comment fuctions.\u003c/b\u003e\u003c/summary\u003e\n\n![alt text][feedimg]\n\u003c/details\u003e\n\n---\n\n## **Activity Creation**\n\u003cdetails\u003e\n\n\u003csummary\u003e\u003cb\u003eUsers can create the activity from the list of their created routes.\u003c/b\u003e\u003c/summary\u003e\n\n![alt text][addactivityimg]\n\u003c/details\u003e\n\n---\n\n## **Friending** \n\n\u003cdetails\u003e\n\n\u003csummary\u003e\u003cb\u003eUser can search friends, send friends' requests, unfriend.\u003c/b\u003e\u003c/summary\u003e\n\n![alt text][friendsimg]\n\u003c/details\u003e\n\n---\n\n# **Additional Resources**\n\n---\n\n* [**MVP**][mvp]\n* [**Schema**][schema]\n* [**Sample State**][state]\n* [**Frontend Routes**][front-end]\n* [**Backend Routes**][back-end]\n\n\n[homeimg]: https://github.com/phamv21/stvp-running-shape/blob/main/app/assets/screenshots/Home.png?raw=true \"Home\"\n\n[activitiesimg]: https://github.com/phamv21/stvp-running-shape/blob/main/app/assets/screenshots/activities.png?raw=true \"Activities\"\n\n[addactivityimg]: https://github.com/phamv21/stvp-running-shape/blob/main/app/assets/screenshots/add%20activity.png?raw=true \"Add Activity\"\n\n[createrouteimg]: https://github.com/phamv21/stvp-running-shape/blob/main/app/assets/screenshots/create%20route.png?raw=true \"Create New Route\"\n\n[feedimg]: https://github.com/phamv21/stvp-running-shape/blob/main/app/assets/screenshots/feed.png?raw=true \"Feed\"\n\n[friendsimg]: https://github.com/phamv21/stvp-running-shape/blob/main/app/assets/screenshots/friends.png?raw=true \"Friends\"\n\n[routesimg]: https://github.com/phamv21/stvp-running-shape/blob/main/app/assets/screenshots/routes.png?raw=true \"Routes\"\n\n[searchroutesimg]: https://github.com/phamv21/stvp-running-shape/blob/main/app/assets/screenshots/search%20routes.png?raw=true \"Search\"\n\n[showroutesearch]: https://github.com/phamv21/stvp-running-shape/blob/main/app/assets/screenshots/show%20route%20search.png?raw=true \"Search Show\"\n\n[showroute]: https://github.com/phamv21/stvp-running-shape/blob/main/app/assets/screenshots/show%20route.png?raw=true \"Show Route\"\n\n[staticimg]: https://github.com/phamv21/stvp-running-shape/blob/main/app/assets/screenshots/staticmap.png?raw=true \"Static Image\"\n\n[mvp]: https://github.com/phamv21/stvp-running-shape/wiki/MVP\n[schema]: https://github.com/phamv21/stvp-running-shape/wiki/Schema\n[back-end]: https://github.com/phamv21/stvp-running-shape/wiki/Backend-Routes\n[front-end]: https://github.com/phamv21/stvp-running-shape/wiki/Frontend-Routes\n[state]: https://github.com/phamv21/stvp-running-shape/wiki/Sample-State\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphamv21%2Fstvp-running-shape","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphamv21%2Fstvp-running-shape","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphamv21%2Fstvp-running-shape/lists"}