Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/ci-container.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ jobs:
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
REACT_APP_CLIENT_ID=${{ secrets.REACT_APP_CLIENT_ID }}
REACT_APP_PUBLIC_VAPID_KEY=${{ secrets.REACT_APP_PUBLIC_VAPID_KEY }}
REACT_APP_ENCRYPTION_KEY=${{ secrets.REACT_APP_ENCRYPTION_KEY }}
REACT_APP_GOOGLE_MAPS_API_KEY=${{ secrets.REACT_APP_GOOGLE_MAPS_API_KEY }}
REACT_APP_GOOGLE_MAPS_MAP_ID=${{ secrets.REACT_APP_GOOGLE_MAPS_MAP_ID }}
VITE_CLIENT_ID=${{ secrets.VITE_CLIENT_ID }}
VITE_PUBLIC_VAPID_KEY=${{ secrets.VITE_PUBLIC_VAPID_KEY }}
VITE_ENCRYPTION_KEY=${{ secrets.VITE_ENCRYPTION_KEY }}
VITE_GOOGLE_MAPS_API_KEY=${{ secrets.VITE_GOOGLE_MAPS_API_KEY }}
VITE_GOOGLE_MAPS_MAP_ID=${{ secrets.VITE_GOOGLE_MAPS_MAP_ID }}
24 changes: 12 additions & 12 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,18 @@
RUN pnpm install -r --offline --filter frontend...

# Read build-time environment variables
ARG REACT_APP_SERVER_URL
ENV REACT_APP_SERVER_URL=${REACT_APP_SERVER_URL}
ARG REACT_APP_CLIENT_ID
ENV REACT_APP_CLIENT_ID=${REACT_APP_CLIENT_ID}
ARG REACT_APP_PUBLIC_VAPID_KEY
ENV REACT_APP_PUBLIC_VAPID_KEY=${REACT_APP_PUBLIC_VAPID_KEY}
ARG REACT_APP_ENCRYPTION_KEY
ENV REACT_APP_ENCRYPTION_KEY=${REACT_APP_ENCRYPTION_KEY}
ARG REACT_APP_GOOGLE_MAPS_API_KEY
ENV REACT_APP_GOOGLE_MAPS_API_KEY=${REACT_APP_GOOGLE_MAPS_API_KEY}
ARG REACT_APP_GOOGLE_MAPS_MAP_ID
ENV REACT_APP_GOOGLE_MAPS_MAP_ID=${REACT_APP_GOOGLE_MAPS_MAP_ID}
ARG VITE_SERVER_URL
ENV VITE_SERVER_URL=${VITE_SERVER_URL}
ARG VITE_CLIENT_ID
ENV VITE_CLIENT_ID=${VITE_CLIENT_ID}
ARG VITE_PUBLIC_VAPID_KEY
ENV VITE_PUBLIC_VAPID_KEY=${VITE_PUBLIC_VAPID_KEY}
ARG VITE_ENCRYPTION_KEY

Check warning on line 41 in Dockerfile

View workflow job for this annotation

GitHub Actions / build-push

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ARG "VITE_ENCRYPTION_KEY") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/
ENV VITE_ENCRYPTION_KEY=${VITE_ENCRYPTION_KEY}

Check warning on line 42 in Dockerfile

View workflow job for this annotation

GitHub Actions / build-push

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "VITE_ENCRYPTION_KEY") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/
ARG VITE_GOOGLE_MAPS_API_KEY

Check warning on line 43 in Dockerfile

View workflow job for this annotation

GitHub Actions / build-push

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ARG "VITE_GOOGLE_MAPS_API_KEY") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/
ENV VITE_GOOGLE_MAPS_API_KEY=${VITE_GOOGLE_MAPS_API_KEY}

Check warning on line 44 in Dockerfile

View workflow job for this annotation

GitHub Actions / build-push

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "VITE_GOOGLE_MAPS_API_KEY") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/
ARG VITE_GOOGLE_MAPS_MAP_ID
ENV VITE_GOOGLE_MAPS_MAP_ID=${VITE_GOOGLE_MAPS_MAP_ID}

