Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CC-29: connect favoriting gifts #70

Merged
merged 3 commits into from
Nov 19, 2023
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
27 changes: 27 additions & 0 deletions api/src/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,33 @@ func (pg *PgController) Serve() *gin.Engine {
c.JSON(http.StatusOK, giftAddedCollection)
})

r.POST("/removeCustomerGiftCollection/:collectionName/:customerId", func(c *gin.Context) {
var input model.Gift

collectionName := c.Param("collectionName")
customerId := c.Param("customerId")

intId, err := strconv.Atoi(customerId)
if err != nil {
panic(err)
}

if err := c.BindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, "Failed to unmarshal gift")
fmt.Print(err)
return
}

giftRemovedCollection, err := pg.DeleteGiftFromCustomerCollection(input, collectionName, int64(intId))

if err != nil {
c.JSON(http.StatusBadRequest, input)
panic(err)
}

c.JSON(http.StatusOK, giftRemovedCollection)
})


// Delete Gift to Gift Collection
r.DELETE("/removeGiftFromGiftCollection/:giftID/:giftCollectionID", func(c *gin.Context) {
Expand Down
11 changes: 9 additions & 2 deletions api/src/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import (
"CaitsCurates/backend/src/controller"
"CaitsCurates/backend/src/model"
"fmt"
"github.com/lib/pq"
"gorm.io/gorm/logger"
"log"
"os"
"time"

"github.com/lib/pq"
"gorm.io/gorm/logger"

"gorm.io/driver/postgres"
"gorm.io/gorm"
)
Expand Down Expand Up @@ -183,9 +184,15 @@ func main() {
CollectionName: "Decor Enhancers",
Gifts: []*model.Gift{&decorativeGift3, &decorativeGift4, &decorativeGift2, &decorativeGift1},
}
giftCollectionFavorites := model.GiftCollection{
CustomerID: &customer1.UserID,
CollectionName: "Favorites",
Gifts: []*model.Gift{},
}
err = db.Create(&giftCollectionToy).Error
err = db.Create(&giftCollectionFall).Error
err = db.Create(&giftCollectionDecor).Error
err = db.Create(&giftCollectionFavorites).Error
err = db.Create(&randomGift1).Error
err = db.Create(&randomGift2).Error

Expand Down
12 changes: 12 additions & 0 deletions api/src/model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type Model interface {
AddGiftToGiftCollection(Gift, int64) (GiftCollection, error)
AddGiftToCustomerCollection(Gift, string, int64) (GiftCollection, error)
DeleteGiftFromGiftCollection(int64, int64) (GiftCollection, error)
DeleteGiftFromCustomerCollection(Gift, string, int64) (GiftCollection, error)
}


Expand Down Expand Up @@ -242,6 +243,17 @@ func (m *PgModel) AddGiftToCustomerCollection(gift Gift, collectionName string,
return giftAddedCollection, nil
}

func (m *PgModel) DeleteGiftFromCustomerCollection(gift Gift, collectionName string, customerId int64) (GiftCollection, error) {

giftDeletedCollection, err := DeleteGiftFromCustomerCollectionFromDB(m.Conn, gift, collectionName, customerId);

if err != nil {
return GiftCollection{}, err
}

return giftDeletedCollection, nil
}

func (m *PgModel) DeleteGiftFromGiftCollection(giftID int64, giftCollectionID int64) (GiftCollection, error) {

giftDeletedCollection, err := DeleteGiftFromCollectionFromDB(m.Conn, giftID, giftCollectionID)
Expand Down
21 changes: 20 additions & 1 deletion api/src/model/transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ func GetAllCollectionsFromDB(db *gorm.DB) ([]GiftCollection, error) {
// GetAllCustomerCollectionsFromDB fetches all GiftCollections that associated with the customer ID or none
func GetAllCustomerCollectionsFromDB(db *gorm.DB, id int64) ([]GiftCollection, error) {
var collections []GiftCollection
if err := db.Where("customer_id = ? OR customer_id IS NULL", id).Preload("Gifts").Find(&collections).Error; err != nil {
if err := db.Preload("Gifts").Preload("Gifts.GiftCollections").Where("customer_id = ? OR customer_id IS NULL", id).Find(&collections).Error; err != nil {
return nil, err
}
return collections, nil
Expand Down Expand Up @@ -299,6 +299,25 @@ func AddGiftToCustomerCollectionFromDB(db *gorm.DB, gift Gift, collectionName st
return collection, nil
}

func DeleteGiftFromCustomerCollectionFromDB(db *gorm.DB, gift Gift, collectionName string, customerId int64) (GiftCollection, error) {
var collection GiftCollection
if err := db.Preload("Gifts").Where("collection_name = ? AND customer_id = ?", collectionName, customerId).First(&collection).Error; err != nil {
return GiftCollection{}, err
}

var giftRemovedCollection []*Gift
for _, collectionGift := range collection.Gifts {
if collectionGift.Name != gift.Name {
giftRemovedCollection = append(giftRemovedCollection, collectionGift)
}
}
if err := db.Model(&collection).Association("Gifts").Replace(giftRemovedCollection); err != nil {
return GiftCollection{}, err
}

return collection, nil
}

func DeleteGiftFromCollectionFromDB(db *gorm.DB, giftID int64, giftCollectionID int64) (GiftCollection, error) {
var collection GiftCollection
if err := db.Preload("Gifts").First(&collection, giftCollectionID).Error; err != nil {
Expand Down
98 changes: 95 additions & 3 deletions api/tests/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1148,7 +1148,7 @@ func TestGetAllCustomerGiftCollection(t *testing.T) {
// Test code
w := httptest.NewRecorder()


// Create a Customer
user := model.User{}
err = tx.Create(&user).Error
Expand Down Expand Up @@ -1294,7 +1294,7 @@ func TestAddGiftToCustomerGiftCollection(t *testing.T) {

req, err := http.NewRequest(
"POST",
fmt.Sprintf("/addCustomerGiftCollection/%s/%d", retrievedCollection.CollectionName, retrievedCustomer.ID),
fmt.Sprintf("/addCustomerGiftCollection/%s/%d", retrievedCollection.CollectionName, retrievedCustomer.ID),
bytes.NewBuffer(giftJSON),
)
router.ServeHTTP(w, req)
Expand Down Expand Up @@ -1507,7 +1507,7 @@ func TestSearchGift(t *testing.T) {

assert.GreaterOrEqual(t, len(retrievedOneCategoryGift), 1)

// Test Empty
// Test Empty
req9, err := http.NewRequest("GET", fmt.Sprintf("/search/%d", collection.ID), nil)
if err != nil {
t.Fatalf("Error creating request: %v", err)
Expand All @@ -1522,3 +1522,95 @@ func TestSearchGift(t *testing.T) {

assert.GreaterOrEqual(t, len(retrievedAllGifts), 3)
}


func TestDeleteGiftFromCustomerGiftCollection(t *testing.T) {
// Database setup
dsn := "user=testuser password=testpwd host=localhost port=5433 dbname=testdb sslmode=disable"
if dbURL, exists := os.LookupEnv("TEST_DATABASE_URL"); exists {
dsn = dbURL
}
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
t.Fatalf("Unable to connect to database: %v", err)
}
// Put auto migrations here
err = db.AutoMigrate(&model.GiftCollection{}, &model.Gift{}, &model.Customer{}, &model.User{})
if err != nil {
panic("failed to migrate test database schema")
}
// Wrap the DB connection in a transaction
tx := db.Begin()
defer tx.Rollback()

// Create Model and Controller
m := &model.PgModel{Conn: tx}
c := &c.PgController{Model: m}
router := c.Serve()

// Test code
w := httptest.NewRecorder()

// Create a Customer
user := model.User{}
err = tx.Create(&user).Error
assert.NoError(t, err)
var retrievedUser model.User
err = tx.First(&retrievedUser).Error
assert.NoError(t, err)
customer := model.Customer{
User: retrievedUser,
}
err = tx.Create(&customer).Error
assert.NoError(t, err)
var retrievedCustomer model.Customer
err = tx.First(&retrievedCustomer).Error
assert.NoError(t, err)

// Create gifts
giftToRemove := model.Gift{
Name: "gift to remove",
Price: 25,
}
giftToStay := model.Gift{
Name: "gift to stay",
Price: 25,
}
giftJSON, err := json.Marshal(giftToRemove)
if err != nil {
t.Fatalf("Error marshaling JSON: %v", err)
}
assert.NoError(t, err)

// Create a collection
collection := model.GiftCollection{
CustomerID: &retrievedCustomer.ID,
CollectionName: "test name",
Gifts: []*model.Gift{&giftToRemove, &giftToStay},
}

err = tx.Create(&collection).Error
assert.NoError(t, err)
var retrievedCollection model.GiftCollection
err = tx.Preload("Gifts").First(&retrievedCollection).Error;
assert.NoError(t, err)

req, err := http.NewRequest(
"POST",
fmt.Sprintf("/removeCustomerGiftCollection/%s/%d", retrievedCollection.CollectionName, retrievedCustomer.ID),
bytes.NewBuffer(giftJSON),
)
router.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)

var collectionResponse model.GiftCollection
if e := json.Unmarshal(w.Body.Bytes(), &collectionResponse); e != nil {
t.Fatalf("Error unmarshaling JSON: %v", e)
}

assert.Equal(t, &retrievedCustomer.ID, collectionResponse.CustomerID)
assert.Equal(t, collection.CollectionName, collectionResponse.CollectionName)
assert.Equal(t, 1, len(collectionResponse.Gifts))
assert.Equal(t, giftToStay.Name, collectionResponse.Gifts[0].Name)
assert.Equal(t, giftToStay.Price, collectionResponse.Gifts[0].Price)
}
2 changes: 1 addition & 1 deletion client/src/components/CollectionForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ function CollectionForm({ collection, allGifts, onSave, onClose }: EditFormProps
CollectionName: editedName,
Gifts: editedGifts,
Customer: collection?.Customer,
CustomerId: collection?.CustomerId
CustomerID: collection?.CustomerID
};

try {
Expand Down
16 changes: 10 additions & 6 deletions client/src/components/UpdatedGiftItem.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import { Gift } from "../types";

type GiftItemProps = {
name: string;
price: number;
gift: Gift;
isSaved: boolean;
onFavoriteClick: (gift: Gift, isSaved: boolean) => void;
};

function UpdatedGiftItem({ name, price }: GiftItemProps) {
function UpdatedGiftItem({ gift, isSaved, onFavoriteClick }: GiftItemProps) {
return (
<div className="relative flex flex-col bg-gray-100 flex-start">
<div className="bg-gray-200 w-40 h-40 mx-auto mb-2 relative">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
fill={isSaved ? "red" : "none"}
viewBox="0 0 24 24"
strokeWidth="1.5"
stroke="black"
className="w-6 h-6 absolute bottom-2 right-2"
onClick={() => onFavoriteClick(gift, isSaved)}
>
<path
strokeLinecap="round"
Expand All @@ -23,8 +27,8 @@ function UpdatedGiftItem({ name, price }: GiftItemProps) {
</svg>
</div>
<div className="">
<h2 className="text-sm text-black font-bold">{name}</h2>
<h2 className="text-xs text-black">${price}</h2>
<h2 className="text-sm text-black font-bold">{gift.Name}</h2>
<h2 className="text-xs text-black">${gift.Price}</h2>
</div>
</div>
);
Expand Down
34 changes: 27 additions & 7 deletions client/src/pages/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import GiftSortNavBar from "../components/GiftSortNavBar";
import UpdatedGiftItem from "../components/UpdatedGiftItem";
import axios from "axios";
import {useEffect, useState} from "react";
import {GiftCollection} from "../types.tsx";
import {Gift, GiftCollection} from "../types.tsx";

const HomePage = () => {

Expand Down Expand Up @@ -74,7 +74,7 @@ const HomePage = () => {

const exampleGiftCollection = {
ID: 1,
CustomerId: 1,
CustomerID: 1,
Customer: exampleCustomer,
CollectionName: 'Default',
Gifts: exampleGifts,
Expand All @@ -88,15 +88,32 @@ const HomePage = () => {
getCollection();
}, []);

const getCollection = async ()=> {
const getCollection = async (): Promise<GiftCollection[] | undefined> => {
try {
const response = await axios.get(`/api/collections/${customerID}`);
setCollections(response.data);
return response.data;
} catch (error) {
console.error('An error occurred while fetching the collection:', error);
}
};

const handleFavoriteClick = async (gift: Gift, isSaved: boolean) => {
const baseUrl = isSaved ? "/api/removeCustomerGiftCollection" : "/api/addCustomerGiftCollection"
try {
await axios.post(`${baseUrl}/Favorites/${customerID}`, gift)
// refetch customer gift collections
const updatedCollection = await getCollection();

if (updatedCollection) {
// on success set state for currently displayed collection
const currentCollection = updatedCollection.find((collection) => collection.ID === displayCollection.ID) ?? displayCollection;
setDisplayCollection(currentCollection);
}
} catch (error) {
console.error('An error occured while favoriting a gift:', error)
}
}

return (
<div className="bg-gray-100 h-full text-white flex flex-col">
Expand All @@ -113,7 +130,7 @@ const HomePage = () => {
<div className="flex space-x-4">
{collections.map((collection, index) => (
<div
className={`cursor-pointer ${collection === displayCollection ? 'font-bold' : ''}`}
className={`cursor-pointer ${collection.ID === displayCollection.ID ? 'font-bold' : ''}`}
onClick={() => setDisplayCollection(collection)}>
<CollectionItem key={index} name={collection.CollectionName} gifts={collection.Gifts} />
</div>
Expand All @@ -126,11 +143,14 @@ const HomePage = () => {

<div className="overflow-y-auto" style={{ maxHeight: '290px', maxWidth: '1000px' }}>
<div className="flex flex-wrap justify-between gap-4">
{displayCollection.Gifts.map((gift, index) => (
{displayCollection.Gifts.map((gift, index) => {
const isSaved = gift.GiftCollections === null ? false : gift.GiftCollections.some((collection) => collection.CollectionName === "Favorites" && collection.CustomerID === customerID)
return (
<div key={index}>
<UpdatedGiftItem name={gift.Name} price={gift.Price} />
<UpdatedGiftItem gift={gift} isSaved={isSaved} onFavoriteClick={handleFavoriteClick}/>
</div>
))}
)
})}
</div>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions client/src/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export interface Gift {

export interface GiftRequest {
ID: number;
CustomerId: number;
CustomerID: number;
GiftResponseId: number | null;
RecipientName: string;
RecipientAge: number;
Expand All @@ -26,7 +26,7 @@ export interface GiftRequest {

export interface GiftCollection {
ID: number;
CustomerId: number | null;
CustomerID: number | null;
Customer: Customer;
CollectionName: string;
Gifts: Gift[];
Expand Down
Loading