Skip to content
Closed
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
2 changes: 1 addition & 1 deletion .github/workflows/bundle-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
fetch-depth: 0

Expand Down
7 changes: 2 additions & 5 deletions .github/workflows/preview.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
name: Create EAS Preview

on:
pull_request_target:
pull_request:

permissions:
contents: read
pull-requests: write

jobs:
preview:
if: contains(github.event.pull_request.labels.*.name, 'run-preview')
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.head.sha }}
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 🏗 Setup repo
uses: actions/checkout@v5
uses: actions/checkout@v4

- name: 🏗 Setup Node
uses: actions/setup-node@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/rn-bundle-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
fetch-depth: 0

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for better diff analysis

Expand Down
557 changes: 557 additions & 0 deletions Design Revamp.html

Large diffs are not rendered by default.

49 changes: 49 additions & 0 deletions backend/app/expenses/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,3 +460,52 @@
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
raise HTTPException(status_code=500, detail="Failed to fetch analytics")


# Debug endpoint (remove in production)
@router.get("/expenses/{expense_id}/debug")
async def debug_expense(
group_id: str,
expense_id: str,
current_user: Dict[str, Any] = Depends(get_current_user),
):
"""Debug endpoint to check expense details and user permissions"""
try:
from app.database import mongodb
from bson import ObjectId

Check warning on line 475 in backend/app/expenses/routes.py

View check run for this annotation

Codecov / codecov/patch

backend/app/expenses/routes.py#L473-L475

Added lines #L473 - L475 were not covered by tests

# Check if expense exists
expense = await mongodb.database.expenses.find_one(

Check warning on line 478 in backend/app/expenses/routes.py

View check run for this annotation

Codecov / codecov/patch

backend/app/expenses/routes.py#L478

Added line #L478 was not covered by tests
{"_id": ObjectId(expense_id)}
)
if not expense:
return {"error": "Expense not found", "expense_id": expense_id}

Check warning on line 482 in backend/app/expenses/routes.py

View check run for this annotation

Codecov / codecov/patch

backend/app/expenses/routes.py#L482

Added line #L482 was not covered by tests

# Check group membership
group = await mongodb.database.groups.find_one(

Check warning on line 485 in backend/app/expenses/routes.py

View check run for this annotation

Codecov / codecov/patch

backend/app/expenses/routes.py#L485

Added line #L485 was not covered by tests
{"_id": ObjectId(group_id), "members.userId": current_user["_id"]}
)

# Check if user created the expense
user_created = expense.get("createdBy") == current_user["_id"]

Check warning on line 490 in backend/app/expenses/routes.py

View check run for this annotation

Codecov / codecov/patch

backend/app/expenses/routes.py#L490

Added line #L490 was not covered by tests

return {

Check warning on line 492 in backend/app/expenses/routes.py

View check run for this annotation

Codecov / codecov/patch

backend/app/expenses/routes.py#L492

Added line #L492 was not covered by tests
"expense_exists": True,
"expense_id": expense_id,
"group_id": group_id,
"user_id": current_user["_id"],
"expense_created_by": expense.get("createdBy"),
"user_created_expense": user_created,
"user_in_group": group is not None,
"expense_group_id": expense.get("groupId"),
"group_id_match": expense.get("groupId") == group_id,
"expense_data": {
"description": expense.get("description"),
"amount": expense.get("amount"),
"splits_count": len(expense.get("splits", [])),
"created_at": expense.get("createdAt"),
"updated_at": expense.get("updatedAt"),
},
}
except Exception as e:
return {"error": str(e), "type": type(e).__name__}

Check warning on line 511 in backend/app/expenses/routes.py

View check run for this annotation

Codecov / codecov/patch

backend/app/expenses/routes.py#L510-L511

Added lines #L510 - L511 were not covered by tests

Check warning

Code scanning / CodeQL

Information exposure through an exception Medium

Stack trace information
flows to this location and may be exposed to an external user.

Copilot Autofix

AI 5 months ago

To fix the problem, we should avoid returning exception details (message and type) directly to the user. Instead, return a generic error message such as "An internal error has occurred" or "Failed to fetch expense details". For debugging purposes, the full exception details can be logged server-side using the existing logger (imported as logger from app.config). This ensures developers can still access error information without exposing it to users. The change should be made in the exception handler of the debug_expense function in backend/app/expenses/routes.py, specifically replacing the return statement on line 511. Additionally, add a call to logger.error() to log the exception details.


