From 677d4be674b3bad8a14e7ba313e168e562da8c05 Mon Sep 17 00:00:00 2001 From: centauridesigns Date: Sun, 22 Dec 2024 02:18:03 +0100 Subject: [PATCH] add remaining cards --- src/app/components/Membership.tsx | 45 +++++++++++++++++++++ src/app/components/MembershipCard.tsx | 38 +++++++++++------- src/app/components/MembershipItem.tsx | 48 +++++++++++++++++++++++ src/app/components/ProfileCard.tsx | 56 ++++++++++++++------------- src/app/components/PurchaseCard.tsx | 35 +++++++++++++++++ src/app/components/PurchaseItem.tsx | 54 ++++++++++++++++++++++++++ src/app/components/Subscription.tsx | 43 ++++++++++++++++++++ src/app/globals.css | 47 +++------------------- src/app/page.tsx | 1 - src/pages/main.tsx | 21 ++++++++-- tailwind.config.ts | 52 +++++++++++++++++++++++++ 11 files changed, 352 insertions(+), 88 deletions(-) create mode 100644 src/app/components/Membership.tsx create mode 100644 src/app/components/MembershipItem.tsx create mode 100644 src/app/components/PurchaseCard.tsx create mode 100644 src/app/components/PurchaseItem.tsx create mode 100644 src/app/components/Subscription.tsx diff --git a/src/app/components/Membership.tsx b/src/app/components/Membership.tsx new file mode 100644 index 0000000..6ff83a1 --- /dev/null +++ b/src/app/components/Membership.tsx @@ -0,0 +1,45 @@ +export interface Membership { + id: string; + title: string; + image: string; + perks: string[]; + specialPerks: string[]; + priceYear: number; + endsAtNewYears: boolean; + duration: number | false; +} + +export const membershipData: Record = { + uiog: { + id: "uiog", + title: "UiO Gaming", + perks: [ + "access to Discord Channels", + "access to all Weekly Events" + ], + specialPerks: [ + "access to Cabin Trip", + "access to LAN Events (2x / year)" + ], + image: "/images/uiog.png", + priceYear: 50, + endsAtNewYears: true, + duration: false + }, + dns: { + id: "dns", + title: "DNS Standard", + perks: [ + "access to specific Events", + "discounts on select Events", + "voting rights and ability to run for election at Neuf" + ], + specialPerks: [ + "member pricing at Neuf, Samfundet in Trondheim, and Studentersamfunnet in Bergen" + ], + image: "/images/neuf.png", + priceYear: 100, + endsAtNewYears: false, + duration: 365 + } +}; diff --git a/src/app/components/MembershipCard.tsx b/src/app/components/MembershipCard.tsx index e9527ec..bdc1f73 100644 --- a/src/app/components/MembershipCard.tsx +++ b/src/app/components/MembershipCard.tsx @@ -1,25 +1,33 @@ -import React from "react"; +import React from 'react'; +import MembershipItem from './MembershipItem'; +import { Membership, membershipData } from './Membership'; +import { Subscription, getSubscriptionDetails } from './Subscription'; -interface MembershipCard { - hasUiogMembership: boolean; - hasDnsMembership: boolean; +interface MembershipCardProps { + uiogSubscription: Subscription | null; + dnsSubscription: Subscription | null; } -const membershipPerks = [ - -]; - -const MembershipCard: React.FC = ({ - hasUiogMembership, - hasDnsMembership, +const MembershipCard: React.FC = ({ + uiogSubscription, + dnsSubscription }) => { return ( -
-
-

My Perks

+
+
+

My Perks

+ {uiogSubscription && ( + + )} + {dnsSubscription && ( + + )} + {!uiogSubscription && !dnsSubscription && ( +

No current memberships. Please consider purchasing one.

+ )}
); } -export default MembershipCard; \ No newline at end of file +export default MembershipCard; diff --git a/src/app/components/MembershipItem.tsx b/src/app/components/MembershipItem.tsx new file mode 100644 index 0000000..8963045 --- /dev/null +++ b/src/app/components/MembershipItem.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { Membership } from './Membership'; +import { Subscription, getSubscriptionDetails } from './Subscription'; + +interface MembershipItemProps { + subscription: Subscription; +} + +const MembershipItem: React.FC = ({ subscription }) => { + const { membership, daysLeft } = getSubscriptionDetails(subscription); + + return ( +
+
+
+
+ User Icon +
+
+

{membership.title}

+

kr {membership.priceYear} / year

+
+
+

{daysLeft} days left

+
+ +
+
+

Perks

+ {membership.perks.map((perk) => ( +

+ {perk}

+ ))} +
+
+ {membership.specialPerks.map((perk) => ( +

+ {perk}

+ ))} +
+
+
+ ); +}; + +export default MembershipItem; diff --git a/src/app/components/ProfileCard.tsx b/src/app/components/ProfileCard.tsx index 18eb281..a1651a0 100644 --- a/src/app/components/ProfileCard.tsx +++ b/src/app/components/ProfileCard.tsx @@ -6,7 +6,7 @@ interface ProfileCardProps { hasDnsMembership: boolean; } -const membershipStatuses = { +const subscriptionStatuses = { Combined: { title: "Combined", description: "You have both memberships, and can enjoy all privileges.", @@ -66,17 +66,17 @@ const ProfileCard: React.FC = ({ ? "DNS" : "None"; - const membershipStatus = membershipStatuses[statusKey]; + const membershipStatus = subscriptionStatuses[statusKey]; return (
{/* Top Row */} -
+
{/* User Icon and Name */} -
-
+
+
User Icon = ({ />
-

wailern

+

wailern

{membershipStatus.tag}

{/* Log Out Button */} -
{/* Membership Status */} - {["UIOG", "DNS"].map((key) => { - const isActive = key === "UIOG" ? hasUiogMembership : hasDnsMembership; - const displayName = key === "UIOG" ? "UiO Gaming" : "DNS Standard"; - return ( -
-

{isActive ? `✅ ${displayName}` : `❌ ${displayName}`}

-
{key === "UIOG" ? "🎮" : "🐟"}
-
- ); - })} +
+ {["UIOG", "DNS"].map((key) => { + const isActive = key === "UIOG" ? hasUiogMembership : hasDnsMembership; + const displayName = key === "UIOG" ? "UiO Gaming" : "DNS Standard"; + return ( +
+

{isActive ? `✅ ${displayName}` : `❌ ${displayName}`}

+
{key === "UIOG" ? "🎮" : "🐟"}
+
+ ); + })} +
{/* Description */}
-

{membershipStatus.description}

+

{membershipStatus.description}

{membershipStatus.blurb && (
-

{membershipStatus.blurb}

+

{membershipStatus.blurb}

{membershipStatus.missingPerks.map((perk, index) => (

+ {perk} diff --git a/src/app/components/PurchaseCard.tsx b/src/app/components/PurchaseCard.tsx new file mode 100644 index 0000000..6484868 --- /dev/null +++ b/src/app/components/PurchaseCard.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { membershipData } from "./Membership"; // Importing membership data from Membership.tsx +import PurchaseItem from "./PurchaseItem"; // Importing PurchaseItem component + +interface PurchaseCardProps { + hasUiogMembership: boolean; + hasDnsMembership: boolean; +} + +const PurchaseCard: React.FC = ({ + hasUiogMembership, + hasDnsMembership, +}) => { + const uiogMembership = membershipData.uiog + const dnsMembership = membershipData.dns + + return ( +

+
+

Become a Member

+ {!hasUiogMembership && uiogMembership && ( + + )} + {!hasDnsMembership && dnsMembership && ( + + )} + {(hasUiogMembership && hasDnsMembership) && ( +

No additional memberships are available for purchase.

+ )} +
+
+ ); +} + +export default PurchaseCard; diff --git a/src/app/components/PurchaseItem.tsx b/src/app/components/PurchaseItem.tsx new file mode 100644 index 0000000..f856f03 --- /dev/null +++ b/src/app/components/PurchaseItem.tsx @@ -0,0 +1,54 @@ +import React from "react"; + +interface PurchaseItem { + membership: { + id: string; + title: string; + image: string; + priceYear: number; + perks: string[]; + specialPerks: string[]; + } +} + +const PurchaseItem: React.FC = ({ + membership +}) => { + return ( +
+
+
+
+ User Icon +
+
+

{membership.title}

+

kr {membership.priceYear} / year

+
+
+
+

+

+
+
+
+
+

Perks

+ {membership.perks.map((perk) => ( +

+ {perk}

+ ))} +
+
+ {membership.specialPerks.map((perk) => ( +

+ {perk}

+ ))} +
+
+
+ ) +} + +export default PurchaseItem; \ No newline at end of file diff --git a/src/app/components/Subscription.tsx b/src/app/components/Subscription.tsx new file mode 100644 index 0000000..ade4af7 --- /dev/null +++ b/src/app/components/Subscription.tsx @@ -0,0 +1,43 @@ +import { Membership, membershipData } from './Membership'; + +export interface Subscription { + membershipId: string; + startDate: Date; +} + +export function calculateDaysLeft(subscription: Subscription): number { + const today = new Date(); + const currentYear = today.getFullYear(); + const membership = membershipData[subscription.membershipId]; + let daysLeft = 0; + + if (membership.endsAtNewYears) { + // if the membership ends at new years, we need to calculate the days left until the next new years + const nextYear = currentYear + 1; + const newYearsDay = new Date(nextYear, 0, 1); + + const timeDiff = newYearsDay.getTime() - today.getTime(); + daysLeft = Math.ceil(timeDiff / (1000 * 60 * 60 * 24)); + + } + + else { + // if the membership does not end at new years, we can just calculate the days based on the duration of the membership + if (!membership.duration) { + return 0; + } + + const duration: number = parseInt(membership.duration.toString()); + + daysLeft = Math.ceil((subscription.startDate.getTime() + duration * 24 * 60 * 60 * 1000 - today.getTime()) / (1000 * 60 * 60 * 24)); + } + + return daysLeft; +} + +export function getSubscriptionDetails(subscription: Subscription): { membership: Membership; daysLeft: number } { + return { + membership: membershipData[subscription.membershipId], + daysLeft: calculateDaysLeft(subscription) + }; +} diff --git a/src/app/globals.css b/src/app/globals.css index 95f56f9..6808e75 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -7,6 +7,8 @@ --foreground: #261F31; --primary: #392e49; --overlay: #4c3e62; + --overlay-2: #55456D; + --overlay-3: #8E73A4; --overlay-bright: #6d517b; --outline: #987cc4; --outline2: #dba0f6; @@ -23,6 +25,8 @@ --foreground: #261F31; --primary: #392e49; --overlay: #4c3e62; + --overlay-2: #55456D; + --overlay-3: #8E73A4; --overlay-bright: #6d517b; --outline: #987cc4; --outline2: #dba0f6; @@ -52,40 +56,7 @@ h2 { p { font-family: var(--font-nunito); font-weight: 400; - font-size: 1.45rem; - color: white; -} - -.pb { - font-weight: 800; -} - -.ph { - font-weight: 600; - font-size: 1.25rem; -} - -.po { - color: var(--outline); -} - -.pd { - font-size: 1.45rem; - color: var(--foreground); -} - -.pp { - font-size: 1.5rem; - color: var(--foreground) -} - -.ps { - font-size: 1.25rem; -} - -.psh { - font-size: 1.25rem; - font-weight: 600; + font-size: 0.85rem; } .header { @@ -94,14 +65,6 @@ p { color: white !important; } -.header-light { - font-weight: 400; - font-size: 2rem; - color: white !important; -} - - - @layer utilities { .text-balance { text-wrap: balance; diff --git a/src/app/page.tsx b/src/app/page.tsx index 2b9bf7b..ad53b24 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,7 +1,6 @@ import ButtonSpecial from "./components/ButtonSpecial"; import Button from "./components/Button"; import Image from "next/image"; -import ProfileCard from "./components/ProfileCard"; import { Nunito, Rubik } from 'next/font/google'; diff --git a/src/pages/main.tsx b/src/pages/main.tsx index 5c7433f..74cb3c4 100644 --- a/src/pages/main.tsx +++ b/src/pages/main.tsx @@ -2,12 +2,27 @@ import ProfileCard from '@/app/components/ProfileCard'; import MembershipCard from '@/app/components/MembershipCard'; import React from 'react'; import "../app/globals.css"; +import PurchaseCard from '@/app/components/PurchaseCard'; +import { Subscription } from '@/app/components/Subscription'; + +let dnsSubscription: Subscription | null = null; +let uiogSubscription: Subscription | null = null; + + +uiogSubscription = { + membershipId: "uiog", + startDate: new Date(new Date().setDate(new Date().getDate() - 111)), +}; + +const hasDns = dnsSubscription !== null; +const hasUiog = uiogSubscription !== null; export default function Main() { return ( -
- - +
+ + +
); }; \ No newline at end of file diff --git a/tailwind.config.ts b/tailwind.config.ts index c489b34..4208236 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -13,6 +13,8 @@ const config: Config = { 'foreground': "var(--foreground)", 'primary': "var(--primary)", 'overlay': "var(--overlay)", + 'overlay-2': "var(--overlay-2)", + 'overlay-3': "var(--overlay-3)", 'overlay-bright': "var(--overlay-bright)", 'outline': "var(--outline)", 'outline2': "var(--outline2)", @@ -22,15 +24,65 @@ const config: Config = { 'failure': "var(--failure)", 'mixed': "var(--mixed)", }, + fontWeight: { + thin: "100", + extralight: "200", + light: "300", + normal: "400", + medium: "500", + semibold: "600", + bold: "700", + extrabold: "800", + black: "900", + }, }, borderRadius: { DEFAULT: "0.5rem", + sms: "0.1rem", sm: "0.125rem", + sml: "0.25rem", + + mds: "0.3rem", md: "0.375rem", + mdl: "0.5rem", + + lgs: "0.75rem", lg: "1rem", + xl: "2rem", full: "9999px", }, + fontSize: { + xs: "0.75rem", + sm: "0.875rem", + base: "1rem", + md: "1.105rem", + lg: "1.125rem", + xl: "1.25rem" + }, + fontWeight: { + thin: "100", + extralight: "200", + light: "300", + normal: "400", + medium: "500", + semibold: "600", + bold: "700", + extrabold: "800", + black: "900", + }, + outlineWidth: { + DEFAULT: "2px", + 1: "1px", + 1.25: "1.25px", + 1.5: "1.5px", + 1.75: "1.75px", + 2: "2px", + 3: "3px", + 4: "4px", + 6: "6px", + 8: "8px", + }, }, plugins: [], };