diff --git a/src/app/api/property-meta/[propertyId]/route.ts b/src/app/api/property-meta/[propertyId]/route.ts index 11f001b3..6996cff8 100644 --- a/src/app/api/property-meta/[propertyId]/route.ts +++ b/src/app/api/property-meta/[propertyId]/route.ts @@ -29,6 +29,7 @@ export async function GET( currentEpcRating: true, currentSapPoints: true, updatedAt: true, + currentValuation: true, }, where: eq(property.id, BigInt(propertyId)), with: { diff --git a/src/app/components/Buttons.tsx b/src/app/components/Buttons.tsx index 9d2d87d7..962e9cfd 100644 --- a/src/app/components/Buttons.tsx +++ b/src/app/components/Buttons.tsx @@ -26,19 +26,25 @@ export function BrandButton({ }: { label: string; onClick: Dispatch>; - backgroundColor: "brandblue" | "brandgold"; // Restrict backgroundColor to these two options + backgroundColor: + | "brandblue" + | "brandgold" + | "brandmidblue" + | "brandlightblue"; // Restrict backgroundColor to these two options }) { // Dictionary to map background colors to hover colors const hoverColors = { brandblue: "hover:bg-hoverblue", brandgold: "hover:bg-hovergold", + brandmidblue: "hover:bg-hoverblue", + brandlightblue: "hover:bg-brandmidblue", }; return ( + + + {/* Row 2: Budget */} +
+ Budget: +
+
+ handleNumericKeyDown(e)} + /> +
+
+ +
+ + {/* Row 3: Goal */} +
+ Goal: +
+ +
+ +
+ + + {/* Row 4: Status */} +
+ Status: +
+
+ +
+ + +
Portfolio Name: {portfolioName}
+
Portfolio Budget: {portfolioBudget}
+
Goal value: {portfolioGoal}
+
Status value: {portfolioStatus}
+ + {/* Row 5: Delete */} +
+
+ +
+ {/* Delete portfolio modal */} + + + Are you sure? +

+ To confirm, please type the name of the portfolio ( + {portfolioSettingsData.name}) +

+ setDeleteConfirmationByName(e.target.value)} + placeholder="Type portfolio name" + /> + + + + +
+
+ + + + ); +} diff --git a/src/app/portfolio/[slug]/(portfolio)/settings/page.tsx b/src/app/portfolio/[slug]/(portfolio)/settings/page.tsx new file mode 100644 index 00000000..533713cc --- /dev/null +++ b/src/app/portfolio/[slug]/(portfolio)/settings/page.tsx @@ -0,0 +1,38 @@ +import { getPortfolioSettings } from "../../utils"; +import PortfolioSettings from "./PortfolioSettings"; + +export default async function PortfolioSettingsPage({ + params, +}: { + params: { slug: string }; +}) { + const portfolioId = params.slug; + // fetch data securely on the server + // Stef's page!!!! + // 1) Rename + // 2) Update budget, status, goal + // 3) Delete - much harder + + // fetch data in the server - name, budget, goal, + // pass it to a client component to render and take user input + + const portfolioSettingsData = await getPortfolioSettings(portfolioId); + + // Get goal options and status options by importing something like this: + // import { + // PortfolioStatus, + // PortfolioGoal, + // } from "./../../db/schema/portfolio"; + // and then pass them to the portfoliosettings component + + return ( + <> +
+ +
+ + ); +} diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/plans/[planId]/page.tsx b/src/app/portfolio/[slug]/building-passport/[propertyId]/plans/[planId]/page.tsx index a0ee436b..5e79ea8b 100644 --- a/src/app/portfolio/[slug]/building-passport/[propertyId]/plans/[planId]/page.tsx +++ b/src/app/portfolio/[slug]/building-passport/[propertyId]/plans/[planId]/page.tsx @@ -1,5 +1,5 @@ import RecommendationContainer from "@/app/components/building-passport/RecommendationContainer"; -import { getPropertyMeta, getRecommendations } from "../../utils"; +import { getPropertyMeta, getRecommendations, getPlanMeta } from "../../utils"; export default async function Recommendations({ params, @@ -8,6 +8,7 @@ export default async function Recommendations({ }) { const propertyMeta = await getPropertyMeta(params.propertyId); const recommendations = await getRecommendations(params.planId); + const planMeta = await getPlanMeta(params.planId); return (
@@ -15,6 +16,7 @@ export default async function Recommendations({
); diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/utils.ts b/src/app/portfolio/[slug]/building-passport/[propertyId]/utils.ts index 37ded61a..487ea865 100644 --- a/src/app/portfolio/[slug]/building-passport/[propertyId]/utils.ts +++ b/src/app/portfolio/[slug]/building-passport/[propertyId]/utils.ts @@ -1,6 +1,8 @@ import { Recommendation, planRecommendations, + plan, + Plan, } from "@/app/db/schema/recommendations"; import { db } from "@/app/db/db"; import { @@ -14,7 +16,6 @@ import { NonIntrusiveSurveyData, nonInstrusiveSurvey, } from "@/app/db/schema/property"; -import { plan, Plan } from "@/app/db/schema/recommendations"; import { getRating } from "@/app/utils"; import { eq, desc } from "drizzle-orm"; import { @@ -85,6 +86,18 @@ export async function getRecommendations( return recommendations; } +export async function getPlanMeta(planId: string): Promise { + const data = await db.query.plan.findFirst({ + where: eq(plan.id, BigInt(planId)), + }); + + if (!data) { + throw new Error("Network response was not ok"); + } + + return data; +} + type PlanRelation = Plan & { planRecommendations: { recommendation: { diff --git a/src/app/portfolio/[slug]/utils.ts b/src/app/portfolio/[slug]/utils.ts index c50bcd2e..3cc8dd53 100644 --- a/src/app/portfolio/[slug]/utils.ts +++ b/src/app/portfolio/[slug]/utils.ts @@ -1,3 +1,7 @@ +import type { + PortfolioStatus, + PortfolioGoal, +} from "./../../db/schema/portfolio"; import { formatNumber } from "@/app/utils"; import { and, eq, inArray } from "drizzle-orm"; import { db } from "@/app/db/db"; @@ -15,6 +19,38 @@ import { ScenarioSelect, } from "@/app/db/schema/recommendations"; +export interface PortfolioSettingsType { + name: string; + budget: number | null; + goal: (typeof PortfolioGoal)[number]; + status: (typeof PortfolioStatus)[number]; +} + +export async function getPortfolioSettings( + portfolioId: string +): Promise { + //name, budget, goal, status + const data = await db + .select({ + name: portfolio.name, + budget: portfolio.budget, + goal: portfolio.goal, + status: portfolio.status, + }) + .from(portfolio) + .where(eq(portfolio.id, BigInt(portfolioId))); + + if (data.length === 0) { + throw new Error("Portfolio not found"); + } + + if (data.length > 1) { + throw new Error("More than one portfolio found"); + } + + return data[0]; +} + export async function getPortfolio(portfolioId: string): Promise { const data = await db .select() diff --git a/src/app/utils.ts b/src/app/utils.ts index f8371c7a..319ae0ce 100644 --- a/src/app/utils.ts +++ b/src/app/utils.ts @@ -1,4 +1,16 @@ import { Rating } from "./db/schema/property"; +import { KeyboardEvent} from "react"; + + export function handleNumericKeyDown(event: KeyboardEvent) { + + /** + * Allowing: Integers | Backspace | Tab | Delete | Left & Right arrow keys + **/ + + const regex = new RegExp(/(^\d*$)|(Backspace|Tab|Delete|ArrowLeft|ArrowRight|ArrowUp|ArrowDown)/); + + return !event.key.match(regex) && event.preventDefault(); + } export function convertDaysToWorkingWeeks(days: number | null) { if (days === null) { @@ -149,3 +161,5 @@ export function roundToDecimalPlaces( const factor = 10 ** decimalPlaces; return Math.round(number * factor) / factor; } + + diff --git a/tailwind.config.js b/tailwind.config.js index 7521e9e3..80e84638 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -104,6 +104,7 @@ module.exports = { hovergold: "#c79d12", brandbrown: "#3d1e05", brandmidblue: "#3943b7", + brandlightblue: "#00a9f4", border: "hsl(var(--border))", input: "hsl(var(--input))", ring: "hsl(var(--ring))", @@ -145,6 +146,7 @@ module.exports = { hovertan: "#947750", brandbrown: "#3d1e05", brandmidblue: "#3943b7", + brandlightblue: "#00a9f4", }, borderRadius: { lg: `var(--radius)`,