feat: add user authentication UI to header component
Add login and register dialogs, user dropdown menu with profile link and logout option, and integrate with session management hook. Prevent hydration mismatches by conditionally rendering auth elements after mount. Update package-lock.json with new @esbuild dependencies for build tooling.
This commit is contained in:
1 parent
e641138887
commit
55eebde0ba
55 files changed
+7353
-7
No files matched your search
@@ -0,0 +1,241 @@
|
||||
/**
|
||||
* Queries de transactions (transações do sistema de escrow)
|
||||
* Operações para a tabela transactions
|
||||
*/
|
||||
|
||||
import { query } from "../index";
|
||||
|
||||
export interface Transaction {
|
||||
transaction_id: number;
|
||||
listing_id: number | null;
|
||||
buyer_id: number;
|
||||
seller_id: number;
|
||||
item_name: string;
|
||||
quantity: number;
|
||||
server_name: string;
|
||||
amount: number;
|
||||
payment_method: "pix" | "tibia_coins" | "gold_coins" | null;
|
||||
escrow_fee: number | null;
|
||||
transaction_status:
|
||||
| "pending"
|
||||
| "deposited"
|
||||
| "item_delivered"
|
||||
| "completed"
|
||||
| "disputed"
|
||||
| "cancelled"
|
||||
| "refunded";
|
||||
created_at: Date;
|
||||
deposited_at: Date | null;
|
||||
delivered_at: Date | null;
|
||||
completed_at: Date | null;
|
||||
buyer_confirmed: boolean;
|
||||
seller_confirmed: boolean;
|
||||
buyer_notes: string | null;
|
||||
seller_notes: string | null;
|
||||
}
|
||||
|
||||
export interface CreateTransactionData {
|
||||
listing_id?: number;
|
||||
buyer_id: number;
|
||||
seller_id: number;
|
||||
item_name: string;
|
||||
quantity: number;
|
||||
server_name: string;
|
||||
amount: number;
|
||||
payment_method: "pix" | "tibia_coins" | "gold_coins";
|
||||
escrow_fee?: number;
|
||||
}
|
||||
|
||||
export interface UpdateTransactionStatusData {
|
||||
transaction_status: Transaction["transaction_status"];
|
||||
deposited_at?: Date;
|
||||
delivered_at?: Date;
|
||||
completed_at?: Date;
|
||||
buyer_confirmed?: boolean;
|
||||
seller_confirmed?: boolean;
|
||||
buyer_notes?: string;
|
||||
seller_notes?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cria uma nova transação
|
||||
*/
|
||||
export async function createTransaction(
|
||||
data: CreateTransactionData
|
||||
): Promise<Transaction> {
|
||||
// Calcular escrow_fee se não fornecido (2% do valor)
|
||||
const escrowFee = data.escrow_fee ?? Number((data.amount * 0.02).toFixed(2));
|
||||
|
||||
const result = await query<Transaction>(
|
||||
`
|
||||
INSERT INTO transactions (
|
||||
listing_id, buyer_id, seller_id, item_name, quantity,
|
||||
server_name, amount, payment_method, escrow_fee
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
RETURNING *
|
||||
`,
|
||||
[
|
||||
data.listing_id || null,
|
||||
data.buyer_id,
|
||||
data.seller_id,
|
||||
data.item_name,
|
||||
data.quantity,
|
||||
data.server_name,
|
||||
data.amount,
|
||||
data.payment_method,
|
||||
escrowFee,
|
||||
]
|
||||
);
|
||||
|
||||
return result.rows[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Busca transação por ID
|
||||
*/
|
||||
export async function getTransactionById(
|
||||
transactionId: number
|
||||
): Promise<Transaction | null> {
|
||||
const result = await query<Transaction>(
|
||||
`
|
||||
SELECT * FROM transactions WHERE transaction_id = $1
|
||||
`,
|
||||
[transactionId]
|
||||
);
|
||||
|
||||
return result.rows[0] || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Atualiza status da transação
|
||||
*/
|
||||
export async function updateTransactionStatus(
|
||||
transactionId: number,
|
||||
data: UpdateTransactionStatusData
|
||||
): Promise<Transaction | null> {
|
||||
const updates: string[] = [];
|
||||
const params: any[] = [];
|
||||
let paramIndex = 1;
|
||||
|
||||
updates.push(`transaction_status = $${paramIndex}`);
|
||||
params.push(data.transaction_status);
|
||||
paramIndex++;
|
||||
|
||||
if (data.deposited_at !== undefined) {
|
||||
updates.push(`deposited_at = $${paramIndex}`);
|
||||
params.push(data.deposited_at);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
if (data.delivered_at !== undefined) {
|
||||
updates.push(`delivered_at = $${paramIndex}`);
|
||||
params.push(data.delivered_at);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
if (data.completed_at !== undefined) {
|
||||
updates.push(`completed_at = $${paramIndex}`);
|
||||
params.push(data.completed_at);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
if (data.buyer_confirmed !== undefined) {
|
||||
updates.push(`buyer_confirmed = $${paramIndex}`);
|
||||
params.push(data.buyer_confirmed);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
if (data.seller_confirmed !== undefined) {
|
||||
updates.push(`seller_confirmed = $${paramIndex}`);
|
||||
params.push(data.seller_confirmed);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
if (data.buyer_notes !== undefined) {
|
||||
updates.push(`buyer_notes = $${paramIndex}`);
|
||||
params.push(data.buyer_notes);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
if (data.seller_notes !== undefined) {
|
||||
updates.push(`seller_notes = $${paramIndex}`);
|
||||
params.push(data.seller_notes);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
params.push(transactionId);
|
||||
const result = await query<Transaction>(
|
||||
`
|
||||
UPDATE transactions
|
||||
SET ${updates.join(", ")}
|
||||
WHERE transaction_id = $${paramIndex}
|
||||
RETURNING *
|
||||
`,
|
||||
params
|
||||
);
|
||||
|
||||
return result.rows[0] || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Busca transações de um usuário (comprador ou vendedor)
|
||||
*/
|
||||
export async function getTransactionsByUser(
|
||||
userId: number,
|
||||
filters: {
|
||||
role?: "buyer" | "seller" | "both";
|
||||
status?: Transaction["transaction_status"];
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
} = {}
|
||||
): Promise<{ transactions: Transaction[]; total: number }> {
|
||||
const { role = "both", status, page = 1, pageSize = 20 } = filters;
|
||||
|
||||
const conditions: string[] = [];
|
||||
const params: any[] = [];
|
||||
let paramIndex = 1;
|
||||
|
||||
if (role === "buyer") {
|
||||
conditions.push(`buyer_id = $${paramIndex}`);
|
||||
} else if (role === "seller") {
|
||||
conditions.push(`seller_id = $${paramIndex}`);
|
||||
} else {
|
||||
conditions.push(`(buyer_id = $${paramIndex} OR seller_id = $${paramIndex})`);
|
||||
}
|
||||
params.push(userId);
|
||||
paramIndex++;
|
||||
|
||||
if (status) {
|
||||
conditions.push(`transaction_status = $${paramIndex}`);
|
||||
params.push(status);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
||||
|
||||
// Contar total
|
||||
const countResult = await query<{ count: string }>(
|
||||
`SELECT COUNT(*) as count FROM transactions ${whereClause}`,
|
||||
params
|
||||
);
|
||||
const total = parseInt(countResult.rows[0].count, 10);
|
||||
|
||||
// Buscar transações
|
||||
const offset = (page - 1) * pageSize;
|
||||
const result = await query<Transaction>(
|
||||
`
|
||||
SELECT * FROM transactions
|
||||
${whereClause}
|
||||
ORDER BY created_at DESC
|
||||
LIMIT $${paramIndex} OFFSET $${paramIndex + 1}
|
||||
`,
|
||||
[...params, pageSize, offset]
|
||||
);
|
||||
|
||||
return {
|
||||
transactions: result.rows,
|
||||
total,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user