Skip to content
This repository was archived by the owner on Nov 11, 2025. It is now read-only.
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
Original file line number Diff line number Diff line change
Expand Up @@ -144,18 +144,20 @@ export default function ProposalDetailsPage() {
<>
<section>
<h2 className="text-lg font-semibold mb-4">
Informações da Proposta
{t('proposalDetails.title')}
</h2>
<p className="text-muted-foreground">{proposal.description}</p>
<div className="grid grid-cols-2 gap-4 mt-4">
<div>
<p className="text-sm text-muted-foreground">
Item para Troca
{t('proposalDetails.swapWith')}
</p>
<p className="font-bold">{proposal.swap_with}</p>
</div>
<div className="text-right">
<p className="text-sm text-muted-foreground">Proposta De</p>
<p className="text-sm text-muted-foreground">
{t('proposalDetails.proposalFrom')}
</p>
{senderProfile && (
<ProfileHoverCard profile={senderProfile} />
)}
Expand All @@ -165,7 +167,9 @@ export default function ProposalDetailsPage() {

{proposalImages.length > 0 && (
<section className="mt-6">
<h2 className="text-lg font-semibold mb-4">Imagens em Anexo</h2>
<h2 className="text-lg font-semibold mb-4">
{t('proposalDetails.proposalImages')}
</h2>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{proposalImages.map((imageUrl, index) => (
<div
Expand All @@ -184,7 +188,9 @@ export default function ProposalDetailsPage() {
sizes="(max-width: 768px) 50vw, (max-width: 1200px) 25vw, 20vw"
/>
<div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center">
<span className="text-white text-sm">Ver imagem</span>
<span className="text-white text-sm">
{t('proposalDetails.viewImage')}
</span>
</div>
</div>
))}
Expand All @@ -198,18 +204,22 @@ export default function ProposalDetailsPage() {
return (
<section>
<h2 className="text-lg font-semibold mb-4">
Informações da Proposta
{t('proposalDetails.title')}
</h2>
<p className="text-muted-foreground">{proposal.description}</p>
<div className="grid grid-cols-2 gap-4 mt-4">
<div>
<p className="text-sm text-muted-foreground">Valor Proposto</p>
<p className="text-sm text-muted-foreground">
{t('proposalDetails.offeredPrice')}
</p>
<p className="font-bold">
€{proposal.offered_price?.toFixed(2) || '0.00'}
</p>
</div>
<div className="text-right">
<p className="text-sm text-muted-foreground">Proposta De</p>
<p className="text-sm text-muted-foreground">
{t('proposalDetails.proposalFrom')}
</p>
{senderProfile && <ProfileHoverCard profile={senderProfile} />}
</div>
</div>
Expand All @@ -220,18 +230,22 @@ export default function ProposalDetailsPage() {
return (
<section>
<h2 className="text-lg font-semibold mb-4">
Informações da Proposta
{t('proposalDetails.title')}
</h2>
<p className="text-muted-foreground">{proposal.description}</p>
<div className="grid grid-cols-2 gap-4 mt-4">
<div>
<p className="text-sm text-muted-foreground">Valor por Dia</p>
<p className="text-sm text-muted-foreground">
{t('proposalDetails.valuePerDay')}
</p>
<p className="font-bold">
€{proposal.offered_rent_per_day?.toFixed(2) || '0.00'}
</p>
</div>
<div>
<p className="text-sm text-muted-foreground">Período</p>
<p className="text-sm text-muted-foreground">
{t('proposalDetails.rentalPeriod')}
</p>
<p className="font-medium">
{proposal.start_date &&
onlyDayMonthYear(proposal.start_date.toString())}{' '}
Expand All @@ -245,12 +259,12 @@ export default function ProposalDetailsPage() {
proposal.offered_rent_per_day && (
<div>
<p className="text-sm text-muted-foreground">
Valor Total (
{t('proposalDetails.totalValue')} (
{calculateTotalRentalDays(
proposal.start_date.toString(),
proposal.end_date.toString(),
)}{' '}
dias)
{t('proposalDetails.days')})
</p>
<p className="font-bold text-lg">
Expand All @@ -264,7 +278,9 @@ export default function ProposalDetailsPage() {
</div>
)}
<div className="text-right">
<p className="text-sm text-muted-foreground">Proposta De</p>
<p className="text-sm text-muted-foreground">
{t('proposalDetails.proposalFrom')}
</p>
{senderProfile && <ProfileHoverCard profile={senderProfile} />}
</div>
</div>
Expand All @@ -275,11 +291,13 @@ export default function ProposalDetailsPage() {
return (
<section>
<h2 className="text-lg font-semibold mb-4">
Informações da Proposta
{t('proposalDetails.title')}
</h2>
<p className="text-muted-foreground">{proposal.description}</p>
<div className="text-right mt-4">
<p className="text-sm text-muted-foreground">Proposta De</p>
<p className="text-sm text-muted-foreground">
{t('proposalDetails.proposalFrom')}
</p>
{senderProfile && <ProfileHoverCard profile={senderProfile} />}
</div>
</section>
Expand Down Expand Up @@ -352,12 +370,16 @@ export default function ProposalDetailsPage() {

{/* Listing Information */}
<section>
<h2 className="text-lg font-semibold mb-4">Informações do Anúncio</h2>
<h2 className="text-lg font-semibold mb-4">
{t('proposalDetails.listingInfo.title')}
</h2>
{listing && (
<ListingCardSmall listing={transformToListingBasic(listing)} />
)}
<div className="mt-4 text-right">
<p className="text-sm text-muted-foreground">Anúncio De</p>
<p className="text-sm text-muted-foreground">
{t('proposalDetails.listingInfo.listingFrom')}
</p>
{receiverProfile && <ProfileHoverCard profile={receiverProfile} />}
</div>
</section>
Expand All @@ -372,14 +394,14 @@ export default function ProposalDetailsPage() {
className="flex-1 bg-brand-500"
onClick={handleAcceptProposal}
>
Aceitar Proposta
{t('proposalDetails.actionButtons.acceptProposal')}
</Button>
<Button
variant="destructive"
className="flex-1"
onClick={handleRejectProposal}
>
Rejeitar Proposta
{t('proposalDetails.actionButtons.rejectProposal')}
</Button>
{listing &&
(listing.listing_type === 'sale' ? (
Expand All @@ -394,7 +416,10 @@ export default function ProposalDetailsPage() {
onSubmit={handleCounterProposal}
trigger={
<Button variant="outline" className="flex-1">
↺ Contra Proposta
↺{' '}
{t(
'proposalDetails.actionButtons.makeCounterProposal',
)}
</Button>
}
receiver_id={proposal.sender_id}
Expand All @@ -408,12 +433,15 @@ export default function ProposalDetailsPage() {
(listing as RentalListing).cost_per_day,
),
image: listing.image_url,
condition: 'good', // Rental listings don't have condition
condition: '',
}}
onSubmit={handleCounterProposal}
trigger={
<Button variant="outline" className="flex-1">
↺ Contra Proposta
↺{' '}
{t(
'proposalDetails.actionButtons.makeCounterProposal',
)}
</Button>
}
receiver_id={proposal.sender_id}
Expand All @@ -425,12 +453,15 @@ export default function ProposalDetailsPage() {
title: listing.title,
price: 0,
image: listing.image_url,
condition: 'good',
condition: '',
}}
onSubmit={handleCounterProposal}
trigger={
<Button variant="outline" className="flex-1">
↺ Contra Proposta
↺{' '}
{t(
'proposalDetails.actionButtons.makeCounterProposal',
)}
</Button>
}
receiver_id={proposal.sender_id}
Expand All @@ -442,12 +473,15 @@ export default function ProposalDetailsPage() {
title: listing.title,
price: 0,
image: listing.image_url,
condition: 'good',
condition: '',
}}
onSubmit={handleCounterProposal}
trigger={
<Button variant="outline" className="flex-1">
↺ Contra Proposta
↺{' '}
{t(
'proposalDetails.actionButtons.makeCounterProposal',
)}
</Button>
}
receiver_id={proposal.sender_id}
Expand Down Expand Up @@ -497,6 +531,7 @@ function ProposalDetailsSkeleton() {
}

function ErrorState({ error }: { error: string | null }) {
const t = useTranslations('proposals.proposalDetails');
return (
<div className="text-center py-12">
<h3 className="text-lg font-medium text-destructive">
Expand All @@ -507,7 +542,7 @@ function ErrorState({ error }: { error: string | null }) {
className="mt-4"
onClick={() => window.location.reload()}
>
Try Again
{t('proposalDetails.tryAgain')}
</Button>
</div>
);
Expand Down
17 changes: 11 additions & 6 deletions frontend/vizzy/components/proposals/cancel-proposal-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Button } from '@/components/ui/common/button';
import { Trash2 } from 'lucide-react';
import { cancelProposal } from '@/lib/api/proposals/cancel-proposal';
import { toast } from 'sonner';
import { useTranslations } from 'next-intl';

interface CancelProposalDialogProps {
proposalId: number;
Expand All @@ -25,6 +26,7 @@ export function CancelProposalDialog({
proposalId,
onConfirm,
}: CancelProposalDialogProps) {
const t = useTranslations('proposals');
console.log('Dialog opened with proposalId:', proposalId);
const handleCancel = async () => {
try {
Expand All @@ -51,21 +53,24 @@ export function CancelProposalDialog({
<AlertDialogTrigger asChild>
<Button variant="destructive" className="w-1/4">
<Trash2 className="h-4 w-4 mr-2" />
Cancelar Proposta
{t('proposalDetails.cancelDialog.title')}
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Cancelar Proposta</AlertDialogTitle>
<AlertDialogTitle>
{t('proposalDetails.cancelDialog.title')}
</AlertDialogTitle>
<AlertDialogDescription>
Tem a certeza que pretende cancelar esta proposta? Esta ação não
pode ser revertida.
{t('proposalDetails.cancelDialog.description')}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Voltar</AlertDialogCancel>
<AlertDialogCancel>
{t('proposalDetails.cancelDialog.goBack')}
</AlertDialogCancel>
<AlertDialogAction onClick={handleCancel}>
Confirmar
{t('proposalDetails.cancelDialog.confirm')}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
Expand Down
31 changes: 31 additions & 0 deletions frontend/vizzy/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,37 @@
"title": "Total Revenue",
"description": "What you've earned so far!",
"error": "Failed to load balance"
},
"proposalDetails": {
"title": "Proposal Details",
"description": "Proposal description",
"swapWith": "Swap for",
"proposalFrom": "Proposal from",
"proposalTo": "Proposal to",
"proposalImages": "Attached Images",
"viewImage": "View Image",
"offeredPrice": "Offered Price",
"rentalPeriod": "Rental Period",
"valuePerDay": "Value per Day",
"totalValue": "Total Value",
"days": "days",
"listingInfo": {
"title": "Listing Information",
"listingFrom": "Listing from"
},
"actionButtons": {
"acceptProposal": "Accept Proposal",
"rejectProposal": "Reject Proposal",
"makeCounterProposal": "Make Counter Proposal"
},
"tryAgain": "Try Again",
"cancelDialog": {
"title": "Cancel Proposal",
"description": "Are you sure you want to cancel this proposal?",
"cancel": "Cancel",
"confirm": "Confirm",
"goBack": "Go Back"
}
}
}
}
31 changes: 31 additions & 0 deletions frontend/vizzy/messages/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,37 @@
"title": "Receita Total",
"description": "O que já ganhaste até agora!",
"error": "Erro ao carregar o saldo"
},
"proposalDetails": {
"title": "Informações da Proposta",
"description": "Descrição da proposta",
"swapWith": "Para trocar por",
"proposalFrom": "Proposta de",
"proposalTo": "Para",
"proposalImages": "Imagens em Anexo",
"viewImage": "Ver Imagem",
"offeredPrice": "Valor Proposto",
"rentalPeriod": "Período de Aluguer",
"valuePerDay": "Valor por Dia",
"totalValue": "Valor Total",
"days": "dias",
"listingInfo": {
"title": "Informações do Anúncio",
"listingFrom": "Anúncio de"
},
"actionButtons": {
"acceptProposal": "Aceitar Proposta",
"rejectProposal": "Rejeitar Proposta",
"makeCounterProposal": "Fazer Contra-Proposta"
},
"tryAgain": "Tentar Novamente",
"cancelDialog": {
"title": "Cancelar Proposta",
"description": "Tem a certeza que deseja cancelar esta proposta?",
"cancel": "Cancelar",
"confirm": "Confirmar",
"goBack": "Voltar"
}
}
}
}