Repository: MiniCapstone-Project/SOEN390
Pull request #111: Shuttle service functionality added
Author: login: Johnnyghub
Labels: name: user story
description: New feature or request
Created: 2025-02-28T21:07:11Z | Merged: 2025-03-02T02:59:59Z

## Description
Added a new button for shuttle service that routes from Hall Building to Loyola Campus, and displays the time until the next shuttle bus conditionally when only that button is pressed. The time to next shuttle is based on the current schedule on the Concordia website. 

### Related Issue
User Story 2.6

### Changes Made
Added Shuttle Button
Added Shuttle Countdown Message
Added Shuttle Routing
Added Shuttle Starting and Destination Point Coordinates



Diff:
diff --git a/assets/schedule.json b/assets/schedule.json
new file mode 100644
index 00000000..2949ebd0
--- /dev/null
+++ b/assets/schedule.json
@@ -0,0 +1,8 @@
+{
+  "times": [
+    "09:15", "09:45", "10:15", "10:45", "11:45",
+    "12:15", "12:45", "13:15", "13:45", "14:15",
+    "14:45", "15:15", "15:45", "16:15", "17:15",
+    "17:45", "18:15", "18:45"
+  ]
+}
\ No newline at end of file
diff --git a/screens/DirectionsScreen.tsx b/screens/DirectionsScreen.tsx
index 0babf941..899d799a 100644
--- a/screens/DirectionsScreen.tsx
+++ b/screens/DirectionsScreen.tsx
@@ -11,6 +11,7 @@ import { SGW_CAMPUS } from "./MapExplorerScreen";
 import "react-native-get-random-values";
 import { useRoute } from "@react-navigation/native";
 import { retrieveRoutes } from "@/services/DirectionService.ts";
+import { findNextShuttle } from "@/services/ShuttleService.ts"
 
 const googleMapsKey = GOOGLE_MAPS_API_KEY;
 const EDGE_PADDING = { top: 70, right: 70, bottom: 70, left: 70 };
@@ -26,6 +27,9 @@ export default function DirectionsScreen() {
   const destinationObject = route.params?.destination; // pass destination to second screen
   const [directionsRoute, setDirectionsRoute] = useState<LatLng | null>(null);  // create directions route state
   const [transportMode, setTransportMode] = useState<"DRIVING"|"WALKING"|"TRANSIT">("DRIVING");
+  const HALL_BUILDING: LatLng = { latitude: 45.4973223, longitude: -73.5790288};  // start point of shuttle routing
+  const LOYOLA_CAMPUS: LatLng = { latitude: 45.4581244, longitude: -73.6391259};  // end point of shuttle routing
+  const [showShuttleTime, setShowShuttleTime] = useState(false); // this tracks the button press for shuttle and shows time till next shuttle
 
   useEffect(() => {
     const loadSavedLocations = async () => {
@@ -109,6 +113,14 @@ export default function DirectionsScreen() {
     }
   };
 
+  const setShuttleRoute = async() => {
+    setOrigin(HALL_BUILDING);
+    setDestination(LOYOLA_CAMPUS);
+    setTransportMode("DRIVING");  // buses drive, so driving mode
+
+    await traceRoute(); // call trace route to trace shuttle bus service route
+  }
+
   useEffect (() => {
     traceRoute();
   }, [transportMode]);
@@ -179,16 +191,20 @@ export default function DirectionsScreen() {
           styles={{ container: { flex: 0, marginBottom: 10 }, textInput: DirectionsScreenStyles.input }}
         />
         <Button title="Route" color="#733038" onPress={traceRoute} />
-        <Button title="Walking" color="#733038" marginBottom="20px" onPress={setWalking} />
-        <Button title="Driving" color="#733038" marginBottom="20px" onPress={setDriving} />
-        <Button title="Transit" color="#733038" marginBottom="20px" onPress={setTransit} />
+        /* All button on presses change state of shuttle service to true or false */
+        <Button title="Walking" color="#733038" marginBottom="20px" onPress={() => {setWalking(); setShowShuttleTime(false);}} />
+        <Button title="Driving" color="#733038" marginBottom="20px" onPress={() => {setDriving(); setShowShuttleTime(false);}} />
+        <Button title="Transit" color="#733038" marginBottom="20px" onPress={() => {setTransit(); setShowShuttleTime(false);}} />
+        <Button title="Shuttle" color="#733038" marginBottom="20px" onPress={() => {setShuttleRoute(); setShowShuttleTime(true);}} />
         {distance > 0 && duration > 0 && (
           <View style={DirectionsScreenStyles.stats}>
             <Text>Distance: {distance.toFixed(2)} km</Text>
             <Text>Duration: {Math.ceil(duration)} min</Text>
+            /* Only show this conditionally if the shuttle button is pressed */
+            {showShuttleTime && <Text>{findNextShuttle()}</Text>}
           </View>
         )}
       </View>
     </View>
   );
-}
+}
\ No newline at end of file
diff --git a/services/ShuttleService.ts b/services/ShuttleService.ts
new file mode 100644
index 00000000..08b0229e
--- /dev/null
+++ b/services/ShuttleService.ts
@@ -0,0 +1,23 @@
+import schedule from "@/assets/schedule.json";
+
+export const findNextShuttle = (): string => {
+    const now = new Date();  // get current date time
+    const currentTime = now.getHours() * 60 + now.getMinutes(); // get time in minutes since day began
+
+    const shuttleSchedule = schedule.times;  // get shuttle times from schedule json
+
+    for (let i = 0; i < Object.keys(shuttleSchedule).length; i++) {
+        const timeArray = shuttleSchedule[i].split(":");  // split time string into hours and minutes
+        const hours = Number(timeArray[0]);
+        const minutes = Number(timeArray[1]);  // get hours and minutes in a number format
+        const time = hours * 60 + minutes;  // get the time in minutes for comparator
+
+        if (time > currentTime) {  // this means that the time coming up is the closest to our current time
+            // get minutes till shuttle by subtracting, turn to String and create message
+            const returnString = "Time Until Next Shuttle: " + String(time - currentTime) + " min";
+            return returnString;
+        }
+    }
+    const returnString = "No More Shuttles Today";
+    return returnString;  // fall back if no matches were found
+}


Reviews:
user: login: adssib
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-02-28T21:13:58Z
user: login: Johnnyghub
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-01T22:07:23Z
user: login: elion-abdyli
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-02T02:29:37Z
user: login: adssib
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-02T02:58:34Z
user: login: elion-abdyli
body: Ready for merge, implementation is cohesive, issue is considered resolved, new requirement is better recorded as new enhancment issue under new branch. Good to go !
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-02T02:58:39Z

Review comments:
diff_hunk: @@ -109,6 +113,36 @@ export default function DirectionsScreen() {
     }
   };
 
