Skip to content

Commit

Permalink
feat: fix user routes
Browse files Browse the repository at this point in the history
  • Loading branch information
brolag committed Feb 22, 2025
1 parent b602251 commit 5b751ea
Show file tree
Hide file tree
Showing 26 changed files with 732 additions and 342 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-- CreateTable
CREATE TABLE `Favorite` (
`id` VARCHAR(191) NOT NULL,
`userId` VARCHAR(191) NOT NULL,
`productId` INTEGER NOT NULL,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,

INDEX `Favorite_userId_idx`(`userId`),
INDEX `Favorite_productId_idx`(`productId`),
UNIQUE INDEX `Favorite_userId_productId_key`(`userId`, `productId`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
16 changes: 16 additions & 0 deletions apps/web/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ model Product {
updatedAt DateTime @updatedAt
shoppingCartItems ShoppingCartItem[]
orderItems OrderItem[]
favorites Favorite[]
@@index([name])
}
Expand Down Expand Up @@ -137,6 +138,7 @@ model User {
sessions Session[]
farms Farm[]
password String?
favorites Favorite[]
}

model Farm {
Expand All @@ -154,6 +156,20 @@ model Farm {
updatedAt DateTime @updatedAt
}

model Favorite {
id String @id @default(cuid())
userId String
productId Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
product Product @relation(fields: [productId], references: [id])
@@unique([userId, productId])
@@index([userId])
@@index([productId])
}

model VerificationToken {
identifier String
token String @unique
Expand Down
Binary file modified apps/web/public/favicon.ico
Binary file not shown.
36 changes: 35 additions & 1 deletion apps/web/public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@
"chat_with_seller": "Chat with the seller",
"bags_available": "Bags Available",
"bags": "bags",
"bags_available_count": "{{count}} bags available",
"unit_price": "Unit price ({{weight}})",
"sold_out": "SOLD OUT",
"add_to_favorites": "Add to favorites",
Expand Down Expand Up @@ -211,6 +212,7 @@
"contributor": "Contributor",
"producer": "Producer",
"united_states": "United States",
"costa_rica": "Costa Rica",
"since": "Since",

"edit_my_profile": "Edit my profile",
Expand Down Expand Up @@ -315,5 +317,37 @@

"your_coffee": "Your Coffee",
"delivery_information": "Delivery Information",
"shipping_to": "Shipping to"
"shipping_to": "Shipping to",

"badges": {
"lover": "Coffee Lover",
"contributor": "Contributor",
"producer": "Producer"
},

"update_status": "Update Status",
"status_updated": "Status updated successfully",

"cart": {
"empty_message": "Your cart is empty",
"quantity_label": "Quantity",
"remove_confirmation_title": "Do you want to remove this item?",
"remove_confirmation_yes": "Yes, remove",
"cancel": "Cancel",
"total": "Total",
"checkout": "Checkout"
},

"added_to_favorites": "Added to favorites",
"error_adding_to_favorites": "Error adding to favorites",
"removed_from_favorites": "Removed from favorites",
"error_removing_from_favorites": "Error removing from favorites",
"error_updating_favorites": "Error updating favorites",
"please_connect_to_favorite": "Please connect to add to favorites",
"loading_strk_price": "Loading STRK price...",
"connect_to_buy": "Connect to buy",
"share_product": "Share product",
"farm_details": "Farm details",
"no_favorite_products": "No favorite products yet",
"click_to_upload": "Click to upload"
}
37 changes: 36 additions & 1 deletion apps/web/public/locales/es/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
"chat_with_seller": "Chatear con el vendedor",
"bags_available": "Bolsas disponibles",
"bags": "bolsas",
"bags_available_count": "{{count}} bolsas disponibles",
"unit_price": "Precio por unidad ({{weight}})",
"sold_out": "AGOTADO",
"add_to_favorites": "Agregar a favoritos",
Expand Down Expand Up @@ -203,6 +204,7 @@
"contributor": "Contribuyente",
"producer": "Productor",
"united_states": "Estados Unidos",
"costa_rica": "Costa Rica",
"since": "Desde",

"edit_my_profile": "Editar mi perfil",
Expand Down Expand Up @@ -280,5 +282,38 @@
"en": "Inglés",
"es": "Español",
"pt": "Portugués"
}
},

"badges": {
"lover": "Amante del Café",
"contributor": "Contribuidor",
"producer": "Productor"
},

"update_status": "Actualizar Estado",
"status_updated": "Estado actualizado con éxito",

"cart": {
"empty_message": "Tu carrito está vacío",
"quantity_label": "Cantidad",
"remove_confirmation_title": "¿Deseas eliminar este artículo?",
"remove_confirmation_yes": "Sí, eliminar",
"cancel": "Cancelar",
"total": "Total",
"checkout": "Pagar"
},

"added_to_favorites": "Agregado a favoritos",
"error_adding_to_favorites": "Error al agregar a favoritos",
"removed_from_favorites": "Eliminado de favoritos",
"error_removing_from_favorites": "Error al eliminar de favoritos",
"error_updating_favorites": "Error al actualizar favoritos",
"please_connect_to_favorite": "Por favor conéctate para agregar a favoritos",
"loading_strk_price": "Cargando precio en STRK...",
"connect_to_buy": "Conéctate para comprar",
"marketplace": "Mercado",
"share_product": "Compartir producto",
"farm_details": "Detalles de la granja",
"no_favorite_products": "Aún no hay productos favoritos",
"click_to_upload": "Haz clic para subir"
}
24 changes: 23 additions & 1 deletion apps/web/public/locales/pt/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
"chat_with_seller": "Conversar com o vendedor",
"bags_available": "Sacos disponíveis",
"bags": "sacos",
"bags_available_count": "{{count}} sacos disponíveis",
"unit_price": "Preço por unidade ({{weight}})",
"sold_out": "ESGOTADO",
"add_to_favorites": "Adicionar aos favoritos",
Expand Down Expand Up @@ -203,6 +204,7 @@
"contributor": "Contribuidor",
"producer": "Produtor",
"united_states": "Estados Unidos",
"costa_rica": "Costa Rica",
"since": "Desde",

