From 03c6425fd7aa590036f293bed2a912a6e8815948 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Fri, 31 Oct 2025 23:18:21 +0000 Subject: [PATCH] project ui live --- .../portfolio/summary/EpcBarChart.tsx | 14 +- src/app/globals.css | 128 ---------- .../temp-reporting/ProjectProposal.tsx | 93 ++++--- .../(portfolio)/temp-reporting/page.tsx | 19 +- tailwind.config.js | 226 ++++++++++++++++++ 5 files changed, 302 insertions(+), 178 deletions(-) diff --git a/src/app/components/portfolio/summary/EpcBarChart.tsx b/src/app/components/portfolio/summary/EpcBarChart.tsx index 6c26bab5..f111afa2 100644 --- a/src/app/components/portfolio/summary/EpcBarChart.tsx +++ b/src/app/components/portfolio/summary/EpcBarChart.tsx @@ -23,13 +23,13 @@ const EpcBarChart = ({ index="name" categories={["G", "F", "E", "D", "C", "B", "A"]} // Each treated as a separate series colors={[ - "epc_g", // Color for 'G' - "epc_f", // Color for 'F' - "epc_e", // Color for 'E' - "epc_d", // Color for 'D' - "epc_c", // Color for 'C' - "epc_b", // Color for 'B' - "epc_a", // Color for 'A' + "#e41e3b", // Color for 'G' + "#ef8026", // Color for 'F' + "#f3a96a", // Color for 'E' + "#f7cd14", // Color for 'D' + "#8dbd40", // Color for 'C' + "#2da55c", // Color for 'B' + "#117d58", // Color for 'A' ]} valueFormatter={dataFormatter} yAxisWidth={48} diff --git a/src/app/globals.css b/src/app/globals.css index 6d892a40..3b98d12f 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,5 +1,3 @@ -@import "tailwindcss"; -@plugin "@tailwindcss/forms"; @tailwind base; @tailwind components; @tailwind utilities; @@ -120,129 +118,3 @@ .animate-spin { animation: spin 1s linear infinite; } - -@custom-variant dark (&:where(.dark, .dark *)); - -@theme { - --animate-hide: hide 150ms cubic-bezier(0.16, 1, 0.3, 1); - --animate-slide-down-and-fade: slideDownAndFade 150ms - cubic-bezier(0.16, 1, 0.3, 1); - --animate-slide-left-and-fade: slideLeftAndFade 150ms - cubic-bezier(0.16, 1, 0.3, 1); - --animate-slide-up-and-fade: slideUpAndFade 150ms - cubic-bezier(0.16, 1, 0.3, 1); - --animate-slide-right-and-fade: slideRightAndFade 150ms - cubic-bezier(0.16, 1, 0.3, 1); - --animate-accordion-open: accordionOpen 150ms cubic-bezier(0.87, 0, 0.13, 1); - --animate-accordion-close: accordionClose 150ms cubic-bezier(0.87, 0, 0.13, 1); - --animate-dialog-overlay-show: dialogOverlayShow 150ms - cubic-bezier(0.16, 1, 0.3, 1); - --animate-dialog-content-show: dialogContentShow 150ms - cubic-bezier(0.16, 1, 0.3, 1); - --animate-drawer-slide-left-and-fade: drawerSlideLeftAndFade 150ms - cubic-bezier(0.16, 1, 0.3, 1); - --animate-drawer-slide-right-and-fade: drawerSlideRightAndFade 150ms ease-in; - - @keyframes hide { - from { - opacity: 1; - } - to { - opacity: 0; - } - } - @keyframes slideDownAndFade { - from { - opacity: 0; - transform: translateY(-6px); - } - to { - opacity: 1; - transform: translateY(0); - } - } - @keyframes slideLeftAndFade { - from { - opacity: 0; - transform: translateX(6px); - } - to { - opacity: 1; - transform: translateX(0); - } - } - @keyframes slideUpAndFade { - from { - opacity: 0; - transform: translateY(6px); - } - to { - opacity: 1; - transform: translateY(0); - } - } - @keyframes slideRightAndFade { - from { - opacity: 0; - transform: translateX(-6px); - } - to { - opacity: 1; - transform: translateX(0); - } - } - @keyframes accordionOpen { - from { - height: 0px; - } - to { - height: var(--radix-accordion-content-height); - } - } - @keyframes accordionClose { - from { - height: var(--radix-accordion-content-height); - } - to { - height: 0px; - } - } - @keyframes dialogOverlayShow { - from { - opacity: 0; - } - to { - opacity: 1; - } - } - @keyframes dialogContentShow { - from { - opacity: 0; - transform: translate(-50%, -45%) scale(0.95); - } - to { - opacity: 1; - transform: translate(-50%, -50%) scale(1); - } - } - @keyframes drawerSlideLeftAndFade { - from { - opacity: 0; - transform: translateX(100%); - } - to { - opacity: 1; - transform: translateX(0); - } - } - @keyframes drawerSlideRightAndFade { - from { - opacity: 1; - transform: translateX(0); - } - to { - opacity: 0; - transform: translateX(100%); - } - } -} diff --git a/src/app/portfolio/[slug]/(portfolio)/temp-reporting/ProjectProposal.tsx b/src/app/portfolio/[slug]/(portfolio)/temp-reporting/ProjectProposal.tsx index c71a2732..3a0be4e0 100644 --- a/src/app/portfolio/[slug]/(portfolio)/temp-reporting/ProjectProposal.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/temp-reporting/ProjectProposal.tsx @@ -9,6 +9,8 @@ import { } from "@/app/shadcn_components/ui/card"; import { BarChart, DonutChart } from "@tremor/react"; import { formatNumber } from "@/app/utils"; +import { PoundSterling, Leaf, Zap, Home } from "lucide-react"; +import { motion } from "framer-motion"; const mappedTitles: Record = { solar_eco4: "Solar ECO4 project metrics", @@ -71,19 +73,10 @@ export function ProjectProposal({ plans }: { plans: any[] }) { ? grouped[0] : null; - const domnaPalette = [ - "#14163d", // brandblue (deep navy) - "#2d348f", // midblue - "#3943b7", // brandmidblue - "#c4a47c", // brandbrown - "#d3b488", // brandtan - "#eff6fc", // brandlightblue (for subtle items) - ]; - return ( -
+
{/* Chart */} - + Homes by Work Type @@ -95,7 +88,7 @@ export function ProjectProposal({ plans }: { plans: any[] }) { data={grouped} index="planType" categories={["count"]} - colors={domnaPalette} + colors={["#2d348f", "#14163d", "#3943b7", "#5d6be0"]} valueFormatter={(v) => v.toString()} onValueChange={(v) => setSelectedType( @@ -111,7 +104,7 @@ export function ProjectProposal({ plans }: { plans: any[] }) { data={grouped} category="count" index="planType" - colors={["midblue"]} + colors={["#2d348f", "#14163d", "#3943b7", "#5d6be0"]} valueFormatter={(v) => `${v} home${v === 1 ? "" : "s"}`} /> )} @@ -130,7 +123,7 @@ export function ProjectProposal({ plans }: { plans: any[] }) {

Total client contribution

-

+

£{formatNumber(selectedData?.totalClientContribution || 0)}

@@ -177,49 +170,81 @@ export function DashboardSummary({ plans }: { plans: any[] }) { ); const planCount = plans.length; - const cards = [ + const cards: { + title: string; + value: string | number; + subtitle: string; + icon: React.ElementType; + }[] = [ { title: "Total Funding", value: `£${formatNumber(totalFunding)}`, subtitle: "Domna will help you unlock this much funding.", + icon: PoundSterling, // ✅ no }, { title: "Carbon Savings", value: `${(totalCarbon * 1000).toFixed(2)} kgCO₂e`, subtitle: "Your projects’ total estimated CO₂e savings.", + icon: Leaf, }, { title: "Bill Savings", value: `£${formatNumber(totalBills)}`, subtitle: "Expected total bill reductions across all homes.", + icon: Zap, }, { title: "Number of Homes", value: planCount, subtitle: "Properties included across your project plans.", + icon: Home, }, ]; return ( -

- {cards.map((c) => ( - - - - {c.title} - - - -
- {c.value} -
-

{c.subtitle}

-
-
- ))} +
+ {cards.map((c) => { + const Icon = c.icon; + return ( + + +
+ + + +
+ + {c.title} + +
+ + +
+ {c.value} +
+

{c.subtitle}

+
+
+ ); + })}
); } diff --git a/src/app/portfolio/[slug]/(portfolio)/temp-reporting/page.tsx b/src/app/portfolio/[slug]/(portfolio)/temp-reporting/page.tsx index e15eb0bc..cef20f06 100644 --- a/src/app/portfolio/[slug]/(portfolio)/temp-reporting/page.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/temp-reporting/page.tsx @@ -12,21 +12,22 @@ export default async function YourProjectsPage({ const latestPlans = await getPlansWithTotals(portfolioId); return ( -
-
-

- Your Retrofit Projects -

-

- Review project performance and funding insights across your portfolio. +

+
+
+ Project Overview +
+

+ Summary of funding, carbon savings, and household metrics.

-
+
+
-

+

Your Homes

diff --git a/tailwind.config.js b/tailwind.config.js index 317049d6..c977fab8 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -28,6 +28,64 @@ module.exports = { "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", }, colors: { + tremor: { + brand: { + faint: "colors.blue[50]", + muted: "colors.blue[200]", + subtle: "colors.blue[400]", + DEFAULT: "colors.blue[500]", + emphasis: "colors.blue[700]", + inverted: "colors.white", + }, + background: { + muted: "colors.gray[50]", + subtle: "colors.gray[100]", + DEFAULT: "colors.white", + emphasis: "colors.gray[700]", + }, + border: { + DEFAULT: "colors.gray[200]", + }, + ring: { + DEFAULT: "colors.gray[200]", + }, + content: { + subtle: "colors.gray[400]", + DEFAULT: "colors.gray[500]", + emphasis: "colors.gray[700]", + strong: "colors.gray[900]", + inverted: "colors.white", + }, + }, + "dark-tremor": { + brand: { + faint: "#0B1229", + muted: "colors.blue[950]", + subtle: "colors.blue[800]", + DEFAULT: "colors.blue[500]", + emphasis: "colors.blue[400]", + inverted: "colors.blue[950]", + }, + background: { + muted: "#131A2B", + subtle: "colors.gray[800]", + DEFAULT: "colors.gray[900]", + emphasis: "colors.gray[300]", + }, + border: { + DEFAULT: "colors.gray[800]", + }, + ring: { + DEFAULT: "colors.gray[800]", + }, + content: { + subtle: "colors.gray[600]", + DEFAULT: "colors.gray[500]", + emphasis: "colors.gray[200]", + strong: "colors.gray[50]", + inverted: "colors.gray[950]", + }, + }, epc_a: "#117d58", epc_b: "#2da55c", epc_c: "#8dbd40", @@ -88,6 +146,11 @@ module.exports = { brandmidblue: "#3943b7", brandlightblue: "#00a9f4", }, + borderRadius: { + "tremor-small": "0.375rem", + "tremor-default": "0.5rem", + "tremor-full": "9999px", + }, fontFamily: { sans: ["var(--font-sans)", ...fontFamily.sans], }, @@ -134,6 +197,44 @@ module.exports = { maxWidth: { "8xl": "90rem", }, + boxShadow: { + "tremor-input": "0 1px 2px 0 rgb(0 0 0 / 0.05)", + "tremor-card": + "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)", + "tremor-dropdown": + "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)", + "dark-tremor-input": "0 1px 2px 0 rgb(0 0 0 / 0.05)", + "dark-tremor-card": + "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)", + "dark-tremor-dropdown": + "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)", + }, + fontSize: { + "tremor-label": [ + "0.75rem", + { + lineHeight: "1rem", + }, + ], + "tremor-default": [ + "0.875rem", + { + lineHeight: "1.25rem", + }, + ], + "tremor-title": [ + "1.125rem", + { + lineHeight: "1.75rem", + }, + ], + "tremor-metric": [ + "1.875rem", + { + lineHeight: "2.25rem", + }, + ], + }, }, }, variants: { @@ -169,6 +270,131 @@ module.exports = { pattern: /^(fill-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/, }, + // This enables the EPC colours for tremor. They're listed from EPC G -> A + "bg-[#e41e3b]", + "border-[#e41e3b]", + "hover:bg-[#e41e3b]", + "hover:border-[#e41e3b]", + "hover:text-[#e41e3b]", + "fill-[#e41e3b]", + "ring-[#e41e3b]", + "stroke-[#e41e3b]", + "text-[#e41e3b]", + "ui-selected:bg-[#e41e3b]", + "ui-selected:border-[#e41e3b]", + "ui-selected:text-[#e41e3b]", + "bg-[#ef8026]", + "border-[#ef8026]", + "hover:bg-[#ef8026]", + "hover:border-[#ef8026]", + "hover:text-[#ef8026]", + "fill-[#ef8026]", + "ring-[#ef8026]", + "stroke-[#ef8026]", + "text-[#ef8026]", + "ui-selected:bg-[#ef8026]", + "ui-selected:border-[#ef8026]", + "ui-selected:text-[#ef8026]", + "bg-[#f3a96a]", + "border-[#f3a96a]", + "hover:bg-[#f3a96a]", + "hover:border-[#f3a96a]", + "hover:text-[#f3a96a]", + "fill-[#f3a96a]", + "ring-[#f3a96a]", + "stroke-[#f3a96a]", + "text-[#f3a96a]", + "ui-selected:bg-[#f3a96a]", + "ui-selected:border-[#f3a96a]", + "ui-selected:text-[#f3a96a]", + "bg-[#f7cd14]", + "border-[#f7cd14]", + "hover:bg-[#f7cd14]", + "hover:border-[#f7cd14]", + "hover:text-[#f7cd14]", + "fill-[#f7cd14]", + "ring-[#f7cd14]", + "stroke-[#f7cd14]", + "text-[#f7cd14]", + "ui-selected:bg-[#f7cd14]", + "ui-selected:border-[#f7cd14]", + "ui-selected:text-[#f7cd14]", + "bg-[#8dbd40]", + "border-[#8dbd40]", + "hover:bg-[#8dbd40]", + "hover:border-[#8dbd40]", + "hover:text-[#8dbd40]", + "fill-[#8dbd40]", + "ring-[#8dbd40]", + "stroke-[#8dbd40]", + "text-[#8dbd40]", + "ui-selected:bg-[#8dbd40]", + "ui-selected:border-[#8dbd40]", + "ui-selected:text-[#8dbd40]", + "bg-[#2da55c]", + "border-[#2da55c]", + "hover:bg-[#2da55c]", + "hover:border-[#2da55c]", + "hover:text-[#2da55c]", + "fill-[#2da55c]", + "ring-[#2da55c]", + "stroke-[#2da55c]", + "text-[#2da55c]", + "ui-selected:bg-[#2da55c]", + "ui-selected:border-[#2da55c]", + "ui-selected:text-[#2da55c]", + "bg-[#117d58]", + "border-[#117d58]", + "hover:bg-[#117d58]", + "hover:border-[#117d58]", + "hover:text-[#117d58]", + "fill-[#117d58]", + "ring-[#117d58]", + "stroke-[#117d58]", + "text-[#117d58]", + "ui-selected:bg-[#117d58]", + "ui-selected:border-[#117d58]", + "ui-selected:text-[#117d58]", + + // blue colours for graphs - eff6fc + "bg-[#eff6fc]", + "border-[#eff6fc]", + "hover:bg-[#eff6fc]", + "hover:border-[#eff6fc]", + "hover:text-[#eff6fc]", + "fill-[#eff6fc]", + "ring-[#eff6fc]", + "stroke-[#eff6fc]", + "text-[#eff6fc]", + "ui-selected:bg-[#eff6fc]", + "ui-selected:border-[#eff6fc]", + "ui-selected:text-[#eff6fc]", + // brand blues for Tremor charts + "bg-[#14163d]", + "border-[#14163d]", + "fill-[#14163d]", + "stroke-[#14163d]", + "text-[#14163d]", + "bg-[#2d348f]", + "border-[#2d348f]", + "fill-[#2d348f]", + "stroke-[#2d348f]", + "text-[#2d348f]", + "bg-[#3943b7]", + "border-[#3943b7]", + "fill-[#3943b7]", + "stroke-[#3943b7]", + "text-[#3943b7]", + "bg-[#5d6be0]", + "border-[#5d6be0]", + "fill-[#5d6be0]", + "stroke-[#5d6be0]", + "text-[#5d6be0]", + "bg-[#1f3abdff]", + "border-[#1f3abdff]", + "fill-[#1f3abdff]", + "stroke-[#1f3abdff]", + "text-[#1f3abdff]", ], plugins: [ function ({ addVariant }) {