+  const setShuttleRoute = async() => {
+    setOrigin(HALL_BUILDING);
+    setDestination(LOYOLA_CAMPUS);
+    setTransportMode("DRIVING");  // buses drive, so driving mode
+
+    await traceRoute(); // call trace route to trace shuttle bus service route
+  }
+
+  const findNextShuttleCountdown = (): string => {
path: screens/DirectionsScreen.tsx
user: login: adssib
body: Here I consider you create a new file in the services directory since this function can be used other places and we dont wont one file that contain everything 

it is a better approach to move the function to a new file and please call it `finNextShuttle`
created_at: 2025-02-28T21:13:25Z
author_association: COLLABORATOR
diff_hunk: @@ -109,6 +113,36 @@ export default function DirectionsScreen() {
     }
   };
 
+  const setShuttleRoute = async() => {
+    setOrigin(HALL_BUILDING);
+    setDestination(LOYOLA_CAMPUS);
+    setTransportMode("DRIVING");  // buses drive, so driving mode
+
+    await traceRoute(); // call trace route to trace shuttle bus service route
+  }
+
+  const findNextShuttleCountdown = (): string => {
path: screens/DirectionsScreen.tsx
user: login: Johnnyghub
body: Updated the branch with the requested changes, please review again. Thank you.
created_at: 2025-03-01T22:07:23Z
author_association: COLLABORATOR
diff_hunk: @@ -26,6 +27,8 @@ export default function DirectionsScreen() {
   const destinationObject = route.params?.destination; // pass destination to second screen
   const [directionsRoute, setDirectionsRoute] = useState<LatLng | null>(null);  // create directions route state
   const [transportMode, setTransportMode] = useState<"DRIVING"|"WALKING"|"TRANSIT">("DRIVING");
+  const HALL_BUILDING: LatLng = { latitude: 45.4973223, longitude: -73.5790288};  // start point of shuttle routing
path: screens/DirectionsScreen.tsx
user: login: elion-abdyli
body: redundant data, will be imported from a gis file by elion. ok.
created_at: 2025-03-02T02:29:37Z
author_association: COLLABORATOR

PR comments:
user: login: codecov-commenter
created_at: 2025-02-28T21:08:58Z
body: ## [Codecov](https://app.codecov.io/gh/MiniCapstone-Project/SOEN390/pull/111?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MiniCapstone-Project) Report
Attention: Patch coverage is `0%` with `27 lines` in your changes missing coverage. Please review.
| [Files with missing lines](https://app.codecov.io/gh/MiniCapstone-Project/SOEN390/pull/111?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MiniCapstone-Project) | Patch % | Lines |
|---|---|---|
| [services/ShuttleService.ts](https://app.codecov.io/gh/MiniCapstone-Project/SOEN390/pull/111?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MiniCapstone-Project#diff-c2VydmljZXMvU2h1dHRsZVNlcnZpY2UudHM=) | 0.00% | [14 Missing :warning: ](https://app.codecov.io/gh/MiniCapstone-Project/SOEN390/pull/111?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MiniCapstone-Project) |
| [screens/DirectionsScreen.tsx](https://app.codecov.io/gh/MiniCapstone-Project/SOEN390/pull/111?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MiniCapstone-Project#diff-c2NyZWVucy9EaXJlY3Rpb25zU2NyZWVuLnRzeA==) | 0.00% | [13 Missing :warning: ](https://app.codecov.io/gh/MiniCapstone-Project/SOEN390/pull/111?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=MiniCapstone-Project) |

:loudspeaker: Thoughts on this report? [Let us know!](https://github.com/codecov/feedback/issues/255)
author_association: NONE
user: login: sonarqubecloud[bot]
created_at: 2025-03-01T22:10:32Z
body: ## [![Quality Gate Failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-failed-20px.png 'Quality Gate Failed')](https://sonarcloud.io/dashboard?id=MiniCapstone-Project_SOEN390&pullRequest=111) **Quality Gate failed**  
Failed conditions  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/failed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=MiniCapstone-Project_SOEN390&pullRequest=111&metric=new_coverage&view=list) (required ≥ 80%)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/failed-16px.png '') [C Reliability Rating on New Code](https://sonarcloud.io/dashboard?id=MiniCapstone-Project_SOEN390&pullRequest=111) (required ≥ A)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=MiniCapstone-Project_SOEN390&pullRequest=111)

##   
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/light_bulb-16px.png '') Catch issues before they fail your Quality Gate with our IDE extension ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/sonarlint-16px.png '') [SonarQube for IDE](https://www.sonarsource.com/products/sonarlint/features/connected-mode/?referrer=pull-request)


author_association: NONE
user: login: elion-abdyli
created_at: 2025-03-02T02:35:31Z
body: Is this state self-sufficient ? Does it need modifications that are important to its atomicity ?
author_association: COLLABORATOR
user: login: Johnnyghub
created_at: 2025-03-02T02:40:29Z
body: > Is this state self-sufficient ? Does it need modifications that are important to its atomicity ?

We need to implement conditional shuttle routing based on the updated requirements. 
![image](https://github.com/user-attachments/assets/159af1c6-dba0-4342-a731-0305067b53d5)

author_association: COLLABORATOR
user: login: elion-abdyli
created_at: 2025-03-02T02:42:13Z
body: So does the code work well enough to merge now and implement that feature separately ?
author_association: COLLABORATOR
user: login: Johnnyghub
created_at: 2025-03-02T02:43:35Z
body: > So does the code work well enough to merge now and implement that feature separately ?

Yes, as it stands now the code is functional and does not invoke any issues in the overall project, there should be no issues if a merge takes place other than the changes that need to be applied. 
author_association: COLLABORATOR
user: login: adssib
created_at: 2025-03-02T02:45:01Z
body: Hello @elion-abdyli  when the constant file is ready we will update it but for now will keep it 

the pull request can be merged 

I dont mind both options 

either jhonny wanna finish the issue then we merge since the pull  request will contain all the commits related to one issue (this option I like it more) 

or merge it now and create a new one which is messy 

@Johnnyghub  please let me know which one you like better 
author_association: COLLABORATOR
user: login: Johnnyghub
created_at: 2025-03-02T02:50:01Z
body: For the sake of changes propagating and making it easier for everyone else to code I believe it is better to merge now after discussion with team members. It should pose no issue to merge now and create a new branch to implement the requirements changes since they will majorly re-define the current function of the shuttle service and can warrant their own branch. 

If you are opposed to this however I also have no problems not merging, this majorly affects the other developers in the team and does not personally affect me to any great extent, simply just means I have to create a new branch and make updates there instead.

Please let me know what your thoughts are on the matter, the decision should fall onto those who are affected more, which is the other developers on the team, and not me.

Thanks
author_association: COLLABORATOR
user: login: elion-abdyli
created_at: 2025-03-02T02:51:36Z
body: The gis files gis files will be implemented after the refactoring branch is merged, in an upcoming branch 
author_association: COLLABORATOR
user: login: adssib
created_at: 2025-03-02T02:52:55Z
body: If you're concerned about bugs arising from merges, don't worry—we have a dedicated branch for stable versions of the app. The main branch may be a work in progress, but only bug-free versions make it to production.
author_association: COLLABORATOR
user: login: Johnnyghub
created_at: 2025-03-02T02:57:39Z
body: If there is a bug-free production version, I think merging now so everyone can work on up-to-date code is agreeable for the other developers. If anyone has any explicit objections to merging now, we can hold off until I update the code, otherwise I think we should just push the merge through and take a look at the other pull requests, so we can work on the new requirement changes in isolation as they are quite hefty.
author_association: COLLABORATOR
user: login: adssib
created_at: 2025-03-02T03:01:58Z
body: @elion-abdyli  sonar qube was failing 

Jhonny didnt write unit tests so I was talking to him that he forgot to do then and was communicating with him but you merged it  before finalizing a decision 
author_association: COLLABORATOR