diff --git a/public/bigHeart.png b/public/bigHeart.png new file mode 100644 index 0000000..9f5648f Binary files /dev/null and b/public/bigHeart.png differ diff --git a/public/downloadIcon.png b/public/downloadIcon.png new file mode 100644 index 0000000..e5aab8b Binary files /dev/null and b/public/downloadIcon.png differ diff --git a/public/facebookIcon.png b/public/facebookIcon.png new file mode 100644 index 0000000..efa20dc Binary files /dev/null and b/public/facebookIcon.png differ diff --git a/public/heartFilled.png b/public/heartFilled.png new file mode 100644 index 0000000..c9b1485 Binary files /dev/null and b/public/heartFilled.png differ diff --git a/public/image.png b/public/image.png new file mode 100644 index 0000000..fcae407 Binary files /dev/null and b/public/image.png differ diff --git a/public/instagramIcon.png b/public/instagramIcon.png new file mode 100644 index 0000000..848b192 Binary files /dev/null and b/public/instagramIcon.png differ diff --git a/public/logoBlue.png b/public/logoBlue.png new file mode 100644 index 0000000..725fa16 Binary files /dev/null and b/public/logoBlue.png differ diff --git a/public/share_icon.png b/public/share_icon.png new file mode 100644 index 0000000..213a2f0 Binary files /dev/null and b/public/share_icon.png differ diff --git a/public/stick_couple.png b/public/stick_couple.png new file mode 100644 index 0000000..c647d8a Binary files /dev/null and b/public/stick_couple.png differ diff --git a/public/stick_couple_mirrored.png b/public/stick_couple_mirrored.png new file mode 100644 index 0000000..7afd93b Binary files /dev/null and b/public/stick_couple_mirrored.png differ diff --git a/public/tikTokIcon.png b/public/tikTokIcon.png new file mode 100644 index 0000000..ab3b642 Binary files /dev/null and b/public/tikTokIcon.png differ diff --git a/public/twitterIcon.png b/public/twitterIcon.png new file mode 100644 index 0000000..8d3acf7 Binary files /dev/null and b/public/twitterIcon.png differ diff --git a/public/wechatIcon.png b/public/wechatIcon.png new file mode 100644 index 0000000..ac26608 Binary files /dev/null and b/public/wechatIcon.png differ diff --git a/src/app/globals.css b/src/app/globals.css index bacdc6b..319075e 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -8,6 +8,7 @@ --color-foreground: var(--foreground); --font-sans: var(--font-geist-sans); --font-mono: var(--font-geist-mono); + --font-family-press-start: var(--font-press-start); --color-sidebar-ring: var(--sidebar-ring); --color-sidebar-border: var(--sidebar-border); --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); @@ -37,6 +38,9 @@ --color-popover: var(--popover); --color-card-foreground: var(--card-foreground); --color-card: var(--card); + --color-pmpink2-500: rgb(235, 168, 176); + --color-pmred-500: rgb(243, 0, 32); + --color-pmblue-500: rgb(36, 67, 141); --radius-sm: calc(var(--radius) - 4px); --radius-md: calc(var(--radius) - 2px); --radius-lg: var(--radius); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 93896c1..94b436d 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,5 +1,5 @@ import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; +import { Geist, Geist_Mono, Press_Start_2P } from "next/font/google"; import "./globals.css"; import Providers from "@/components/providers"; import { Analytics } from "@vercel/analytics/next"; @@ -16,6 +16,12 @@ const geistMono = Geist_Mono({ subsets: ["latin"], }); +const pressStart = Press_Start_2P({ + weight: "400", + subsets: ["latin"], + variable: "--font-press-start", +}); + export const metadata: Metadata = { title: "PMTI", description: "Cornell University's Perfect Match Type Indicator website", @@ -32,7 +38,7 @@ export default function RootLayout({ return ( diff --git a/src/app/type/[typeCode]/page.tsx b/src/app/type/[typeCode]/page.tsx new file mode 100644 index 0000000..e425de0 --- /dev/null +++ b/src/app/type/[typeCode]/page.tsx @@ -0,0 +1,240 @@ +import { COUPLE_TYPES, CoupleTypeCode } from "@/lib/constants/coupleTypes"; +import { notFound } from "next/navigation"; +import Image from "next/image"; +import { ShareButton } from "@/components/ShareButton"; +import { PersonalityBar } from "@/components/PersonalityBar"; +import { Footer } from "@/components/footer"; + +interface TypePageProps { + params: Promise<{ + typeCode: string; + }>; +} + +//http://localhost:3000/type/olin-and-uris +export default async function TypePage({ params }: TypePageProps) { + const { typeCode } = await params; + + // Fixes naming scheme + const enumKey = typeCode.toUpperCase().replace(/-/g, '_') as CoupleTypeCode; + const coupleType = COUPLE_TYPES[enumKey]; + + // If type not found, show 404 + if (!coupleType) { + notFound(); + } + + return ( +
+ {/* You are... Section */} +
+ {/* Faint PER background text */} +
+

RER

+
+ + {/* Scattered Hearts */} + + + + + {/* Big Heart Background - Right Side */} + + + {/* Stick Figure Couple */} + Couple illustration + + {/* Top Div - Pink Background with Content */} +
+
+ {/* Main Content - Left Side */} +
+
+ {/* Stats and Button */} +
+

+ + 46% + of couples share your type +

+ +
+ + {/* Title section - appears above due to flex-col-reverse */} +
+

You are ...

+

{coupleType.displayName}

+
+
+
+
+
+ + {/* Wavy Divider - Handles entire transition */} +
+ + + +
+ {/* Bottom Section*/} +
+
+
+ + {/* Section 2 */} +
+ {/* Stick Figure Couple */} + Couple illustration + Couple illustration + +
+ {/* Top Section: Description + Stick Figure + Personality Bars */} +
+ {/* Left: Type Description Box */} +
+

+ {coupleType.displayName} - PER +

+

+ {coupleType.description.summary} +

+
+ + {/*Personality Bars */} +
+ {/* Planned vs Spontaneous */} + + + {/* Physical vs Emotional */} + + + {/* Reflective vs Active */} + +
+
+ + {/*Date Ideas and Conflict Resolution*/} +
+ {/* Date Ideas */} +
+
+ +
+

+ 💝 Date Ideas +

+
    + {coupleType.dateIdeas?.map((idea, index) => ( +
  • + + {idea} +
  • + ))} +
+
+ + {/* How They Resolve Conflict */} +
+

+ ❤️‍🩹 How They Resolve Conflict +

+

+ {coupleType.conflictResolutionText || coupleType.description.conflictResolution?.style} +

+
+
+ + {/* Bottom Row: Love Language (Full Width) */} +
+ {/* Love Language */} +
+

+ 💖 Love Language +

+
    + {coupleType.loveLanguages?.map((language, index) => ( +
  • + + {language} +
  • + ))} +
+
+
+
+ + {/* Wavy Divider */} +
+ + + + + + +
+ +
+
+ +
+ ); +} + +export async function generateStaticParams() { + return Object.values(COUPLE_TYPES).map((type) => ({ + typeCode: type.code.toLowerCase().replace(/_/g, '-'), + })); +} \ No newline at end of file diff --git a/src/components/PersonalityBar.tsx b/src/components/PersonalityBar.tsx new file mode 100644 index 0000000..c35502a --- /dev/null +++ b/src/components/PersonalityBar.tsx @@ -0,0 +1,74 @@ +interface PersonalityBarProps { + leftLabel: string; + rightLabel: string; + leftPercentage: number; + leftColor: 'blue' | 'pink'; + rightColor: 'blue' | 'pink'; + backgroundColor: 'blue' | 'pink'; +} + +export function PersonalityBar({ + leftLabel, + rightLabel, + leftPercentage, + leftColor, + rightColor, + backgroundColor +}: PersonalityBarProps) { + const rightPercentage = 100 - leftPercentage; + + // Color mappings + const colorMap = { + blue: { + text: 'text-blue-900', + bg: 'bg-blue-900', + textNoWeight: 'text-blue-900' + }, + pink: { + text: 'text-pink-500', + bg: 'bg-pink-100', + textNoWeight: 'text-pink-500' + } + }; + + const bgColorMap = { + blue: 'bg-blue-100', + pink: 'bg-pink-100' + }; + + return ( +
+
+ + {leftLabel} + + + {rightLabel} + +
+
+ {/* Left side */} +
+ + {leftPercentage}% + +
+ {/* Right side */} +
+ + {rightPercentage}% + +
+
+
+ ); +} diff --git a/src/components/ShareButton.tsx b/src/components/ShareButton.tsx new file mode 100644 index 0000000..3471af3 --- /dev/null +++ b/src/components/ShareButton.tsx @@ -0,0 +1,72 @@ +'use client'; + +import { useState } from "react"; +import Image from "next/image"; +import { Button } from "@/components/ui/button"; + +interface ShareButtonProps { + buttonText?: string; + showShareMenu?: boolean; +} + +export function ShareButton({ buttonText = "Share Your Results!", showShareMenu = true }: ShareButtonProps) { + const [isShareMenuOpen, setIsShareMenuOpen] = useState(false); + + return ( +
+ + + {/* Social Media Share Menu */} + {showShareMenu && ( +
+ + + + + + +
+ )} +
+ ); +} diff --git a/src/components/footer.tsx b/src/components/footer.tsx new file mode 100644 index 0000000..5e96a2a --- /dev/null +++ b/src/components/footer.tsx @@ -0,0 +1,60 @@ +import Image from "next/image"; + +export function Footer() { + return ( + + ); +} + +export default Footer; diff --git a/src/components/navbar/desktop.tsx b/src/components/navbar/desktop.tsx index ea2c983..a4e78cd 100644 --- a/src/components/navbar/desktop.tsx +++ b/src/components/navbar/desktop.tsx @@ -38,18 +38,20 @@ export function DesktopNavbar() {
- {filteredNavItems.map((item) => ( - - - - {item.label} - - - - ))} + {filteredNavItems + .filter((item) => item.label !== "Type Indicator") + .map((item) => ( + + + + {item.label} + + + + ))} diff --git a/src/components/navbar/nav-config.ts b/src/components/navbar/nav-config.ts index 8a8dcac..7dfe645 100644 --- a/src/components/navbar/nav-config.ts +++ b/src/components/navbar/nav-config.ts @@ -19,4 +19,8 @@ export const navigationItems: NavItem[] = [ label: "About", href: "/about", }, + { + label: "Type Indicator", + href: "/type", + } ]; diff --git a/src/components/navbar/type-indicator-dropdown.tsx b/src/components/navbar/type-indicator-dropdown.tsx new file mode 100644 index 0000000..5c7da95 --- /dev/null +++ b/src/components/navbar/type-indicator-dropdown.tsx @@ -0,0 +1,102 @@ + +"use client"; + +import Link from "next/link"; +import { COUPLE_TYPES } from "@/lib/constants/coupleTypes"; +import { + NavigationMenu, + NavigationMenuContent, + NavigationMenuItem, + NavigationMenuLink, + NavigationMenuList, + NavigationMenuTrigger, +} from "@/components/ui/navigation-menu"; + +export interface DropdownItem { + label: string; + href: string; +} + +// Generate dropdown items from couple types +const generateTypeIndicatorDropdown = (): DropdownItem[] => { + return Object.values(COUPLE_TYPES) + .filter(coupleType => coupleType && coupleType.displayName && coupleType.code) + .map((coupleType) => ({ + label: coupleType.displayName, + href: `/type/${coupleType.code.toLowerCase().replace(/_/g, '-')}`, + })); +}; + +interface TypeIndicatorDropdownProps { + dropdown?: DropdownItem[]; +} + +export function TypeIndicatorDropdown({ dropdown = generateTypeIndicatorDropdown() }: TypeIndicatorDropdownProps) { + return ( + + + + + Type Indicator + + + {/* Row 1 */} +
+ {dropdown.slice(0, 4).map((dropdownItem, index) => ( + + +
+ {dropdownItem.label || 'Loading...'} +
+ +
+ ))} +
+
+ + {/* Row 2 */} +
+ {dropdown.slice(4, 8).map((dropdownItem, index) => ( + + +
+ {dropdownItem.label || 'Loading...'} +
+ +
+ ))} +
+
+ + {/* Row 3 */} +
+ {dropdown.slice(8, 12).map((dropdownItem, index) => ( + + +
+ {dropdownItem.label || 'Loading...'} +
+ +
+ ))} +
+
+ + {/* Row 4 */} +
+ {dropdown.slice(12, 16).map((dropdownItem, index) => ( + + +
+ {dropdownItem.label || 'Loading...'} +
+ +
+ ))} +
+
+
+
+
+ ); +} diff --git a/src/components/types/adventurous-planners.tsx b/src/components/types/adventurous-planners.tsx new file mode 100644 index 0000000..4cf2cca --- /dev/null +++ b/src/components/types/adventurous-planners.tsx @@ -0,0 +1,9 @@ +import { Navbar } from "@/components/navbar"; + +export default function AdventurousPlanners() { + return ( +
+

Hello World - The Adventure Planners

+
+ ); +} \ No newline at end of file diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index c9f329a..a73250d 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -17,6 +17,7 @@ const buttonVariants = cva( secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", link: "text-primary underline-offset-4 hover:underline", + pressed: "!rounded-full bg-white text-pmred-500 border-4 border-pmblue-500 font-bold shadow-[6px_6px_0px_0px_rgba(36,67,141,1)] hover:translate-x-[4px] hover:translate-y-[4px] hover:shadow-[2px_2px_0px_0px_rgba(36,67,141,1)] active:translate-x-[6px] active:translate-y-[6px] active:shadow-none", }, size: { default: "h-9 px-4 py-2 has-[>svg]:px-3", diff --git a/src/lib/constants/coupleTypes.ts b/src/lib/constants/coupleTypes.ts index 1979e5b..c897f70 100644 --- a/src/lib/constants/coupleTypes.ts +++ b/src/lib/constants/coupleTypes.ts @@ -4,7 +4,7 @@ import { pgEnum } from "drizzle-orm/pg-core"; // This file should be imported by both frontend components and backend API routes export enum CoupleTypeCode { - ADVENTUROUS_PLANNERS = "ADVENTUROUS_PLANNERS", + OLIN_AND_URIS = "OLIN_AND_URIS", COZY_HOMEBODIES = "COZY_HOMEBODIES", SOCIAL_BUTTERFLIES = "SOCIAL_BUTTERFLIES", THOUGHTFUL_DEEP_THINKERS = "THOUGHTFUL_DEEP_THINKERS", @@ -48,6 +48,15 @@ export interface CoupleTypeDefinition { accent: string; }; }; + // New fields for detailed type page + dimensions?: { + planned: number; // 0-100, percentage for Planned (opposite is Spontaneous) + physical: number; // 0-100, percentage for Physical (opposite is Emotional) + reflective: number; // 0-100, percentage for Reflective (opposite is Active) + }; + dateIdeas?: string[]; + conflictResolutionText?: string; + loveLanguages?: string[]; } // Utility functions for working with couple types @@ -89,7 +98,7 @@ export const calculateCoupleType = ( // Simple algorithm - you can make this more sophisticated if (combinedScores.adventure > 7 && combinedScores.communication > 6) { - return CoupleTypeCode.ADVENTUROUS_PLANNERS; + return CoupleTypeCode.OLIN_AND_URIS; } else if (combinedScores.adventure < 4 && combinedScores.values > 7) { return CoupleTypeCode.COZY_HOMEBODIES; } else if (combinedScores.communication > 8) { @@ -105,13 +114,13 @@ export const calculateCoupleType = ( // Example couple type definitions (you can expand these) export const COUPLE_TYPES: Record = { - [CoupleTypeCode.ADVENTUROUS_PLANNERS]: { - code: CoupleTypeCode.ADVENTUROUS_PLANNERS, - displayName: "The Adventure Planners", + [CoupleTypeCode.OLIN_AND_URIS]: { + code: CoupleTypeCode.OLIN_AND_URIS, + displayName: "Olin & Uris", shortDescription: "Organized explorers who love planning their next big adventure", description: { summary: - "You both love exploring new places and making detailed plans for your adventures. You're the couple with the shared Pinterest board full of travel destinations and the Google Sheets itinerary for every trip.", + "Iris & Uris couples are the planners who still leave space for feelings the ones who color-code their calendars and check in on how you're doing. Thoughtful and intentional, they balance logic with empathy, creating steady, nurturing relationships that grow through reflection and honest connection. Whether mapping out their next adventure or simply enjoying quiet time together, Iris & Uris couples make love feel calm, considered, and kind.", traits: ["Organized", "Adventurous", "Future-focused", "Detail-oriented"], strengths: [ "Great at planning trips", @@ -142,6 +151,27 @@ export const COUPLE_TYPES: Record = { accent: "#F59E0B", }, }, + dimensions: { + planned: 68, + physical: 39, + reflective: 88, + }, + dateIdeas: [ + "Cozy coffee shop study dates", + "Museum strolls with shared playlists", + "Cozy coffee shop study dates", + "Museum strolls with shared playlists", + "Cozy coffee shop study dates", + ], + conflictResolutionText: + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + loveLanguages: [ + "Quality time", + "Words of affirmation", + "Thoughtful gestures that say 'I remembered'", + "Museum strolls with shared playlists", + "Cozy coffee shop study dates", + ], }, [CoupleTypeCode.COZY_HOMEBODIES]: {