# Build with CI=false to avoid treat warnings as errors
RUN CI=false pnpm run -r --filter frontend... build
Expand Down
12 changes: 6 additions & 6 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ services:
build:
context: .
args:
- REACT_APP_SERVER_URL=${REACT_APP_SERVER_URL}
- REACT_APP_CLIENT_ID=${REACT_APP_CLIENT_ID}
- REACT_APP_PUBLIC_VAPID_KEY=${REACT_APP_PUBLIC_VAPID_KEY}
- REACT_APP_ENCRYPTION_KEY=${REACT_APP_ENCRYPTION_KEY}
- REACT_APP_GOOGLE_MAPS_API_KEY=${REACT_APP_GOOGLE_MAPS_API_KEY}
- REACT_APP_GOOGLE_MAPS_MAP_ID=${REACT_APP_GOOGLE_MAPS_MAP_ID}
- VITE_SERVER_URL=${VITE_SERVER_URL}
- VITE_CLIENT_ID=${VITE_CLIENT_ID}
- VITE_PUBLIC_VAPID_KEY=${VITE_PUBLIC_VAPID_KEY}
- VITE_ENCRYPTION_KEY=${VITE_ENCRYPTION_KEY}
- VITE_GOOGLE_MAPS_API_KEY=${VITE_GOOGLE_MAPS_API_KEY}
- VITE_GOOGLE_MAPS_MAP_ID=${VITE_GOOGLE_MAPS_MAP_ID}
environment:
- NODE_ENV=production
- DEBUG=True
Expand Down
7 changes: 4 additions & 3 deletions frontend/public/index.html → frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
Expand All @@ -14,9 +14,10 @@
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="apple-touch-icon" href="/logo192.png" />
<link rel="manifest" href="/manifest.json" />
<script src="https://accounts.google.com/gsi/client" async defer></script>
<script type="module" src="/src/index.tsx"></script>
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Expand Down
20 changes: 8 additions & 12 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@
"@carriage-web/shared": "workspace:*"
},
"scripts": {
"dev": "react-scripts start",
"start": "react-scripts start",
"build": "react-scripts build",
"type-check": "tsc --project tsconfig.json --pretty --noEmit",
"test": "react-scripts test",
"eject": "react-scripts eject"
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"type-check": "tsc --project tsconfig.json --pretty --noEmit"
},
"eslintConfig": {
"extends": [
Expand Down Expand Up @@ -43,27 +41,24 @@
"@react-aria/utils": "^3.25.2",
"@types/crypto-js": "^4.2.2",
"@types/google.maps": "^3.58.1",
"@types/jest": "^29.5.13",
"@types/node": "^22.5.5",
"@types/react": "^18.3.8",
"@types/react-big-calendar": "^1.8.11",
"@types/react-csv": "^1.1.10",
"@types/react-datepicker": "^7.0.0",
"@types/react-dom": "^18.3.0",
"@types/react-router-dom": "^5.3.3",
"@types/react-router-hash-link": "^2.4.9",
"@types/uuid": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"@vis.gl/react-google-maps": "^1.4.0",
"@vitejs/plugin-react": "^5.1.2",
"addresser": "^1.1.20",
"autoprefixer": "^10.4.21",
"axios": "^1.12.0",
"classnames": "^2.5.1",
"crypto-js": "^4.2.0",
"date-fns": "^2.30.0",
"dayjs": "^1.11.19",
"env-cmd": "^10.1.0",
"eslint": "^8.56.0",
"eslint-config-prettier": "^8.10.0",
"eslint-config-react-app": "^7.0.1",
Expand All @@ -82,11 +77,12 @@
"react-hook-form": "^7.53.0",
"react-router-dom": "^6.26.2",
"react-router-hash-link": "^2.4.3",
"react-scripts": "^5.0.1",
"react-select": "^5.8.1",
"reactjs-popup": "^2.0.6",
"tailwindcss": "^3.4.17",
"typescript": "catalog:",
"uuid": "^13.0.0"
"uuid": "^13.0.0",
"vite": "^7.2.7",
"vite-tsconfig-paths": "^5.1.4"
}
}
12 changes: 7 additions & 5 deletions frontend/src/components/AuthManager/AuthManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import CryptoJS from 'crypto-js';
import axios, { setAuthToken } from '../../util/axios';
import UnregisteredUserPage from '../Onboarding/UnregisteredUserPage';

const secretKey = `${process.env.REACT_APP_ENCRYPTION_KEY!}`;
const secretKey = `${import.meta.env.VITE_ENCRYPTION_KEY!}`;

