Skip to content
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
22 changes: 11 additions & 11 deletions components/mobile/MobileFooter.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client'
'use client';

import React from 'react'
import Link from 'next/link'
import React from 'react';
import Link from 'next/link';

// Navigation links data
const footerLinks = [
Expand All @@ -13,30 +13,30 @@ const footerLinks = [
{ label: 'Terms of Service', href: '/terms' },
{ label: 'Contact', href: '/contact' },
{ label: 'FAQs', href: '/faq' },
]
];

// Social icons data
const socialLinks = [
{ label: 'Twitter', href: 'https://twitter.com/swiftchain', icon: '🐦' },
{ label: 'GitHub', href: 'https://github.com/swiftchain', icon: '🐙' },
{ label: 'LinkedIn', href: 'https://linkedin.com/company/swiftchain', icon: '🔗' },
{ label: 'Discord', href: 'https://discord.gg/swiftchain', icon: '💬' },
]
];

export function MobileFooter() {
const currentYear = new Date().getFullYear()
const currentYear = new Date().getFullYear();

return (
<footer className="border-t border-gray-200 bg-white px-4 py-8 dark:border-gray-800 dark:bg-gray-950">
<div className="mx-auto max-w-md">
{/* Navigation links - 2x2 grid on mobile */}
{/* Navigation links - 2x4 grid on mobile */}
<nav className="mb-6 grid grid-cols-2 gap-2" aria-label="Footer navigation">
{footerLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className="rounded-lg px-3 py-2.5 text-sm text-gray-600 transition-colors hover:bg-gray-100 hover:text-gray-900 active:bg-gray-200 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-100 dark:active:bg-gray-700"
style={{ minHeight: '44px' }}
style={{ minHeight: 44 }}
>
{link.label}
</Link>
Expand All @@ -53,7 +53,7 @@ export function MobileFooter() {
rel="noopener noreferrer"
className="flex h-11 w-11 items-center justify-center rounded-full text-xl transition-colors hover:bg-gray-100 hover:text-gray-900 dark:hover:bg-gray-800 dark:hover:text-gray-100"
aria-label={social.label}
style={{ minHeight: '44px', minWidth: '44px' }}
style={{ minHeight: 44, minWidth: 44 }}
>
<span className="sr-only">{social.label}</span>
<span role="img" aria-hidden="true">
Expand All @@ -69,7 +69,7 @@ export function MobileFooter() {
</p>
</div>
</footer>
)
);
}

export default MobileFooter
export default MobileFooter;
3 changes: 2 additions & 1 deletion components/providers/ToastProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ export default function ToastProvider({
* Handle toast display based on configuration
*/
const handleToast = (config: ToastConfig): void => {
const { variant, message, description, duration, action } = config;
const { variant, description, duration, action } = config;
const message = config.message ?? config.title ?? '';

switch (variant) {
case 'success':
Expand Down
2 changes: 1 addition & 1 deletion hooks/useCreateDelivery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export function useCreateDelivery(): UseCreateDeliveryReturn {

return {
form,
isSubmitting: createDelivery.isLoading,
isSubmitting: createDelivery.isPending,
isSuccess: createDelivery.isSuccess,
onSubmit,
};
Expand Down
7 changes: 0 additions & 7 deletions hooks/useDisputeForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,6 @@ export function useDisputeForm(): UseDisputeFormReturn {

const response = await escrowService.openDispute(params);

if (!response.success) {
const errorMessage = response.message ?? 'Failed to open dispute. Please try again.';
toast.error(errorMessage);
onError?.(errorMessage);
return;
}

toast.success(
`Dispute opened successfully! Dispute ID: ${response.disputeId}`
);
Expand Down
8 changes: 6 additions & 2 deletions hooks/useEscrowLock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { escrowService, LockEscrowParams } from '@/services/escrowService';

interface UseEscrowLockState {
isLoading: boolean;
isSuccess: boolean;
error: string | null;
escrowId: string | null;
transactionHash: string | null;
Expand All @@ -17,18 +18,20 @@ interface UseEscrowLockState {
export function useEscrowLock() {
const [state, setState] = useState<UseEscrowLockState>({
isLoading: false,
isSuccess: false,
error: null,
escrowId: null,
transactionHash: null,
});

const lockEscrow = useCallback(async (params: LockEscrowParams) => {
setState({ isLoading: true, error: null, escrowId: null, transactionHash: null });
setState({ isLoading: true, isSuccess: false, error: null, escrowId: null, transactionHash: null });

try {
const response = await escrowService.lockEscrow(params);
setState({
isLoading: false,
isSuccess: true,
error: null,
escrowId: response.escrowId,
transactionHash: response.transactionHash,
Expand All @@ -38,6 +41,7 @@ export function useEscrowLock() {
const errorMessage = err instanceof Error ? err.message : 'Failed to lock escrow';
setState({
isLoading: false,
isSuccess: false,
error: errorMessage,
escrowId: null,
transactionHash: null,
Expand All @@ -47,7 +51,7 @@ export function useEscrowLock() {
}, []);

const reset = useCallback(() => {
setState({ isLoading: false, error: null, escrowId: null, transactionHash: null });
setState({ isLoading: false, isSuccess: false, error: null, escrowId: null, transactionHash: null });
}, []);

return {
Expand Down
13 changes: 5 additions & 8 deletions hooks/useEscrowRelease.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useState, useCallback } from 'react';
import { toast } from 'sonner';
import { escrowService, EscrowDetails } from '@/services/escrowService';
import { escrowService } from '@/services/escrowService';
import type { EscrowDetails } from '@/types/escrow';

export type ReleaseStep = 'idle' | 'confirming' | 'signing' | 'releasing' | 'done';

Expand Down Expand Up @@ -33,12 +34,8 @@ export function useEscrowRelease(): UseEscrowReleaseReturn {
const fetchEscrowDetails = useCallback(async (escrowId: string) => {
setIsLoading(true);
try {
const response = await escrowService.getEscrowDetails(escrowId);
if (response.success && response.data) {
setEscrowDetails(response.data);
} else {
toast.error(response.message ?? 'Failed to load escrow details.');
}
const details = await escrowService.getEscrowDetails(escrowId);
setEscrowDetails(details);
} catch {
toast.error('Unable to fetch escrow details. Please try again.');
} finally {
Expand Down Expand Up @@ -82,7 +79,7 @@ export function useEscrowRelease(): UseEscrowReleaseReturn {
: 'Payment released successfully!'
);
setEscrowDetails((prev) =>
prev ? { ...prev, status: 'released' as const } : prev
prev ? { ...prev, isReleased: true } : prev
);
} catch (err: unknown) {
setStep('idle');
Expand Down
24 changes: 13 additions & 11 deletions hooks/useHandoffQR.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { shipmentHandoffService } from '../services/shipmentHandoffService';
import { HandoffQRData } from '../types/shipment';
import { useToast } from './useToast';
import { shipmentHandoffService } from '@/services/shipmentHandoffService';
import type { HandoffQRData } from '@/types/shipment';
import { useToast } from '@/hooks/useToast';

export function useHandoffQR(deliveryId: string, enabled: boolean = true) {
return useQuery<HandoffQRData, Error>({
Expand All @@ -16,7 +16,7 @@ export function useHandoffQR(deliveryId: string, enabled: boolean = true) {

export function useGenerateHandoffQR() {
const queryClient = useQueryClient();
const { success, error: showError } = useToast();
const { toast } = useToast();

return useMutation({
mutationFn: (deliveryId: string) =>
Expand All @@ -26,17 +26,18 @@ export function useGenerateHandoffQR() {
queryClient.invalidateQueries({
queryKey: ['handoffQR', data.deliveryId],
});
success?.('QR code generated successfully');
toast({ variant: 'success', message: 'QR code generated successfully' });
},
onError: () => {
showError?.('Failed to generate QR code');
onError: (error) => {
toast({ variant: 'error', message: 'Failed to generate QR code' });
console.error('QR generation error:', error);
},
});
}

export function useVerifyHandoffQR() {
const queryClient = useQueryClient();
const { success, error: showError } = useToast();
const { toast } = useToast();

return useMutation({
mutationFn: ({ deliveryId, token }: { deliveryId: string; token: string }) =>
Expand All @@ -49,10 +50,11 @@ export function useVerifyHandoffQR() {
queryClient.invalidateQueries({
queryKey: ['delivery', data.id],
});
success?.('Handoff verified successfully!');
toast({ variant: 'success', message: 'Handoff verified successfully!' });
},
onError: () => {
showError?.('Failed to verify handoff');
onError: (error) => {
toast({ variant: 'error', message: 'Failed to verify handoff' });
console.error('Handoff verification error:', error);
},
});
}
5 changes: 3 additions & 2 deletions hooks/useQrScanner.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { useState, useEffect, useRef, useCallback } from 'react';
import { useMutation } from '@tanstack/react-query';
import { qrScannerService } from '../services/qrScannerService';
import { qrScannerService } from '@/services/qrScannerService';

interface QrScannerHookResult {
error: string | null;
videoRef: React.RefObject<HTMLVideoElement | null>;
isCameraActive: boolean;
verifyMutation: ReturnType<typeof useMutation>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
verifyMutation: ReturnType<typeof useMutation<any, Error, string>>;
startCamera: () => Promise<void>;
stopCamera: () => void;

Expand Down
12 changes: 7 additions & 5 deletions hooks/useToast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,20 +104,22 @@ export function useToast(): UseToastReturn {
}, []);

const showToast = useCallback((config: ToastConfig) => {
const { message, description, variant, duration } = config;
const { title, message, description, variant = 'default', duration } = config;
// Support both `title` (component API) and `message` (service API)
const text = title ?? message ?? '';
switch (variant) {
case 'error':
toastService.error(message, description, duration);
toastService.error(text, description, duration);
break;
case 'loading':
toastService.loading(message, description);
toastService.loading(text, description);
break;
case 'success':
toastService.success(message, description, duration);
toastService.success(text, description, duration);
break;
case 'info':
default:
toastService.info(message, description, duration);
toastService.info(text, description, duration);
break;
}
}, []);
Expand Down
2 changes: 1 addition & 1 deletion hooks/useTxTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function useTxTracker(transactionHash: string | null) {
}

// Active polling: increase interval over time (exponential backoff)
const pollCount = (query.state.dataUpdatedAt - query.state.dataUndefinedAt) / POLL_INTERVAL;
const pollCount = Math.floor((Date.now() - query.state.dataUpdatedAt) / POLL_INTERVAL);
const interval = Math.min(POLL_INTERVAL * Math.pow(1.5, Math.floor(pollCount / 3)), MAX_POLL_INTERVAL);
return interval;
},
Expand Down
9 changes: 6 additions & 3 deletions lib/toast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@ const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL ?? '';
/**
* Toast variant types
*/
export type ToastVariant = 'success' | 'error' | 'info' | 'loading';
export type ToastVariant = 'success' | 'error' | 'info' | 'loading' | 'default' | 'destructive';

/**
* Toast configuration from backend
*/
export interface ToastConfig {
variant: ToastVariant;
message: string;
/** Short title shown in bold (alias for message when message is omitted) */
title?: string;
variant?: ToastVariant;
/** Main message body */
message?: string;
duration?: number;
description?: string;
action?: {
Expand Down
2 changes: 1 addition & 1 deletion next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/dev/types/routes.d.ts";
import "./.next/types/routes.d.ts";

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
7 changes: 6 additions & 1 deletion services/deliveries.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,10 @@ export const deliveriesService = {
createDelivery: async (payload: CreateDeliveryPayload): Promise<Delivery> => {
const { data } = await apiClient.post<Delivery>('/deliveries', payload);
return data;
}
},

getStatusTimeline: async (deliveryId: string): Promise<import('../types/delivery').StatusTimeline> => {
const { data } = await apiClient.get<import('../types/delivery').StatusTimeline>(`/deliveries/${deliveryId}/timeline`);
return data;
},
};
Loading
Loading