Suggested changeset 1
backend/app/expenses/routes.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/backend/app/expenses/routes.py b/backend/app/expenses/routes.py
--- a/backend/app/expenses/routes.py
+++ b/backend/app/expenses/routes.py
@@ -508,4 +508,5 @@
             },
         }
     except Exception as e:
-        return {"error": str(e), "type": type(e).__name__}
+        logger.error(f"Error in debug_expense: {e}", exc_info=True)
+        return {"error": "Failed to fetch expense details"}
EOF
@@ -508,4 +508,5 @@
},
}
except Exception as e:
return {"error": str(e), "type": type(e).__name__}
logger.error(f"Error in debug_expense: {e}", exc_info=True)
return {"error": "Failed to fetch expense details"}
Copilot is powered by AI and may make mistakes. Always verify output.
23 changes: 23 additions & 0 deletions frontend/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,31 @@ import React from 'react';
import AppNavigator from './navigation/AppNavigator';
import { PaperProvider } from 'react-native-paper';
import { AuthProvider } from './context/AuthContext';
import { useFonts } from 'expo-font';
import { ActivityIndicator, View } from 'react-native';

export default function App() {
const [fontsLoaded] = useFonts({
'Inter-Black': require('./assets/fonts/Inter-Black.ttf'),
'Inter-BlackItalic': require('./assets/fonts/Inter-BlackItalic.ttf'),
'Inter-Bold': require('./assets/fonts/Inter-Bold.ttf'),
'Inter-BoldItalic': require('./assets/fonts/Inter-BoldItalic.ttf'),
'Inter-Italic': require('./assets/fonts/Inter-Italic.ttf'),
'Inter-Medium': require('./assets/fonts/Inter-Medium.ttf'),
'Inter-MediumItalic': require('./assets/fonts/Inter-MediumItalic.ttf'),
'Inter-Regular': require('./assets/fonts/Inter-Regular.ttf'),
'Inter-SemiBold': require('./assets/fonts/Inter-SemiBold.ttf'),
'Inter-SemiBoldItalic': require('./assets/fonts/Inter-SemiBoldItalic.ttf'),
});

if (!fontsLoaded) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<ActivityIndicator size="large" />
</View>
);
}

