feature/trade-fix #13
@@ -1,11 +1,15 @@
|
|||||||
{
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"mcp__supabase__apply_migration",
|
||||||
|
"mcp__supabase__list_tables",
|
||||||
|
"mcp__supabase__execute_sql",
|
||||||
|
"Bash(npm run build:*)",
|
||||||
|
"mcp__supabase__get_advisors"
|
||||||
|
]
|
||||||
|
},
|
||||||
"enableAllProjectMcpServers": true,
|
"enableAllProjectMcpServers": true,
|
||||||
"enabledMcpjsonServers": [
|
"enabledMcpjsonServers": [
|
||||||
"supabase"
|
"supabase"
|
||||||
],
|
|
||||||
"permissions": {
|
|
||||||
"allow": [
|
|
||||||
"mcp__supabase__apply_migration"
|
|
||||||
]
|
]
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ define(['./workbox-ca84f546'], (function (workbox) { 'use strict';
|
|||||||
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
||||||
}, {
|
}, {
|
||||||
"url": "index.html",
|
"url": "index.html",
|
||||||
"revision": "0.ufhads5pjvs"
|
"revision": "0.tucc18p1f38"
|
||||||
}], {});
|
}], {});
|
||||||
workbox.cleanupOutdatedCaches();
|
workbox.cleanupOutdatedCaches();
|
||||||
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
||||||
|
|||||||
@@ -794,8 +794,10 @@ export default function Community() {
|
|||||||
) : (
|
) : (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{(tradesSubTab === 'pending' ? pendingTrades : tradeHistory).map((trade) => {
|
{(tradesSubTab === 'pending' ? pendingTrades : tradeHistory).map((trade) => {
|
||||||
const isSender = trade.sender_id === user?.id;
|
const isUser1 = trade.user1_id === user?.id;
|
||||||
const otherUser = isSender ? trade.receiver : trade.sender;
|
const myUserId = user?.id || '';
|
||||||
|
const otherUserId = isUser1 ? trade.user2_id : trade.user1_id;
|
||||||
|
const otherUser = isUser1 ? trade.user2 : trade.user1;
|
||||||
const statusColors: Record<string, string> = {
|
const statusColors: Record<string, string> = {
|
||||||
accepted: 'text-green-400',
|
accepted: 'text-green-400',
|
||||||
declined: 'text-red-400',
|
declined: 'text-red-400',
|
||||||
@@ -819,7 +821,7 @@ export default function Community() {
|
|||||||
<div className="flex items-center gap-2 min-w-0">
|
<div className="flex items-center gap-2 min-w-0">
|
||||||
<ArrowLeftRight size={16} className="text-blue-400 flex-shrink-0" />
|
<ArrowLeftRight size={16} className="text-blue-400 flex-shrink-0" />
|
||||||
<span className="text-sm truncate">
|
<span className="text-sm truncate">
|
||||||
{isSender ? 'To' : 'From'}: <strong>{otherUser?.username}</strong>
|
With: <strong>{otherUser?.username}</strong>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span className={`text-xs capitalize ${statusColors[trade.status]}`}>
|
<span className={`text-xs capitalize ${statusColors[trade.status]}`}>
|
||||||
@@ -829,18 +831,18 @@ export default function Community() {
|
|||||||
|
|
||||||
{/* Items */}
|
{/* Items */}
|
||||||
<div className="grid grid-cols-2 gap-2 text-sm">
|
<div className="grid grid-cols-2 gap-2 text-sm">
|
||||||
{renderTradeItems(trade.items, trade.sender_id, isSender ? 'Give' : 'Receive')}
|
{renderTradeItems(trade.items, myUserId, 'You Give')}
|
||||||
{renderTradeItems(trade.items, trade.receiver_id, isSender ? 'Get' : 'Send')}
|
{renderTradeItems(trade.items, otherUserId, 'You Get')}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{canViewDetails && (
|
{canViewDetails && (
|
||||||
<p className="text-xs text-blue-400 text-center pt-1">
|
<p className="text-xs text-blue-400 text-center pt-1">
|
||||||
{isSender ? 'Tap to view/edit' : 'Tap to view details'}
|
Tap to view details
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Actions - Only show quick actions for sender (cancel) */}
|
{/* Actions - Allow any user to cancel pending trade */}
|
||||||
{tradesSubTab === 'pending' && isSender && (
|
{tradesSubTab === 'pending' && (
|
||||||
<div className="flex gap-2 pt-2 border-t border-gray-700" onClick={(e) => e.stopPropagation()}>
|
<div className="flex gap-2 pt-2 border-t border-gray-700" onClick={(e) => e.stopPropagation()}>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleCancelTrade(trade.id)}
|
onClick={() => handleCancelTrade(trade.id)}
|
||||||
|
|||||||
@@ -375,12 +375,12 @@ export default function TradeCreator({
|
|||||||
|
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
try {
|
try {
|
||||||
const senderCards = Array.from(myOfferedCards.values()).map((item) => ({
|
const myCards = Array.from(myOfferedCards.values()).map((item) => ({
|
||||||
cardId: item.card.id,
|
cardId: item.card.id,
|
||||||
quantity: item.quantity,
|
quantity: item.quantity,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const receiverCards = Array.from(wantedCards.values()).map((item) => ({
|
const theirCards = Array.from(wantedCards.values()).map((item) => ({
|
||||||
cardId: item.card.id,
|
cardId: item.card.id,
|
||||||
quantity: item.quantity,
|
quantity: item.quantity,
|
||||||
}));
|
}));
|
||||||
@@ -391,18 +391,18 @@ export default function TradeCreator({
|
|||||||
tradeId: existingTradeId,
|
tradeId: existingTradeId,
|
||||||
editorId: user.id,
|
editorId: user.id,
|
||||||
message: message || undefined,
|
message: message || undefined,
|
||||||
senderCards,
|
myCards,
|
||||||
receiverCards,
|
theirCards,
|
||||||
});
|
});
|
||||||
toast.success('Trade updated!');
|
toast.success('Trade updated!');
|
||||||
} else {
|
} else {
|
||||||
// Create new trade
|
// Create new trade
|
||||||
await createTrade({
|
await createTrade({
|
||||||
senderId: user.id,
|
user1Id: user.id,
|
||||||
receiverId,
|
user2Id: receiverId,
|
||||||
message: message || undefined,
|
message: message || undefined,
|
||||||
senderCards,
|
user1Cards: myCards,
|
||||||
receiverCards,
|
user2Cards: theirCards,
|
||||||
});
|
});
|
||||||
toast.success('Trade offer sent!');
|
toast.success('Trade offer sent!');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,9 +50,11 @@ export default function TradeDetail({
|
|||||||
const [showEditMode, setShowEditMode] = useState(false);
|
const [showEditMode, setShowEditMode] = useState(false);
|
||||||
const [editReceiverCollection, setEditReceiverCollection] = useState<CollectionItem[]>([]);
|
const [editReceiverCollection, setEditReceiverCollection] = useState<CollectionItem[]>([]);
|
||||||
|
|
||||||
const isSender = trade.sender_id === user?.id;
|
const isUser1 = trade.user1_id === user?.id;
|
||||||
const isReceiver = trade.receiver_id === user?.id;
|
const isUser2 = trade.user2_id === user?.id;
|
||||||
const otherUser = isSender ? trade.receiver : trade.sender;
|
const otherUser = isUser1 ? trade.user2 : trade.user1;
|
||||||
|
const myUserId = user?.id || '';
|
||||||
|
const otherUserId = isUser1 ? trade.user2_id : trade.user1_id;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadTradeCards();
|
loadTradeCards();
|
||||||
@@ -73,22 +75,22 @@ export default function TradeDetail({
|
|||||||
const cardMap = new Map<string, Card>();
|
const cardMap = new Map<string, Card>();
|
||||||
cards.forEach(card => cardMap.set(card.id, card));
|
cards.forEach(card => cardMap.set(card.id, card));
|
||||||
|
|
||||||
const senderItems: TradeCardItem[] = [];
|
const myItems: TradeCardItem[] = [];
|
||||||
const receiverItems: TradeCardItem[] = [];
|
const theirItems: TradeCardItem[] = [];
|
||||||
|
|
||||||
trade.items?.forEach(item => {
|
trade.items?.forEach(item => {
|
||||||
const card = cardMap.get(item.card_id);
|
const card = cardMap.get(item.card_id);
|
||||||
if (!card) return;
|
if (!card) return;
|
||||||
|
|
||||||
if (item.owner_id === trade.sender_id) {
|
if (item.owner_id === myUserId) {
|
||||||
senderItems.push({ card, quantity: item.quantity });
|
myItems.push({ card, quantity: item.quantity });
|
||||||
} else {
|
} else {
|
||||||
receiverItems.push({ card, quantity: item.quantity });
|
theirItems.push({ card, quantity: item.quantity });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setSenderCards(senderItems);
|
setSenderCards(myItems);
|
||||||
setReceiverCards(receiverItems);
|
setReceiverCards(theirItems);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading trade cards:', error);
|
console.error('Error loading trade cards:', error);
|
||||||
toast.error('Failed to load trade details');
|
toast.error('Failed to load trade details');
|
||||||
@@ -133,7 +135,6 @@ export default function TradeDetail({
|
|||||||
const handleEdit = async () => {
|
const handleEdit = async () => {
|
||||||
try {
|
try {
|
||||||
// Load the other user's collection for editing
|
// Load the other user's collection for editing
|
||||||
const otherUserId = isSender ? trade.receiver_id : trade.sender_id;
|
|
||||||
const collectionMap = await getUserCollection(otherUserId);
|
const collectionMap = await getUserCollection(otherUserId);
|
||||||
const cardIds = Array.from(collectionMap.keys());
|
const cardIds = Array.from(collectionMap.keys());
|
||||||
const cards = await getCardsByIds(cardIds);
|
const cards = await getCardsByIds(cardIds);
|
||||||
@@ -152,8 +153,8 @@ export default function TradeDetail({
|
|||||||
|
|
||||||
const handleCounterOffer = async () => {
|
const handleCounterOffer = async () => {
|
||||||
try {
|
try {
|
||||||
// For counter-offer, load sender's collection and swap the cards
|
// For counter-offer, load the other user's collection
|
||||||
const collectionMap = await getUserCollection(trade.sender_id);
|
const collectionMap = await getUserCollection(otherUserId);
|
||||||
const cardIds = Array.from(collectionMap.keys());
|
const cardIds = Array.from(collectionMap.keys());
|
||||||
const cards = await getCardsByIds(cardIds);
|
const cards = await getCardsByIds(cardIds);
|
||||||
const collection = cards.map((card) => ({
|
const collection = cards.map((card) => ({
|
||||||
@@ -169,13 +170,11 @@ export default function TradeDetail({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const senderPrice = calculateTotalPrice(senderCards);
|
// senderCards = myCards, receiverCards = theirCards (already calculated correctly)
|
||||||
const receiverPrice = calculateTotalPrice(receiverCards);
|
const yourCards = senderCards;
|
||||||
|
const theirCards = receiverCards;
|
||||||
const yourCards = isSender ? senderCards : receiverCards;
|
const yourPrice = calculateTotalPrice(yourCards);
|
||||||
const theirCards = isSender ? receiverCards : senderCards;
|
const theirPrice = calculateTotalPrice(theirCards);
|
||||||
const yourPrice = isSender ? senderPrice : receiverPrice;
|
|
||||||
const theirPrice = isSender ? receiverPrice : senderPrice;
|
|
||||||
|
|
||||||
// For edit mode, determine initial cards based on mode
|
// For edit mode, determine initial cards based on mode
|
||||||
// Include quantity in the card object so TradeCreator can preserve it
|
// Include quantity in the card object so TradeCreator can preserve it
|
||||||
@@ -190,7 +189,7 @@ export default function TradeDetail({
|
|||||||
if (showEditMode) {
|
if (showEditMode) {
|
||||||
return (
|
return (
|
||||||
<TradeCreator
|
<TradeCreator
|
||||||
receiverId={isSender ? trade.receiver_id : trade.sender_id}
|
receiverId={otherUserId}
|
||||||
receiverUsername={otherUser?.username || 'User'}
|
receiverUsername={otherUser?.username || 'User'}
|
||||||
receiverCollection={editReceiverCollection}
|
receiverCollection={editReceiverCollection}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
@@ -221,7 +220,7 @@ export default function TradeDetail({
|
|||||||
<div>
|
<div>
|
||||||
<h2 className="text-lg font-bold">Trade Details {trade.version > 1 && `(v${trade.version})`}</h2>
|
<h2 className="text-lg font-bold">Trade Details {trade.version > 1 && `(v${trade.version})`}</h2>
|
||||||
<p className="text-sm text-gray-400">
|
<p className="text-sm text-gray-400">
|
||||||
{isSender ? 'To' : 'From'}: {otherUser?.username}
|
With: {otherUser?.username}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -246,7 +245,7 @@ export default function TradeDetail({
|
|||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h3 className="font-semibold text-green-400">
|
<h3 className="font-semibold text-green-400">
|
||||||
{isSender ? 'You Give' : 'You Receive'}
|
You Give
|
||||||
</h3>
|
</h3>
|
||||||
<div className="flex items-center gap-1 text-green-400 text-sm">
|
<div className="flex items-center gap-1 text-green-400 text-sm">
|
||||||
<DollarSign size={14} />
|
<DollarSign size={14} />
|
||||||
@@ -285,7 +284,7 @@ export default function TradeDetail({
|
|||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h3 className="font-semibold text-blue-400">
|
<h3 className="font-semibold text-blue-400">
|
||||||
{isSender ? 'You Receive' : 'They Give'}
|
You Receive
|
||||||
</h3>
|
</h3>
|
||||||
<div className="flex items-center gap-1 text-blue-400 text-sm">
|
<div className="flex items-center gap-1 text-blue-400 text-sm">
|
||||||
<DollarSign size={14} />
|
<DollarSign size={14} />
|
||||||
@@ -330,13 +329,13 @@ export default function TradeDetail({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Price Difference */}
|
{/* Price Difference */}
|
||||||
{!loading && (senderPrice > 0 || receiverPrice > 0) && (
|
{!loading && (yourPrice > 0 || theirPrice > 0) && (
|
||||||
<div className="p-3 bg-gray-800 rounded-lg">
|
<div className="p-3 bg-gray-800 rounded-lg">
|
||||||
<div className="flex items-center justify-between text-sm">
|
<div className="flex items-center justify-between text-sm">
|
||||||
<span className="text-gray-400">Value Difference:</span>
|
<span className="text-gray-400">Value Difference:</span>
|
||||||
<span className={Math.abs(senderPrice - receiverPrice) > 5 ? 'text-yellow-400' : 'text-gray-300'}>
|
<span className={Math.abs(yourPrice - theirPrice) > 5 ? 'text-yellow-400' : 'text-gray-300'}>
|
||||||
${Math.abs(senderPrice - receiverPrice).toFixed(2)}
|
${Math.abs(yourPrice - theirPrice).toFixed(2)}
|
||||||
{senderPrice > receiverPrice ? ' in sender favor' : senderPrice < receiverPrice ? ' in receiver favor' : ' (balanced)'}
|
{yourPrice > theirPrice ? ' in your favor' : yourPrice < theirPrice ? ' in their favor' : ' (balanced)'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -422,9 +421,10 @@ export default function TradeDetail({
|
|||||||
Waiting for {otherUser?.username} to respond...
|
Waiting for {otherUser?.username} to respond...
|
||||||
</p>
|
</p>
|
||||||
) : (
|
) : (
|
||||||
/* No editor yet (version 1) - original flow */
|
/* No editor yet (initial trade) */
|
||||||
<>
|
<>
|
||||||
{isSender ? (
|
{isUser1 ? (
|
||||||
|
/* User1 (initiator) can edit their initial offer */
|
||||||
<button
|
<button
|
||||||
onClick={handleEdit}
|
onClick={handleEdit}
|
||||||
disabled={processing}
|
disabled={processing}
|
||||||
@@ -434,6 +434,7 @@ export default function TradeDetail({
|
|||||||
Edit Trade Offer
|
Edit Trade Offer
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
|
/* User2 (partner) can accept/decline/counter */
|
||||||
<>
|
<>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -205,42 +205,55 @@ export type Database = {
|
|||||||
trades: {
|
trades: {
|
||||||
Row: {
|
Row: {
|
||||||
id: string
|
id: string
|
||||||
sender_id: string
|
user1_id: string
|
||||||
receiver_id: string
|
user2_id: string
|
||||||
status: 'pending' | 'accepted' | 'declined' | 'cancelled'
|
status: 'pending' | 'accepted' | 'declined' | 'cancelled'
|
||||||
message: string | null
|
message: string | null
|
||||||
created_at: string | null
|
created_at: string | null
|
||||||
updated_at: string | null
|
updated_at: string | null
|
||||||
|
version: number
|
||||||
|
editor_id: string | null
|
||||||
}
|
}
|
||||||
Insert: {
|
Insert: {
|
||||||
id?: string
|
id?: string
|
||||||
sender_id: string
|
user1_id: string
|
||||||
receiver_id: string
|
user2_id: string
|
||||||
status?: 'pending' | 'accepted' | 'declined' | 'cancelled'
|
status?: 'pending' | 'accepted' | 'declined' | 'cancelled'
|
||||||
message?: string | null
|
message?: string | null
|
||||||
created_at?: string | null
|
created_at?: string | null
|
||||||
updated_at?: string | null
|
updated_at?: string | null
|
||||||
|
version?: number
|
||||||
|
editor_id?: string | null
|
||||||
}
|
}
|
||||||
Update: {
|
Update: {
|
||||||
id?: string
|
id?: string
|
||||||
sender_id?: string
|
user1_id?: string
|
||||||
receiver_id?: string
|
user2_id?: string
|
||||||
status?: 'pending' | 'accepted' | 'declined' | 'cancelled'
|
status?: 'pending' | 'accepted' | 'declined' | 'cancelled'
|
||||||
message?: string | null
|
message?: string | null
|
||||||
created_at?: string | null
|
created_at?: string | null
|
||||||
updated_at?: string | null
|
updated_at?: string | null
|
||||||
|
version?: number
|
||||||
|
editor_id?: string | null
|
||||||
}
|
}
|
||||||
Relationships: [
|
Relationships: [
|
||||||
{
|
{
|
||||||
foreignKeyName: "trades_sender_id_fkey"
|
foreignKeyName: "trades_user1_id_fkey"
|
||||||
columns: ["sender_id"]
|
columns: ["user1_id"]
|
||||||
isOneToOne: false
|
isOneToOne: false
|
||||||
referencedRelation: "profiles"
|
referencedRelation: "profiles"
|
||||||
referencedColumns: ["id"]
|
referencedColumns: ["id"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
foreignKeyName: "trades_receiver_id_fkey"
|
foreignKeyName: "trades_user2_id_fkey"
|
||||||
columns: ["receiver_id"]
|
columns: ["user2_id"]
|
||||||
|
isOneToOne: false
|
||||||
|
referencedRelation: "profiles"
|
||||||
|
referencedColumns: ["id"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
foreignKeyName: "trades_editor_id_fkey"
|
||||||
|
columns: ["editor_id"]
|
||||||
isOneToOne: false
|
isOneToOne: false
|
||||||
referencedRelation: "profiles"
|
referencedRelation: "profiles"
|
||||||
referencedColumns: ["id"]
|
referencedColumns: ["id"]
|
||||||
|
|||||||
@@ -10,16 +10,16 @@ export interface TradeItem {
|
|||||||
|
|
||||||
export interface Trade {
|
export interface Trade {
|
||||||
id: string;
|
id: string;
|
||||||
sender_id: string;
|
user1_id: string;
|
||||||
receiver_id: string;
|
user2_id: string;
|
||||||
status: 'pending' | 'accepted' | 'declined' | 'cancelled';
|
status: 'pending' | 'accepted' | 'declined' | 'cancelled';
|
||||||
message: string | null;
|
message: string | null;
|
||||||
created_at: string | null;
|
created_at: string | null;
|
||||||
updated_at: string | null;
|
updated_at: string | null;
|
||||||
version: number;
|
version: number;
|
||||||
editor_id: string | null;
|
editor_id: string | null;
|
||||||
sender?: { username: string | null };
|
user1?: { username: string | null };
|
||||||
receiver?: { username: string | null };
|
user2?: { username: string | null };
|
||||||
items?: TradeItem[];
|
items?: TradeItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,19 +43,19 @@ export interface TradeHistoryItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateTradeParams {
|
export interface CreateTradeParams {
|
||||||
senderId: string;
|
user1Id: string;
|
||||||
receiverId: string;
|
user2Id: string;
|
||||||
message?: string;
|
message?: string;
|
||||||
senderCards: { cardId: string; quantity: number }[];
|
user1Cards: { cardId: string; quantity: number }[];
|
||||||
receiverCards: { cardId: string; quantity: number }[];
|
user2Cards: { cardId: string; quantity: number }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateTradeParams {
|
export interface UpdateTradeParams {
|
||||||
tradeId: string;
|
tradeId: string;
|
||||||
editorId: string;
|
editorId: string;
|
||||||
message?: string;
|
message?: string;
|
||||||
senderCards: { cardId: string; quantity: number }[];
|
myCards: { cardId: string; quantity: number }[];
|
||||||
receiverCards: { cardId: string; quantity: number }[];
|
theirCards: { cardId: string; quantity: number }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all trades for a user
|
// Get all trades for a user
|
||||||
@@ -64,11 +64,11 @@ export async function getTrades(userId: string): Promise<Trade[]> {
|
|||||||
.from('trades')
|
.from('trades')
|
||||||
.select(`
|
.select(`
|
||||||
*,
|
*,
|
||||||
sender:profiles!trades_sender_id_fkey(username),
|
user1:profiles!trades_user1_id_fkey(username),
|
||||||
receiver:profiles!trades_receiver_id_fkey(username),
|
user2:profiles!trades_user2_id_fkey(username),
|
||||||
items:trade_items(*)
|
items:trade_items(*)
|
||||||
`)
|
`)
|
||||||
.or(`sender_id.eq.${userId},receiver_id.eq.${userId}`)
|
.or(`user1_id.eq.${userId},user2_id.eq.${userId}`)
|
||||||
.order('created_at', { ascending: false });
|
.order('created_at', { ascending: false });
|
||||||
|
|
||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
@@ -81,12 +81,12 @@ export async function getPendingTrades(userId: string): Promise<Trade[]> {
|
|||||||
.from('trades')
|
.from('trades')
|
||||||
.select(`
|
.select(`
|
||||||
*,
|
*,
|
||||||
sender:profiles!trades_sender_id_fkey(username),
|
user1:profiles!trades_user1_id_fkey(username),
|
||||||
receiver:profiles!trades_receiver_id_fkey(username),
|
user2:profiles!trades_user2_id_fkey(username),
|
||||||
items:trade_items(*)
|
items:trade_items(*)
|
||||||
`)
|
`)
|
||||||
.eq('status', 'pending')
|
.eq('status', 'pending')
|
||||||
.or(`sender_id.eq.${userId},receiver_id.eq.${userId}`)
|
.or(`user1_id.eq.${userId},user2_id.eq.${userId}`)
|
||||||
.order('created_at', { ascending: false });
|
.order('created_at', { ascending: false });
|
||||||
|
|
||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
@@ -99,8 +99,8 @@ export async function getTradeById(tradeId: string): Promise<Trade | null> {
|
|||||||
.from('trades')
|
.from('trades')
|
||||||
.select(`
|
.select(`
|
||||||
*,
|
*,
|
||||||
sender:profiles!trades_sender_id_fkey(username),
|
user1:profiles!trades_user1_id_fkey(username),
|
||||||
receiver:profiles!trades_receiver_id_fkey(username),
|
user2:profiles!trades_user2_id_fkey(username),
|
||||||
items:trade_items(*)
|
items:trade_items(*)
|
||||||
`)
|
`)
|
||||||
.eq('id', tradeId)
|
.eq('id', tradeId)
|
||||||
@@ -112,39 +112,40 @@ export async function getTradeById(tradeId: string): Promise<Trade | null> {
|
|||||||
|
|
||||||
// Create a new trade with items
|
// Create a new trade with items
|
||||||
export async function createTrade(params: CreateTradeParams): Promise<Trade> {
|
export async function createTrade(params: CreateTradeParams): Promise<Trade> {
|
||||||
const { senderId, receiverId, message, senderCards, receiverCards } = params;
|
const { user1Id, user2Id, message, user1Cards, user2Cards } = params;
|
||||||
|
|
||||||
// Create the trade
|
// Create the trade
|
||||||
const { data: trade, error: tradeError } = await supabase
|
const { data: trade, error: tradeError } = await supabase
|
||||||
.from('trades')
|
.from('trades')
|
||||||
.insert({
|
.insert({
|
||||||
sender_id: senderId,
|
user1_id: user1Id,
|
||||||
receiver_id: receiverId,
|
user2_id: user2Id,
|
||||||
message,
|
message,
|
||||||
status: 'pending',
|
status: 'pending',
|
||||||
|
// editor_id starts as null - gets set when someone edits the trade
|
||||||
})
|
})
|
||||||
.select()
|
.select()
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (tradeError) throw tradeError;
|
if (tradeError) throw tradeError;
|
||||||
|
|
||||||
// Add sender's cards
|
// Add user1's cards
|
||||||
const senderItems = senderCards.map((card) => ({
|
const user1Items = user1Cards.map((card) => ({
|
||||||
trade_id: trade.id,
|
trade_id: trade.id,
|
||||||
owner_id: senderId,
|
owner_id: user1Id,
|
||||||
card_id: card.cardId,
|
card_id: card.cardId,
|
||||||
quantity: card.quantity,
|
quantity: card.quantity,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Add receiver's cards (what sender wants)
|
// Add user2's cards
|
||||||
const receiverItems = receiverCards.map((card) => ({
|
const user2Items = user2Cards.map((card) => ({
|
||||||
trade_id: trade.id,
|
trade_id: trade.id,
|
||||||
owner_id: receiverId,
|
owner_id: user2Id,
|
||||||
card_id: card.cardId,
|
card_id: card.cardId,
|
||||||
quantity: card.quantity,
|
quantity: card.quantity,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const allItems = [...senderItems, ...receiverItems];
|
const allItems = [...user1Items, ...user2Items];
|
||||||
|
|
||||||
if (allItems.length > 0) {
|
if (allItems.length > 0) {
|
||||||
const { error: itemsError } = await supabase
|
const { error: itemsError } = await supabase
|
||||||
@@ -199,11 +200,11 @@ export async function getTradeHistory(userId: string): Promise<Trade[]> {
|
|||||||
.from('trades')
|
.from('trades')
|
||||||
.select(`
|
.select(`
|
||||||
*,
|
*,
|
||||||
sender:profiles!trades_sender_id_fkey(username),
|
user1:profiles!trades_user1_id_fkey(username),
|
||||||
receiver:profiles!trades_receiver_id_fkey(username),
|
user2:profiles!trades_user2_id_fkey(username),
|
||||||
items:trade_items(*)
|
items:trade_items(*)
|
||||||
`)
|
`)
|
||||||
.or(`sender_id.eq.${userId},receiver_id.eq.${userId}`)
|
.or(`user1_id.eq.${userId},user2_id.eq.${userId}`)
|
||||||
.in('status', ['accepted', 'declined', 'cancelled'])
|
.in('status', ['accepted', 'declined', 'cancelled'])
|
||||||
.order('updated_at', { ascending: false })
|
.order('updated_at', { ascending: false })
|
||||||
.limit(50);
|
.limit(50);
|
||||||
@@ -214,12 +215,12 @@ export async function getTradeHistory(userId: string): Promise<Trade[]> {
|
|||||||
|
|
||||||
// Update an existing trade (for edits and counter-offers)
|
// Update an existing trade (for edits and counter-offers)
|
||||||
export async function updateTrade(params: UpdateTradeParams): Promise<Trade> {
|
export async function updateTrade(params: UpdateTradeParams): Promise<Trade> {
|
||||||
const { tradeId, editorId, message, senderCards, receiverCards } = params;
|
const { tradeId, editorId, message, myCards, theirCards } = params;
|
||||||
|
|
||||||
// Get current trade info
|
// Get current trade info
|
||||||
const { data: currentTrade, error: tradeError } = await supabase
|
const { data: currentTrade, error: tradeError } = await supabase
|
||||||
.from('trades')
|
.from('trades')
|
||||||
.select('version, sender_id, receiver_id')
|
.select('version, user1_id, user2_id')
|
||||||
.eq('id', tradeId)
|
.eq('id', tradeId)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
@@ -227,6 +228,11 @@ export async function updateTrade(params: UpdateTradeParams): Promise<Trade> {
|
|||||||
|
|
||||||
const newVersion = (currentTrade.version || 1) + 1;
|
const newVersion = (currentTrade.version || 1) + 1;
|
||||||
|
|
||||||
|
// Determine the other user's ID
|
||||||
|
const otherUserId = currentTrade.user1_id === editorId
|
||||||
|
? currentTrade.user2_id
|
||||||
|
: currentTrade.user1_id;
|
||||||
|
|
||||||
// Save current state to history before updating
|
// Save current state to history before updating
|
||||||
const { data: historyEntry, error: historyError } = await supabase
|
const { data: historyEntry, error: historyError } = await supabase
|
||||||
.from('trade_history')
|
.from('trade_history')
|
||||||
@@ -276,22 +282,22 @@ export async function updateTrade(params: UpdateTradeParams): Promise<Trade> {
|
|||||||
// Delete existing items
|
// Delete existing items
|
||||||
await supabase.from('trade_items').delete().eq('trade_id', tradeId);
|
await supabase.from('trade_items').delete().eq('trade_id', tradeId);
|
||||||
|
|
||||||
// Add new items
|
// Add new items (myCards belong to editor, theirCards belong to other user)
|
||||||
const senderItems = senderCards.map((card) => ({
|
const myItems = myCards.map((card) => ({
|
||||||
trade_id: tradeId,
|
trade_id: tradeId,
|
||||||
owner_id: currentTrade.sender_id,
|
owner_id: editorId,
|
||||||
card_id: card.cardId,
|
card_id: card.cardId,
|
||||||
quantity: card.quantity,
|
quantity: card.quantity,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const receiverItems = receiverCards.map((card) => ({
|
const theirItems = theirCards.map((card) => ({
|
||||||
trade_id: tradeId,
|
trade_id: tradeId,
|
||||||
owner_id: currentTrade.receiver_id,
|
owner_id: otherUserId,
|
||||||
card_id: card.cardId,
|
card_id: card.cardId,
|
||||||
quantity: card.quantity,
|
quantity: card.quantity,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const allItems = [...senderItems, ...receiverItems];
|
const allItems = [...myItems, ...theirItems];
|
||||||
|
|
||||||
if (allItems.length > 0) {
|
if (allItems.length > 0) {
|
||||||
const { error: itemsError } = await supabase
|
const { error: itemsError } = await supabase
|
||||||
|
|||||||
Reference in New Issue
Block a user