From b62e4b4551f71375e8b72af0c46e82363336bedd Mon Sep 17 00:00:00 2001 From: walexjnr Date: Sat, 17 Oct 2026 09:53:22 +0100 Subject: [PATCH 1/4] Remove unused deps: jspdf, qrcode, @types/recharts --- frontend/package.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index dc092177..32223d16 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,10 +22,8 @@ "axios": "^1.13.2", "clsx": "^2.1.1", "date-fns": "^4.1.0", - "jspdf": "^4.0.0", "lucide-react": "^0.562.0", "next": "15.5.9", - "qrcode": "^1.5.4", "react": "19.1.0", "react-dom": "19.1.0", "react-dropzone": "^14.3.8", @@ -45,11 +43,9 @@ "@testing-library/react": "^16.3.2", "@types/jest": "^30.0.0", "@types/node": "^20", - "@types/qrcode": "^1.5.6", "@types/react": "^19", "@playwright/test": "^1.49.1", "@types/react-dom": "^19", - "@types/recharts": "^1.8.29", "autoprefixer": "^10.4.23", "eslint": "^9", "eslint-config-next": "15.5.4", @@ -60,4 +56,4 @@ "ts-jest": "^29.4.4", "typescript": "^5" } -} +} \ No newline at end of file From c0e2e52dd94d6b517e0b324fb5a864d32d112a68 Mon Sep 17 00:00:00 2001 From: walexjnr Date: Sat, 17 Oct 2026 09:53:25 +0100 Subject: [PATCH 2/4] Add reset-password page to complete forgot-password flow --- frontend/app/(auth)/reset-password/page.tsx | 81 +++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 frontend/app/(auth)/reset-password/page.tsx diff --git a/frontend/app/(auth)/reset-password/page.tsx b/frontend/app/(auth)/reset-password/page.tsx new file mode 100644 index 00000000..d370bf4f --- /dev/null +++ b/frontend/app/(auth)/reset-password/page.tsx @@ -0,0 +1,81 @@ +'use client'; + +import { useState } from 'react'; +import { useSearchParams, useRouter } from 'next/navigation'; +import { z } from 'zod'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { Input } from '@/components/ui/input'; +import { Button } from '@/components/ui/button'; +import { api } from '@/lib/api'; + +const schema = z.object({ + password: z.string().min(8, 'Password must be at least 8 characters'), + confirmPassword: z.string().min(8, 'Please confirm your password'), +}).refine((data) => data.password === data.confirmPassword, { + message: 'Passwords do not match', + path: ['confirmPassword'], +}); + +type FormValues = z.infer; + +export default function ResetPasswordPage() { + const searchParams = useSearchParams(); + const router = useRouter(); + const token = searchParams.get('token') || ''; + const [success, setSuccess] = useState(false); + const [error, setError] = useState(''); + const [loading, setLoading] = useState(false); + + const { register, handleSubmit, formState: { errors } } = useForm({ resolver: zodResolver(schema) }); + + const onSubmit = async (values: FormValues) => { + setError(''); + setLoading(true); + try { + await api.post('/auth/reset-password', { token, password: values.password }); + setSuccess(true); + setTimeout(() => router.push('/login'), 3000); + } catch (err: any) { + const msg = err?.response?.data?.message; + setError(msg || 'Invalid or expired token. Please request a new reset link.'); + } finally { + setLoading(false); + } + }; + + if (!token) { + return ( + <> +

Reset Password

+

+ No reset token found. Please request a new password reset link. +

+ + ); + } + + if (success) { + return ( + <> +

Password Reset

+
+ Your password has been reset successfully. Redirecting to login... +
+ + ); + } + + return ( + <> +

Reset your password

+

Enter a new password for your account.

+
+ + + {error &&

{error}

} + +
+ + ); +} \ No newline at end of file From b73cd17fe45375b4d03e1df5fd3bbb1bed638b41 Mon Sep 17 00:00:00 2001 From: walexjnr Date: Sat, 17 Oct 2026 09:53:29 +0100 Subject: [PATCH 3/4] Add OnChainTab component for tokenization, governance and dividends panels --- frontend/components/assets/on-chain-tab.tsx | 40 +++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 frontend/components/assets/on-chain-tab.tsx diff --git a/frontend/components/assets/on-chain-tab.tsx b/frontend/components/assets/on-chain-tab.tsx new file mode 100644 index 00000000..c06e8178 --- /dev/null +++ b/frontend/components/assets/on-chain-tab.tsx @@ -0,0 +1,40 @@ +"use client"; + +import { useState } from "react"; +import { Wallet } from "lucide-react"; +import { useWalletStore } from "@/store/wallet.store"; +import { TokenizationPanel } from "@/components/tokenization/tokenization-panel"; +import { GovernancePanel } from "@/components/governance/governance-panel"; +import { DividendsPanel } from "@/components/dividends/dividends-panel"; + +type Tab = "tokenization" | "governance" | "dividends"; + +export function OnChainTab({ assetId }: { assetId: string }) { + const [tab, setTab] = useState("tokenization"); + const { isConnected } = useWalletStore(); + + if (!isConnected) { + return ( +
+ +

Connect a Freighter wallet to view on-chain features.

+
+ ); + } + + return ( +
+
+ {(["tokenization", "governance", "dividends"] as const).map((t) => ( + + ))} +
+ {tab === "tokenization" && } + {tab === "governance" && } + {tab === "dividends" && } +
+ ); +} \ No newline at end of file From 894afce146ac371ea27dd5dba7394bae52f00fea Mon Sep 17 00:00:00 2001 From: walexjnr Date: Sat, 17 Oct 2026 10:01:01 +0100 Subject: [PATCH 4/4] Add useAssetQr hook for authenticated QR code fetch --- frontend/hooks/useAssetQr.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 frontend/hooks/useAssetQr.ts diff --git a/frontend/hooks/useAssetQr.ts b/frontend/hooks/useAssetQr.ts new file mode 100644 index 00000000..35e4b947 --- /dev/null +++ b/frontend/hooks/useAssetQr.ts @@ -0,0 +1,28 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { api } from '@/lib/api'; + +export function useAssetQr(assetId: string) { + const [qrUrl, setQrUrl] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(false); + + useEffect(() => { + let cancelled = false; + setLoading(true); + api + .get(`/assets/${assetId}/qr`, { responseType: 'blob' }) + .then((r) => { + if (cancelled) return; + const url = URL.createObjectURL(r.data); + setQrUrl(url); + }) + .catch(() => { if (!cancelled) setError(true); }) + .finally(() => { if (!cancelled) setLoading(false); }); + + return () => { cancelled = true; }; + }, [assetId]); + + return { qrUrl, loading, error }; +} \ No newline at end of file