return (
<AuthProvider>
<PaperProvider>
Expand Down
Binary file modified frontend/assets/adaptive-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed frontend/assets/adaptive-icon/background.png
Binary file not shown.
Binary file removed frontend/assets/adaptive-icon/foreground.png
Binary file not shown.
Binary file modified frontend/assets/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/assets/fonts/Inter-Black.ttf
Binary file not shown.
Binary file added frontend/assets/fonts/Inter-BlackItalic.ttf
Binary file not shown.
Binary file added frontend/assets/fonts/Inter-Bold.ttf
Binary file not shown.
Binary file added frontend/assets/fonts/Inter-BoldItalic.ttf
Binary file not shown.
Binary file added frontend/assets/fonts/Inter-Italic.ttf
Binary file not shown.
Binary file added frontend/assets/fonts/Inter-Medium.ttf
Binary file not shown.
Binary file added frontend/assets/fonts/Inter-MediumItalic.ttf
Binary file not shown.
Binary file added frontend/assets/fonts/Inter-Regular.ttf
Binary file not shown.
Binary file added frontend/assets/fonts/Inter-SemiBold.ttf
Binary file not shown.
Binary file not shown.
Binary file modified frontend/assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed frontend/assets/splash.png
Binary file not shown.
40 changes: 40 additions & 0 deletions frontend/components/SkeletonLoader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React, { useEffect } from "react";
import { View, StyleSheet } from "react-native";
import Animated, {
useSharedValue,
useAnimatedStyle,
withRepeat,
withTiming,
interpolateColor,
} from "react-native-reanimated";
import { colors } from "../styles/theme";

const SkeletonLoader = ({ style }) => {
const progress = useSharedValue(0);

useEffect(() => {
progress.value = withRepeat(withTiming(1, { duration: 1000 }), -1, true);
}, []);

const animatedStyle = useAnimatedStyle(() => {
const backgroundColor = interpolateColor(
progress.value,
[0, 1],
[colors.secondary, colors.white]
);
return {
backgroundColor,
};
});

return <Animated.View style={[styles.skeleton, animatedStyle, style]} />;
};

const styles = StyleSheet.create({
skeleton: {
backgroundColor: colors.secondary,
borderRadius: 4,
},
});

export default SkeletonLoader;
123 changes: 123 additions & 0 deletions frontend/components/v2/Button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import React from 'react';
import { TouchableOpacity, Text, StyleSheet, ActivityIndicator, Pressable } from 'react-native';
import Animated, { useSharedValue, useAnimatedStyle, withTiming } from 'react-native-reanimated';
import { colors, spacing, typography } from '../../styles/theme';

const AnimatedPressable = Animated.createAnimatedComponent(Pressable);

const Button = ({
title,
onPress,
mode = 'primary', // 'primary', 'secondary', 'tertiary'
disabled = false,
loading = false,
style,
textStyle,
...props
}) => {
const scale = useSharedValue(1);

const animatedStyle = useAnimatedStyle(() => {
return {
transform: [{ scale: scale.value }],
};
});

const handlePressIn = () => {
scale.value = withTiming(0.98, { duration: 100 });
};

const handlePressOut = () => {
scale.value = withTiming(1, { duration: 100 });
};

const getButtonStyles = () => {
switch (mode) {
case 'secondary':
return styles.secondaryButton;
case 'tertiary':
return styles.tertiaryButton;
default:
return styles.primaryButton;
}
};

const getTextStyles = () => {
switch (mode) {
case 'secondary':
return styles.secondaryText;
case 'tertiary':
return styles.tertiaryText;
default:
return styles.primaryText;
}
};

const buttonStyle = [
styles.button,
getButtonStyles(),
disabled && styles.disabled,
style,
];

const content = loading ? (
<ActivityIndicator color={mode === 'primary' ? colors.neutral.white : colors.brandAccent} />
) : (
<Text style={[styles.text, getTextStyles(), textStyle]}>{title}</Text>
);

return (
<AnimatedPressable
onPress={onPress}
onPressIn={handlePressIn}
onPressOut={handlePressOut}
disabled={disabled || loading}
style={[buttonStyle, animatedStyle]}
{...props}
>
{content}
</AnimatedPressable>
);
};

const styles = StyleSheet.create({
button: {
paddingVertical: spacing.md,
paddingHorizontal: spacing.lg,
borderRadius: 12,
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'row',
},
text: {
...typography.bodyBold,
textAlign: 'center',
},
primaryButton: {
backgroundColor: colors.brandAccent,
},
primaryText: {
color: colors.neutral.white,
},
secondaryButton: {
backgroundColor: 'transparent',
borderWidth: 1.5,
borderColor: colors.brandAccent,
},
secondaryText: {
color: colors.brandAccent,
},
tertiaryButton: {
backgroundColor: 'transparent',
},
tertiaryText: {
color: colors.brandAccent,
},
disabled: {
backgroundColor: '#9CA3AF',
opacity: 0.7,
borderColor: 'transparent',
},
});

export default Button;
52 changes: 52 additions & 0 deletions frontend/components/v2/Card.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react';
import { View, StyleSheet, Platform } from 'react-native';
import { BlurView } from 'expo-blur';
import { colors, spacing } from '../../styles/theme';

const Card = ({ children, style, intensity = 100, tint = 'light' }) => {
// The glassmorphic effect is a combination of a semi-transparent background and a blur.
// BlurView is not supported on all platforms (e.g., web, older Android), so we provide a fallback.
if (Platform.OS === 'ios' || Platform.OS === 'android') {
return (
<View style={[styles.cardContainer, style]}>
<BlurView
style={styles.blurView}
intensity={intensity}
tint={tint}
>
{children}
</BlurView>
</View>
);
}

// Fallback for platforms that don't support BlurView
return (
<View style={[styles.cardContainer, styles.fallbackCard, style]}>
{children}
</View>
);
};

const styles = StyleSheet.create({
cardContainer: {
borderRadius: 20,
overflow: 'hidden',
backgroundColor: 'transparent',
},
blurView: {
padding: spacing.lg,
// The background color is set on the BlurView itself for a better effect
backgroundColor: 'rgba(255, 255, 255, 0.4)',
borderWidth: 1,
borderColor: 'rgba(255, 255, 255, 0.2)',
},
fallbackCard: {
backgroundColor: colors.neutral.white,
padding: spacing.lg,
borderWidth: 1,
borderColor: colors.borderSubtle,
},
});

export default Card;
Loading
Loading