Updated home ui with portfolio status

This commit is contained in:
Khalim Conn-Kowlessar 2023-06-02 18:22:09 +01:00
parent 19bbaa9787
commit 3bc12fdfdb
9 changed files with 532 additions and 5 deletions

348
package-lock.json generated
View file

@ -10,6 +10,7 @@
"dependencies": {
"@headlessui/react": "^1.7.14",
"@heroicons/react": "^2.0.18",
"@radix-ui/react-hover-card": "^1.0.6",
"@radix-ui/react-slot": "^1.0.2",
"@tanstack/react-query": "^4.29.12",
"@types/node": "20.2.3",
@ -107,6 +108,31 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@floating-ui/core": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.6.tgz",
"integrity": "sha512-EvYTiXet5XqweYGClEmpu3BoxmsQ4hkj3QaYA6qEnigCWffTP3vNRwBReTdrwDwo7OoJ3wM8Uoe9Uk4n+d4hfg=="
},
"node_modules/@floating-ui/dom": {
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.2.9.tgz",
"integrity": "sha512-sosQxsqgxMNkV3C+3UqTS6LxP7isRLwX8WMepp843Rb3/b0Wz8+MdUkxJksByip3C2WwLugLHN1b4ibn//zKwQ==",
"dependencies": {
"@floating-ui/core": "^1.2.6"
}
},
"node_modules/@floating-ui/react-dom": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.0.tgz",
"integrity": "sha512-Ke0oU3SeuABC2C4OFu2mSAwHIP5WUiV98O9YWoHV4Q5aT6E9k06DV0Khi5uYspR8xmmBk08t8ZDcz3TR3ARkEg==",
"dependencies": {
"@floating-ui/dom": "^1.2.7"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@headlessui/react": {
"version": "1.7.14",
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.14.tgz",
@ -415,6 +441,37 @@
"url": "https://opencollective.com/unts"
}
},
"node_modules/@radix-ui/primitive": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz",
"integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==",
"dependencies": {
"@babel/runtime": "^7.13.10"
}
},
"node_modules/@radix-ui/react-arrow": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz",
"integrity": "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-primitive": "1.0.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-compose-refs": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz",
@ -432,6 +489,183 @@
}
}
},
"node_modules/@radix-ui/react-context": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz",
"integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==",
"dependencies": {
"@babel/runtime": "^7.13.10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-dismissable-layer": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.4.tgz",
"integrity": "sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.1",
"@radix-ui/react-compose-refs": "1.0.1",
"@radix-ui/react-primitive": "1.0.3",
"@radix-ui/react-use-callback-ref": "1.0.1",
"@radix-ui/react-use-escape-keydown": "1.0.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-hover-card": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.0.6.tgz",
"integrity": "sha512-2K3ToJuMk9wjwBOa+jdg2oPma+AmLdcEyTNsG/iC4BDVG3E0/mGCjbY8PEDSLxJcUi+nJi2QII+ec/4kWd88DA==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.1",
"@radix-ui/react-compose-refs": "1.0.1",
"@radix-ui/react-context": "1.0.1",
"@radix-ui/react-dismissable-layer": "1.0.4",
"@radix-ui/react-popper": "1.1.2",
"@radix-ui/react-portal": "1.0.3",
"@radix-ui/react-presence": "1.0.1",
"@radix-ui/react-primitive": "1.0.3",
"@radix-ui/react-use-controllable-state": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popper": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.2.tgz",
"integrity": "sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@floating-ui/react-dom": "^2.0.0",
"@radix-ui/react-arrow": "1.0.3",
"@radix-ui/react-compose-refs": "1.0.1",
"@radix-ui/react-context": "1.0.1",
"@radix-ui/react-primitive": "1.0.3",
"@radix-ui/react-use-callback-ref": "1.0.1",
"@radix-ui/react-use-layout-effect": "1.0.1",
"@radix-ui/react-use-rect": "1.0.1",
"@radix-ui/react-use-size": "1.0.1",
"@radix-ui/rect": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-portal": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.3.tgz",
"integrity": "sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-primitive": "1.0.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-presence": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz",
"integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-compose-refs": "1.0.1",
"@radix-ui/react-use-layout-effect": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-primitive": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz",
"integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-slot": "1.0.2"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-slot": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
@ -450,6 +684,120 @@
}
}
},
"node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz",
"integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==",
"dependencies": {
"@babel/runtime": "^7.13.10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-controllable-state": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz",
"integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-use-callback-ref": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-escape-keydown": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz",
"integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-use-callback-ref": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-layout-effect": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz",
"integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==",
"dependencies": {
"@babel/runtime": "^7.13.10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-rect": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz",
"integrity": "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/rect": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-size": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz",
"integrity": "sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-use-layout-effect": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/rect": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz",
"integrity": "sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==",
"dependencies": {
"@babel/runtime": "^7.13.10"
}
},
"node_modules/@rushstack/eslint-patch": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.0.tgz",

View file

@ -11,6 +11,7 @@
"dependencies": {
"@headlessui/react": "^1.7.14",
"@heroicons/react": "^2.0.18",
"@radix-ui/react-hover-card": "^1.0.6",
"@radix-ui/react-slot": "^1.0.2",
"@tanstack/react-query": "^4.29.12",
"@types/node": "20.2.3",

View file

@ -1,5 +1,12 @@
import { PortfolioStage } from "@/types/portfolio";
import Link from "next/link";
import React from "react";
import { Badge } from "@/app/shadcn_components/ui/badge";
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/app/shadcn_components/ui/hover-card";
const styles = {
wrapper:
@ -8,7 +15,7 @@ const styles = {
imageWrapper: "h-56 rounded-2xl overflow-hidden flex items-center",
wrapperAnime: "transition-all duration-500 ease-in-out",
image: "object-cover w-1/3 mx-auto",
textWrapper: "pt-10 pb-6 w-full px-4 flex justify-between items-center",
textWrapper: "pt-6 pb-3 w-full px-4 flex justify-between items-center",
};
interface CardProps {
@ -16,9 +23,88 @@ interface CardProps {
title: string;
image: string;
budget: string;
status: PortfolioStage;
}
const Card = ({ id, title, image, budget }: CardProps) => {
function StatusBadge({ status }: { status: PortfolioStage }) {
const statusConfig = statusColor[status];
return (
<div className="flex justify-end w-full mb-1 pr-4">
<HoverCard>
<HoverCardTrigger>
<Badge className={statusConfig.class}>{statusConfig.text}</Badge>
</HoverCardTrigger>
<HoverCardContent>
<div className="flex items-center">
<div>
<div
className={"w-6 h-6 rounded-full mr-2 " + statusConfig.class}
/>
</div>
<p className="text-sm text-muted-foreground">
{statusConfig.hoverText}
</p>
</div>
</HoverCardContent>
</HoverCard>
</div>
);
}
const statusColor: {
[key in PortfolioStage]: { class: string; text: string; hoverText: string };
} = {
scoping: {
class: "bg-emerald-500 hover:bg-emerald-500",
text: "scoping",
hoverText: "This portfolio is currently in scoping",
},
assessment: {
class: "bg-emerald-500 hover:bg-emerald-500",
text: "assessment",
hoverText: "This portfolio is currently in the assessment stage",
},
tendering: {
class: "bg-emerald-500 hover:bg-emerald-500",
text: "tendering",
hoverText: "This portfolio is currently in the tendering stage",
},
"project underway": {
class: "bg-emerald-500 hover:bg-emerald-500",
text: "project underway",
hoverText: "This portfolio has begun works",
},
"completion; status 'on track'": {
class: "bg-emerald-500 hover:bg-emerald-500",
text: "on track",
hoverText: "This portfolio is on track to be completed on time",
},
"completion; status 'delayed'": {
class: "bg-orange-400 hover:bg-orange-400",
text: "delayed",
hoverText:
"This portfolio is delayed and one or more properties require attention",
},
"completion; status 'at risk'": {
class: "bg-red-400 hover:bg-red-400",
text: "at risk",
hoverText:
"This portfolio is at risk. One or more properties require attention",
},
"completion; status 'completed'": {
class: "bg-gray-400 hover:bg-gray-400",
text: "completed",
hoverText: "This portfolio has been completed",
},
"needs review": {
class: "bg-emerald-300 hover:bg-emerald-300",
text: "needs review",
hoverText: "The works in this portfolio has been completed and need review",
},
};
const Card = ({ id, title, image, budget, status }: CardProps) => {
return (
<Link href={`/portfolio/${id}`}>
<div className={[styles.wrapper, styles.wrapperAnime].join(" ")}>
@ -31,6 +117,7 @@ const Card = ({ id, title, image, budget }: CardProps) => {
<h1>{`${title}`}</h1>
<div>{budget}</div>
</div>
<StatusBadge status={status} />
</div>
</Link>
);

View file

@ -2,11 +2,13 @@
import Card from "./Card";
import AddNewCard from "./AddNewCard";
import type { PortfolioStage } from "@/types/portfolio";
type PortfoliosType = Array<{
id: string;
title: string;
budget: string;
status: PortfolioStage;
}>;
export default function CardTiles({
@ -27,6 +29,7 @@ export default function CardTiles({
title={portfolio.title}
image={`house-icon-${image_idx}.svg`}
budget={portfolio.budget}
status={portfolio.status}
/>
);
})}

View file

@ -1,5 +1,5 @@
import Nav from "../components/Navbar";
import CardTiles from "../components/home/CardTiles";
import type { PortfolioStage } from "@/types/portfolio";
const Home = async () => {
const Portfolios = [
@ -7,51 +7,61 @@ const Home = async () => {
id: "d290f1ee-6c54-4b01-90e6-d701748f0851",
title: "Portfolio 1",
budget: "£500k",
status: "scoping" as PortfolioStage,
},
{
id: "d290f1ee-6c54-4b01-90e6-d701748f0852",
title: "Portfolio 2",
budget: "£150k",
status: "assessment" as PortfolioStage,
},
{
id: "d290f1ee-6c54-4b01-90e6-d701748f0853",
title: "Portfolio 3",
budget: "£1m",
status: "tendering" as PortfolioStage,
},
{
id: "d290f1ee-6c54-4b01-90e6-d701748f0854",
title: "Portfolio 4",
budget: "£2m",
status: "tendering" as PortfolioStage,
},
{
id: "d290f1ee-6c54-4b01-90e6-d701748f0855",
title: "Portfolio 5",
budget: "£25k",
budget: "250000",
status: "project underway" as PortfolioStage,
},
{
id: "d290f1ee-6c54-4b01-90e6-d701748f0856",
title: "Portfolio 6",
budget: "£10k",
budget: "£410k",
status: "completion; status 'on track'" as PortfolioStage,
},
{
id: "d290f1ee-6c54-4b01-90e6-d701748f0857",
title: "Portfolio 7",
budget: "£233k",
status: "completion; status 'delayed'" as PortfolioStage,
},
{
id: "d290f1ee-6c54-4b01-90e6-d701748f0858",
title: "Portfolio 8",
budget: "£670k",
status: "completion; status 'at risk'" as PortfolioStage,
},
{
id: "d290f1ee-6c54-4b01-90e6-d701748f0859",
title: "Portfolio 9",
budget: "£240k",
status: "completion; status 'completed'" as PortfolioStage,
},
{
id: "d290f1ee-6c54-4b01-90e6-d701748f0860",
title: "Portfolio 10",
budget: "93k",
status: "needs review" as PortfolioStage,
},
];

View file

@ -0,0 +1,36 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const badgeVariants = cva(
"inline-flex items-center border rounded-full px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
default:
"bg-primary hover:bg-primary/80 border-transparent text-primary-foreground",
secondary:
"bg-secondary hover:bg-secondary/80 border-transparent text-secondary-foreground",
destructive:
"bg-destructive hover:bg-destructive/80 border-transparent text-destructive-foreground",
outline: "text-foreground",
},
},
defaultVariants: {
variant: "default",
},
}
)
export interface BadgeProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof badgeVariants> {}
function Badge({ className, variant, ...props }: BadgeProps) {
return (
<div className={cn(badgeVariants({ variant }), className)} {...props} />
)
}
export { Badge, badgeVariants }

View file

@ -0,0 +1,29 @@
"use client"
import * as React from "react"
import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
import { cn } from "@/lib/utils"
const HoverCard = HoverCardPrimitive.Root
const HoverCardTrigger = HoverCardPrimitive.Trigger
const HoverCardContent = React.forwardRef<
React.ElementRef<typeof HoverCardPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<HoverCardPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
"z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none animate-in zoom-in-90",
className
)}
{...props}
/>
))
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName
export { HoverCard, HoverCardTrigger, HoverCardContent }

12
src/types/portfolio.ts Normal file
View file

@ -0,0 +1,12 @@
type PortfolioStage =
| "scoping"
| "assessment"
| "tendering"
| "project underway"
| "completion; status 'on track'"
| "completion; status 'delayed'"
| "completion; status 'at risk'"
| "completion; status 'completed'"
| "needs review";
export type { PortfolioStage };

View file

@ -1,5 +1,6 @@
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,