diff --git a/frontend/app/streams/[id]/page.tsx b/frontend/app/streams/[id]/page.tsx new file mode 100644 index 0000000..03f933a --- /dev/null +++ b/frontend/app/streams/[id]/page.tsx @@ -0,0 +1,142 @@ + +import { notFound } from "next/navigation"; +import LiveCounter from "@/components/Livecounter"; +import ProgressBar from "@/components/Progressbar"; + + +interface Transaction { + id: string; + date: string; + amount: number; + type: "withdrawal"; +} + +interface Stream { + id: string; + name: string; + recipient: string; + streamedAmount: number; + totalAmount: number; + transactions: Transaction[]; +} + +const MOCK_STREAMS: Record = { + "1": { + id: "1", + name: "Developer Grant — Q1", + recipient: "0xAbC…1234", + streamedAmount: 3200, + totalAmount: 5000, + transactions: [ + { id: "tx1", date: "2026-02-20", amount: 1000, type: "withdrawal" }, + { id: "tx2", date: "2026-02-18", amount: 1200, type: "withdrawal" }, + { id: "tx3", date: "2026-02-15", amount: 1000, type: "withdrawal" }, + ], + }, + "2": { + id: "2", + name: "Marketing Budget Stream", + recipient: "0xDeF…5678", + streamedAmount: 800, + totalAmount: 2000, + transactions: [ + { id: "tx1", date: "2026-02-21", amount: 500, type: "withdrawal" }, + { id: "tx2", date: "2026-02-19", amount: 300, type: "withdrawal" }, + ], + }, +}; + +interface PageProps { + params: Promise<{ id: string }>; +} + +export default async function StreamDetailsPage({ params }: PageProps) { + const { id } = await params; + const stream = MOCK_STREAMS[id]; + + if (!stream) notFound(); + + const percentage = Math.round((stream.streamedAmount / stream.totalAmount) * 100); + + return ( +
+
+ + {/* Header */} +
+

Stream #{stream.id}

+

+ Stream Details +

+
+ + {/* Identity card */} +
+

{stream.name}

+

+ Recipient:{" "} + + {stream.recipient} + +

+
+ + {/* 1️⃣ Progress bar */} +
+
+

Streamed Progress

+
+ +
+ + {/* 3️⃣ Live counter */} +
+
+

Live Balance

+
+ +
+ + {/* 2️⃣ Transaction history */} +
+
+

Transaction History

+ {stream.transactions.length} withdrawals +
+ + {stream.transactions.length === 0 ? ( +
+

No transactions yet.

+
+ ) : ( +
    + {stream.transactions.map((tx) => ( +
  • +
    + Withdrawal +

    {tx.date}

    +
    + + -{tx.amount.toLocaleString()} tokens + +
  • + ))} +
+ )} +
+ +
+
+ ); +} \ No newline at end of file diff --git a/frontend/components/Livecounter.tsx b/frontend/components/Livecounter.tsx new file mode 100644 index 0000000..a49cb4a --- /dev/null +++ b/frontend/components/Livecounter.tsx @@ -0,0 +1,67 @@ +"use client"; + +import { useEffect, useState } from "react"; + +interface LiveCounterProps { + initial: number; + label?: string; +} + +export default function LiveCounter({ initial, label = "Live Streamed" }: LiveCounterProps) { + const [amount, setAmount] = useState(initial); + + useEffect(() => { + const interval = setInterval(() => { + setAmount((prev) => prev + 1); + }, 1000); + + return () => clearInterval(interval); + }, []); + + return ( +
+ + + + + +

+ {label}:{" "} + + {amount.toLocaleString()} + +

+
+ ); +} \ No newline at end of file diff --git a/frontend/components/Progressbar.tsx b/frontend/components/Progressbar.tsx new file mode 100644 index 0000000..22ba090 --- /dev/null +++ b/frontend/components/Progressbar.tsx @@ -0,0 +1,52 @@ +interface ProgressBarProps { + percentage: number; + label?: string; +} + +export default function ProgressBar({ percentage, label }: ProgressBarProps) { + const clamped = Math.min(100, Math.max(0, percentage)); + + return ( +
+ {label && ( +
+ {label} + {clamped}% +
+ )} + + {/* Track */} +
+
+
+
+ ); +} \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index 6b50e91..094fcd2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,6 +19,7 @@ "react-hot-toast": "^2.6.0" }, "devDependencies": { + "@eslint/eslintrc": "^3.3.5", "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/react": "^19", @@ -28,4 +29,4 @@ "tailwindcss": "^4", "typescript": "^5" } -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json index d4fa831..c7b1966 100644 --- a/package-lock.json +++ b/package-lock.json @@ -81,7 +81,6 @@ "version": "25.3.0", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.18.0" } @@ -285,7 +284,6 @@ "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -419,6 +417,7 @@ "react-hot-toast": "^2.6.0" }, "devDependencies": { + "@eslint/eslintrc": "^3.3.5", "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/react": "^19", @@ -501,7 +500,6 @@ "version": "7.29.0", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -753,8 +751,7 @@ "node_modules/@electric-sql/pglite": { "version": "0.3.15", "dev": true, - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/@electric-sql/pglite-socket": { "version": "0.0.20", @@ -1287,18 +1284,20 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.3", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.12.4", + "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", - "minimatch": "^3.1.2", + "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, "engines": { @@ -2020,7 +2019,6 @@ "version": "20.19.33", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -2054,7 +2052,6 @@ "version": "19.2.14", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -2161,7 +2158,6 @@ "version": "8.56.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", @@ -2413,7 +2409,6 @@ "version": "8.16.0", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2905,7 +2900,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -3392,8 +3386,7 @@ }, "node_modules/csstype": { "version": "3.2.3", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -4275,7 +4268,6 @@ "version": "9.39.3", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -4445,7 +4437,6 @@ "version": "2.32.0", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -4698,7 +4689,6 @@ "node_modules/express": { "version": "5.2.1", "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -5378,7 +5368,6 @@ "version": "4.11.4", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=16.9.0" } @@ -6424,7 +6413,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.3", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -7044,7 +7035,6 @@ "node_modules/pg": { "version": "8.18.0", "license": "MIT", - "peer": true, "dependencies": { "pg-connection-string": "^2.11.0", "pg-pool": "^3.11.0", @@ -7248,7 +7238,6 @@ "dev": true, "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@prisma/config": "7.4.1", "@prisma/dev": "0.20.0", @@ -7415,7 +7404,6 @@ "node_modules/react": { "version": "19.2.4", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -7423,7 +7411,6 @@ "node_modules/react-dom": { "version": "19.2.4", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -8575,7 +8562,6 @@ "version": "4.0.3", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -8849,7 +8835,6 @@ "version": "5.9.3", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9454,7 +9439,6 @@ "node_modules/zod": { "version": "4.3.6", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" }