"edit_my_profile": "Editar meu perfil",
Expand Down Expand Up @@ -280,5 +282,25 @@
"en": "Inglês",
"es": "Espanhol",
"pt": "Português"
}
},

"badges": {
"lover": "Amante do Café",
"contributor": "Contribuidor",
"producer": "Produtor"
},

"update_status": "Atualizar Status",
"status_updated": "Status atualizado com sucesso",

"cart": {
"empty_message": "Seu carrinho está vazio",
"quantity_label": "Quantidade",
"remove_confirmation_title": "Deseja remover este item?",
"remove_confirmation_yes": "Sim, remover",
"cancel": "Cancelar",
"total": "Total",
"checkout": "Finalizar compra"
},
"click_to_upload": "Clique para fazer upload"
}
29 changes: 25 additions & 4 deletions apps/web/src/app/_components/features/FilterModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ interface FilterModalProps {
onClose: () => void;
}

interface ProductMetadata {
region?: string;
farmName?: string;
strength?: string;
imageUrl?: string;
imageAlt?: string;
description?: string;
}

export default function FilterModal({ isOpen, onClose }: FilterModalProps) {
const { t } = useTranslation();
const [selectedStrength, setSelectedStrength] = useState("");
Expand All @@ -41,10 +50,22 @@ export default function FilterModal({ isOpen, onClose }: FilterModalProps) {
const { data } = await refetch();

if (data?.productsFound) {
const products = data.productsFound.map((product) => ({
...product,
region: product.region,
}));
const products = data.productsFound.map((product) => {
const metadata: ProductMetadata =
typeof product.nftMetadata === "string"
? (JSON.parse(product.nftMetadata) as ProductMetadata)
: (product.nftMetadata as ProductMetadata);
return {
...product,
nftMetadata:
typeof product.nftMetadata === "string"
? product.nftMetadata
: JSON.stringify(product.nftMetadata),
region: metadata.region ?? "",
farmName: metadata.farmName ?? "",
strength: metadata.strength ?? "",
};
});
setSearchResults(products);
setQuantityProducts(products.length);
} else {
Expand Down
30 changes: 26 additions & 4 deletions apps/web/src/app/_components/features/ProductDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { HeartIcon as HeartSolidIcon } from "@heroicons/react/24/solid";
import Button from "@repo/ui/button";
import { DataCard } from "@repo/ui/dataCard";
import { H2, Text } from "@repo/ui/typography";
import { useSession } from "next-auth/react";
import Image from "next/image";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
Expand Down Expand Up @@ -30,6 +31,9 @@ interface ProductDetailsProps {
};
isConnected?: boolean;
onConnect?: () => void;
isFavorited?: boolean;
onToggleFavorite?: () => void;
isLoadingFavorite?: boolean;
}

interface CoinGeckoResponse {
Expand Down Expand Up @@ -59,6 +63,9 @@ export default function ProductDetails({
product,
isConnected,
onConnect,
isFavorited,
onToggleFavorite,
isLoadingFavorite,
}: ProductDetailsProps) {
const {
image,
Expand All @@ -75,7 +82,6 @@ export default function ProductDetails({

const { t } = useTranslation();
const [quantity, setQuantity] = useState(1);
const [isLiked, setIsLiked] = useState(false);
const [selectedImage, setSelectedImage] = useState(0);
const [strkPrice, setStrkPrice] = useState<number | null>(null);
const [isLoadingPrice, setIsLoadingPrice] = useState(false);
Expand All @@ -87,6 +93,7 @@ export default function ProductDetails({
void refetchCart();
},
});
const { data: session } = useSession();

useEffect(() => {
const fetchStrkPrice = async () => {
Expand Down Expand Up @@ -160,6 +167,18 @@ export default function ProductDetails({
}
};

const handleFavoriteClick = () => {
console.log("Favorite clicked", { isConnected, isFavorited, session });
if (!session) {
// If not authenticated, trigger connect
onConnect?.();
return;
}
if (onToggleFavorite) {
onToggleFavorite();
}
};

return (
<div className="w-full">
{/* Navigation Bar */}
Expand Down Expand Up @@ -205,13 +224,16 @@ export default function ProductDetails({
</button>
<button
type="button"
onClick={() => setIsLiked(!isLiked)}
onClick={handleFavoriteClick}
disabled={isLoadingFavorite}
className="p-2 hover:bg-gray-100 rounded-full transition-colors"
aria-label={
isLiked ? t("remove_from_favorites") : t("add_to_favorites")
isFavorited
? t("remove_from_favorites")
: t("add_to_favorites")
}
>
{isLiked ? (
{isFavorited ? (
<HeartSolidIcon className="h-6 w-6 text-red-500" />
) : (
<HeartIcon className="h-6 w-6" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function ProfileOptionLayout({
/>
<div className="container mx-auto px-4 py-8">
<div className="flex items-center mb-6">
<Link href={backLink ?? "/user-profile"} className="mr-4">
<Link href={backLink ?? "/marketplace"} className="mr-4">
<ArrowLeftIcon
name="arrow-left"
className="w-5 h-5 text-gray-600"
Expand Down
45 changes: 41 additions & 4 deletions apps/web/src/app/_components/features/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,32 @@ const searchSchema = z.object({

type formData = z.infer<typeof searchSchema>;

const metadataSchema = z.object({
process: z.string().optional(),
region: z.string().optional(),
farmName: z.string().optional(),
strength: z.string().optional(),
});

type ProductMetadata = z.infer<typeof metadataSchema>;

const parseMetadata = (data: unknown): ProductMetadata => {
try {
if (typeof data === "string") {
return metadataSchema.parse(JSON.parse(data));
}
return metadataSchema.parse(data);
} catch {
console.warn("Failed to parse metadata, using default values");
return {
process: undefined,
region: undefined,
farmName: undefined,
strength: undefined,
};
}
};

export default function SearchBar() {
const [query, setQuery] = useAtom(searchQueryAtom);
const [, setSearchResults] = useAtom(searchResultsAtom);
Expand All @@ -44,10 +70,21 @@ export default function SearchBar() {
setIsLoading(isLoading);

if (data?.productsFound) {
const productsWithProcess = data.productsFound.map((product) => ({
...product,
process: product.process ?? t("natural_process"),
}));
const productsWithProcess = data.productsFound.map((product) => {
const metadata = parseMetadata(product.nftMetadata);

return {
...product,
nftMetadata:
typeof product.nftMetadata === "string"
? product.nftMetadata
: JSON.stringify(product.nftMetadata),
process: metadata.process ?? t("natural_process"),
region: metadata.region ?? "",
farmName: metadata.farmName ?? "",
strength: metadata.strength ?? "",
};
});
setSearchResults(productsWithProcess);
setQuantityProducts(productsWithProcess.length);
} else {
Expand Down
Loading

0 comments on commit 5b751ea

Please sign in to comment.