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
54 changes: 54 additions & 0 deletions .github/workflows/proj3-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Proj3 Deploy (manual)

on:
workflow_dispatch:
inputs:
environment:
description: "Target environment name"
default: "production"
required: true

jobs:
build-and-push-backend:
name: Proj3 • Build & Push Backend Image (GHCR)
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build & Push
uses: docker/build-push-action@v6
with:
context: proj3/backend
push: true
tags: ghcr.io/${{ github.repository }}-proj3-backend:latest,ghcr.io/${{ github.repository }}-proj3-backend:${{ github.sha }}

build-and-push-frontend:
name: Proj3 • Build & Push Frontend Image (GHCR)
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build & Push
uses: docker/build-push-action@v6
with:
context: proj3/frontend
push: true
tags: ghcr.io/${{ github.repository }}-proj3-frontend:latest,ghcr.io/${{ github.repository }}-proj3-frontend:${{ github.sha }}
1 change: 1 addition & 0 deletions proj3/backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ async def lifespan(app: FastAPI):
lifespan=lifespan,
)

# This is a test comment to trigger the workflow
frontend_path = os.path.abspath(
os.path.join(os.path.dirname(__file__), "..", "frontend", "dist")
)
Expand Down
1 change: 0 additions & 1 deletion proj3/frontend/src/components/CartTab.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable no-unused-vars */
import React from "react";
import { Button } from './ui/button';
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from './ui/alert-dialog';
Expand Down
2 changes: 1 addition & 1 deletion proj3/frontend/src/components/Dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export default function Dashboard({ onLogout }) {
}));
}
}
}, [user]);
}, [user, preferences.userLocation]);