const encrypt = (data: string) => {
const encrypted = CryptoJS.AES.encrypt(
Expand Down Expand Up @@ -109,7 +109,7 @@ const AuthManager = () => {
const handleSSOCallback = async (event?: React.FormEvent<HTMLFormElement>) => {
try {
const response = await fetch(
`${process.env.REACT_APP_SERVER_URL}/api/sso/profile`,
`${import.meta.env.VITE_SERVER_URL}/api/sso/profile`,
{
credentials: 'include', // Send session cookie
}
Expand Down Expand Up @@ -145,7 +145,7 @@ const AuthManager = () => {
if (errorParam) {
// Handle user_not_found specially - fetch unregistered user info
if (errorParam === 'user_not_found') {
fetch(`${process.env.REACT_APP_SERVER_URL}/api/sso/unregistered-user`, {
fetch(`${import.meta.env.VITE_SERVER_URL}/api/sso/unregistered-user`, {
credentials: 'include', // Send session cookie
})
.then((res) => res.json())
Expand Down Expand Up @@ -240,7 +240,9 @@ const AuthManager = () => {
userType = 'Driver';
}

const ssoUrl = `${process.env.REACT_APP_SERVER_URL}/api/sso/login?redirect_uri=${redirectUri}&userType=${userType}`;
const ssoUrl = `${
import.meta.env.VITE_SERVER_URL
}/api/sso/login?redirect_uri=${redirectUri}&userType=${userType}`;
window.location.href = ssoUrl;
}

Expand All @@ -252,7 +254,7 @@ const AuthManager = () => {
setAuthToken('');
setSignedIn(false);
setRefreshUser(() => () => {});
window.location.href = `${process.env.REACT_APP_SERVER_URL}/api/sso/logout`;
window.location.href = `${process.env.VITE_SERVER_URL}/api/sso/logout`;
}

function createRefresh(userId: string, userType: string, token: string) {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/AuthManager/subscribeUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const urlBase64ToUint8Array = (base64String: string) => {
};

const convertedVapidKey = urlBase64ToUint8Array(
process.env.REACT_APP_PUBLIC_VAPID_KEY!
import.meta.env.VITE_PUBLIC_VAPID_KEY!
);

const sendSubscription = (
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Locations/LocationFormModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ export const LocationFormModal: React.FC<Props> = ({

<DialogContent>
<APIProvider
apiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY as string}
apiKey={import.meta.env.VITE_GOOGLE_MAPS_API_KEY as string}
libraries={['places']}
>
<div className={styles.formGrid}>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/Locations/LocationMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const MapContent = ({
<Map
defaultZoom={13}
defaultCenter={{ lat: 42.4534531, lng: -76.4760776 }}
mapId={process.env.REACT_APP_GOOGLE_MAPS_MAP_ID}
mapId={import.meta.env.VITE_GOOGLE_MAPS_MAP_ID}
className={styles.mapPlaceholder}
gestureHandling="greedy"
disableDefaultUI={false}
Expand Down Expand Up @@ -145,7 +145,7 @@ const MapContent = ({
/* -------------------------------------------------------------------------- */
export const LocationMap: React.FC<LocationMapProps> = (props) => (
<APIProvider
apiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY as string}
apiKey={import.meta.env.VITE_GOOGLE_MAPS_API_KEY as string}
libraries={['places']}
>
<MapContent {...props} />
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Locations/LocationMapPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const LocationPickerMap: React.FC<LocationPickerMapProps> = ({
<Map
defaultZoom={initialPosition ? 15 : 13}
defaultCenter={defaultCenter}
mapId={process.env.REACT_APP_GOOGLE_MAPS_MAP_ID}
mapId={import.meta.env.VITE_GOOGLE_MAPS_MAP_ID}
className={styles.mapContainer}
gestureHandling="greedy"
disableDefaultUI={false}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/ResponsiveRideCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ const ResponsiveRideCard: FC<ResponsiveRideCardProps> = ({
defaultZoom={13}
gestureHandling="greedy"
disableDefaultUI
mapId={process.env.REACT_APP_GOOGLE_MAPS_MAP_ID}
mapId={process.env.VITE_GOOGLE_MAPS_MAP_ID}
>
<AdvancedMarker
position={{
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/RideDetails/RideLocations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ const RideMap: React.FC<RideMapProps> = ({
<Map
defaultZoom={12}
defaultCenter={getMapCenter()}
mapId={process.env.REACT_APP_GOOGLE_MAPS_MAP_ID}
mapId={import.meta.env.VITE_GOOGLE_MAPS_MAP_ID}
gestureHandling="greedy"
disableDefaultUI={false}
onClick={handleMapClick}
Expand Down Expand Up @@ -516,7 +516,7 @@ const RideMap: React.FC<RideMapProps> = ({

const RideMapWithProvider: React.FC<RideMapProps> = (props) => (
<APIProvider
apiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY as string}
apiKey={import.meta.env.VITE_GOOGLE_MAPS_API_KEY as string}
libraries={['places']}
>
<RideMap {...props} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ const RequestRideDialog: React.FC<RequestRideDialogProps> = ({
<DialogTitle>{!ride ? 'Request a Ride' : 'Edit Ride'}</DialogTitle>
<DialogContent>
<APIProvider
apiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY as string}
apiKey={import.meta.env.VITE_GOOGLE_MAPS_API_KEY as string}
libraries={['places']}
>
<div className={styles.formContainer}>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/RiderComponents/RequestRideMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ const RequestRideMap: React.FC<RequestRideMapProps> = ({
<Map
defaultZoom={13}
defaultCenter={getMapCenter()}
mapId={process.env.REACT_APP_GOOGLE_MAPS_MAP_ID}
mapId={import.meta.env.VITE_GOOGLE_MAPS_MAP_ID}
gestureHandling={'greedy'}
disableDefaultUI={false}
className={styles.mapContainer}
Expand Down Expand Up @@ -280,7 +280,7 @@ const RequestRideMap: React.FC<RequestRideMapProps> = ({
/* -------------------------------------------------------------------------- */
const RequestRideMapWithProvider: React.FC<RequestRideMapProps> = (props) => (
<APIProvider
apiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY as string}
apiKey={import.meta.env.VITE_GOOGLE_MAPS_API_KEY as string}
libraries={['places']}
>
<RequestRideMap {...props} />
Expand Down
27 changes: 21 additions & 6 deletions frontend/src/context/RidesContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -201,24 +201,39 @@ export const RidesProvider = ({ children }: RidesProviderProps) => {
setError(null);
}, []);

// Optimistic ride operations
const updateRideStatus = useCallback(
async (rideId: string, status: Status) => {
const originalRide = getRideById(rideId);
let originalRide = getRideById(rideId);

// If ride not found in current context, fetch it from the server
if (!originalRide) {
try {
const response = await axios.get(`/api/rides/${rideId}`);
originalRide = response.data.data;
} catch (error) {
console.error('Failed to fetch ride from server:', error);
throw new Error('Ride not found');
}
}

if (!originalRide) {
throw new Error('Ride not found');
}

try {
// Optimistic update
updateRideInLists(rideId, (ride) => ({ ...ride, status }));
// Only do optimistic update if ride is in current context
const rideInContext = getRideById(rideId);
if (rideInContext) {
updateRideInLists(rideId, (ride) => ({ ...ride, status }));
}

// Make API call
await axios.put(`/api/rides/${rideId}`, { status });
} catch (error) {
// Rollback on error
console.error('Failed to update ride status:', error);
if (originalRide) {
const rideInContext = getRideById(rideId);
if (originalRide && rideInContext) {
updateRideInLists(rideId, () => originalRide);
}
setError(error as Error);
Expand All @@ -227,7 +242,7 @@ export const RidesProvider = ({ children }: RidesProviderProps) => {
},
[getRideById]
);

const updateRideScheduling = useCallback(
async (
rideId: string,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/hooks/useClientId.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
const useClientId = () => process.env.REACT_APP_CLIENT_ID || '';
const useClientId = () => import.meta.env.VITE_CLIENT_ID || '';

export default useClientId;
2 changes: 1 addition & 1 deletion frontend/src/pages/Driver/Rides.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ const Rides = () => {
}, []);

return (
<APIProvider apiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY as string}>
<APIProvider apiKey={import.meta.env.VITE_GOOGLE_MAPS_API_KEY}>
<main id="main">
<Box sx={{ p: 3 }}>
<Box
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/Rider/Schedule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ const Schedule: React.FC = () => {

return (
<APIProvider
apiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY as string}
apiKey={import.meta.env.VITE_GOOGLE_MAPS_API_KEY as string}
libraries={['places']}
>
<main id="main" className={styles.schedulePage}>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/serviceWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const swFileName = 'notification-sw.js';
export function register(config?: Config) {
if ('serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
const publicUrl = new URL(import.meta.env.BASE_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
Expand All @@ -42,7 +42,7 @@ export function register(config?: Config) {
}

window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/${swFileName}`;
const swUrl = `${import.meta.env.BASE_URL}/${swFileName}`;

if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/util/axios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import axios from 'axios';
import { decrypt } from 'components/AuthManager/AuthManager';

const instance = axios.create({
baseURL: process.env.REACT_APP_SERVER_URL,
baseURL: import.meta.env.VITE_SERVER_URL,
});

export const setAuthToken = (token: string) => {
Expand Down
Loading
Loading