useEffect(() => {
localStorage.setItem("preferences", JSON.stringify(preferences));
Expand Down
1 change: 0 additions & 1 deletion proj3/frontend/src/components/DisputeForm.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable no-unused-vars */
import React, { useState } from 'react';
import { Button } from './ui/button';
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from './ui/dialog';
Expand Down
1 change: 0 additions & 1 deletion proj3/frontend/src/components/DisputesTab.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable no-unused-vars */
import React, { useState, useEffect } from 'react';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/card';
import { Badge } from './ui/badge';
Expand Down
8 changes: 4 additions & 4 deletions proj3/frontend/src/components/EventsTab.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react';
import { useEffect, useState, useCallback } from 'react';
import { listEvents, listMyEvents, createEvent, joinEvent, addDish } from '../services/EventService';
import { Button } from './ui/button';
import { Card, CardHeader, CardTitle, CardContent } from './ui/card';
Expand All @@ -16,7 +16,7 @@ export default function EventsTab({ currentUser, authToken }) {
const [dishTitle, setDishTitle] = useState('');
const [dishDescription, setDishDescription] = useState('');

const load = async (mine = false) => {
const load = useCallback(async (mine = false) => {
try {
let data;
if (mine) {
Expand All @@ -34,9 +34,9 @@ export default function EventsTab({ currentUser, authToken }) {
console.error('Failed to load events', err);
toast.error('Failed to load events');
}
};
}, [authToken]);

useEffect(() => { load(showMine); }, [showMine]);
useEffect(() => { load(showMine); }, [showMine, load]);

const handleCreate = async () => {
if (!form.title || !form.date) {
Expand Down
1 change: 0 additions & 1 deletion proj3/frontend/src/components/MealCard.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable no-unused-vars */
import { useState } from "react";
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "./ui/card";
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from './ui/alert-dialog';
Expand Down
1 change: 0 additions & 1 deletion proj3/frontend/src/components/OrderHistoryTab.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable no-unused-vars */
import React, { useState, useEffect } from 'react';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/card';
import { Badge } from './ui/badge';
Expand Down
1 change: 0 additions & 1 deletion proj3/frontend/src/components/Profile.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable no-undef */
import { useState, useEffect } from "react";
import * as Dialog from "@radix-ui/react-dialog";
import { Avatar, AvatarFallback } from "@/components/ui/avatar"; // adjust your import
Expand Down
2 changes: 0 additions & 2 deletions proj3/frontend/src/components/RecommendationsTab.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/* eslint-disable no-undef */
/* eslint-disable no-unused-vars */
import { useEffect, useState } from 'react';
import { getRecommendedMeals, getAllMeals } from '../services/MealService';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/card';
Expand Down Expand Up @@ -152,7 +150,7 @@
setReportingMeal(null);
};

const handleRateMeal = () => {

Check failure on line 153 in proj3/frontend/src/components/RecommendationsTab.jsx

View workflow job for this annotation

GitHub Actions / Proj3 • Frontend Checks

'handleRateMeal' is assigned a value but never used. Allowed unused vars must match /^[A-Z_]/u
if (selectedMeal) {
onRateRestaurant(selectedMeal.id, ratingValue, reviewText);
setSelectedMeal(null);
Expand Down
14 changes: 7 additions & 7 deletions proj3/frontend/src/components/ReviewsList.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useCallback } from 'react';
import StarRating from './StarRating';

const ReviewsList = ({ userId }) => {
const [reviewsData, setReviewsData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');

useEffect(() => {
fetchReviews();
}, [userId]);

const fetchReviews = async () => {
const fetchReviews = useCallback(async () => {
try {
const response = await fetch(`http://localhost:8000/reviews/user/${userId}`);
if (!response.ok) {
Expand All @@ -23,7 +19,11 @@ const ReviewsList = ({ userId }) => {
} finally {
setLoading(false);
}
};
}, [userId]);

useEffect(() => {
fetchReviews();
}, [fetchReviews]);

if (loading) {
return (
Expand Down
25 changes: 2 additions & 23 deletions proj3/frontend/src/components/ui/badge.jsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,9 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva } from "class-variance-authority";
import { badgeVariants } from "./variants"

import { cn } from "@/lib/utils"

const badgeVariants = cva(
"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
{
variants: {
variant: {
default:
"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
secondary:
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
destructive:
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
},
},
defaultVariants: {
variant: "default",
},
}
)

function Badge({
className,
variant,
Expand All @@ -41,4 +20,4 @@ function Badge({
);
}

export { Badge, badgeVariants }
export { Badge }
36 changes: 2 additions & 34 deletions proj3/frontend/src/components/ui/button.jsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,9 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva } from "class-variance-authority";
import { buttonVariants } from "./variants"

import { cn } from "@/lib/utils"

const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost:
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
icon: "size-9",
"icon-sm": "size-8",
"icon-lg": "size-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)

function Button({
className,
variant,
Expand All @@ -53,4 +21,4 @@ function Button({
);
}

export { Button, buttonVariants }
export { Button }
54 changes: 54 additions & 0 deletions proj3/frontend/src/components/ui/variants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { cva } from "class-variance-authority";

export const badgeVariants = cva(
"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
{
variants: {
variant: {
default:
"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
secondary:
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
destructive:
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
},
},
defaultVariants: {
variant: "default",
},
}
);

export const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost:
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
icon: "size-9",
"icon-sm": "size-8",
"icon-lg": "size-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
2 changes: 1 addition & 1 deletion proj3/frontend/src/services/MealService.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const createMeal = async (mealData) => {
let errorBody;
try {
errorBody = await response.json();
} catch (e) {
} catch {
throw new Error("Failed to create meal");
}

Expand Down
6 changes: 4 additions & 2 deletions proj3/frontend/src/utils/badges.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,14 +218,16 @@ export const getBadgeProgress = (badgeId, userStats, userMeals = []) => {
return { current: totalReviews, required: 10, extraInfo: `${avgRating.toFixed(1)} rating` };
case 'community_favorite':
return { current: totalReviews, required: 100 };
case 'diverse_cook':
case 'diverse_cook': {
const uniqueCuisines = new Set(userMeals.map(m => m.cuisine_type).filter(Boolean));
return { current: uniqueCuisines.size, required: 5 };
case 'generous_sharer':
}
case 'generous_sharer': {
const freeOrSwapMeals = userMeals.filter(m =>
(m.sale_price === 0 || !m.available_for_sale) && m.available_for_swap
);
return { current: freeOrSwapMeals.length, required: 10 };
}
default:
return null;
}
Expand Down
Loading