From f40a6d42889f2629e233547686a3cc2401b1e84b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matilda=20Landstr=C3=B6m?=
Date: Fri, 14 Jun 2024 10:34:31 +0200
Subject: [PATCH 1/2] Upgrade packages (npm audit fix)
---
package-lock.json | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index f6b074937..250fa8c2c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6890,12 +6890,12 @@
}
},
"node_modules/braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"devOptional": true,
"dependencies": {
- "fill-range": "^7.0.1"
+ "fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
@@ -9453,9 +9453,9 @@
}
},
"node_modules/fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"devOptional": true,
"dependencies": {
"to-regex-range": "^5.0.1"
From 3e54d3c29e26dd6d5235f494dd7a45b90c75c4bb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matilda=20Landstr=C3=B6m?=
Date: Fri, 14 Jun 2024 10:35:41 +0200
Subject: [PATCH 2/2] chore: add memberships "endoint"
---
.../profile/@creditCards/page.module.css | 12 ++++++
.../my-pages/profile/@creditCards/page.tsx | 19 ++++++++
.../@membershipCards/[...catchAll]/page.tsx | 1 +
.../profile/@membershipCards/page.module.css | 22 ++++++++++
.../profile/@membershipCards/page.tsx | 43 +++++++++++++++++++
.../(protected)/my-pages/profile/layout.tsx | 2 +
server/routers/user/output.ts | 10 +++++
server/routers/user/query.ts | 43 +++++++++++++++++++
types/components/myPages/myProfile/layout.ts | 1 +
utils/user.ts | 23 +++++++++-
10 files changed, 175 insertions(+), 1 deletion(-)
create mode 100644 app/[lang]/(live)/(protected)/my-pages/profile/@membershipCards/[...catchAll]/page.tsx
create mode 100644 app/[lang]/(live)/(protected)/my-pages/profile/@membershipCards/page.module.css
create mode 100644 app/[lang]/(live)/(protected)/my-pages/profile/@membershipCards/page.tsx
diff --git a/app/[lang]/(live)/(protected)/my-pages/profile/@creditCards/page.module.css b/app/[lang]/(live)/(protected)/my-pages/profile/@creditCards/page.module.css
index 76b9b1c13..3b9f6e70a 100644
--- a/app/[lang]/(live)/(protected)/my-pages/profile/@creditCards/page.module.css
+++ b/app/[lang]/(live)/(protected)/my-pages/profile/@creditCards/page.module.css
@@ -8,3 +8,15 @@
display: grid;
gap: var(--Spacing-x1);
}
+
+.card {
+ margin-top: 2rem;
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ grid-template-rows: repeat(3, auto);
+ gap: 0.5rem;
+}
+
+.subTitle {
+ grid-column: span 2;
+}
diff --git a/app/[lang]/(live)/(protected)/my-pages/profile/@creditCards/page.tsx b/app/[lang]/(live)/(protected)/my-pages/profile/@creditCards/page.tsx
index 9c650c8bc..c36fefabf 100644
--- a/app/[lang]/(live)/(protected)/my-pages/profile/@creditCards/page.tsx
+++ b/app/[lang]/(live)/(protected)/my-pages/profile/@creditCards/page.tsx
@@ -1,3 +1,5 @@
+import { serverClient } from "@/lib/trpc/server"
+
import { PlusCircleIcon } from "@/components/Icons"
import Link from "@/components/TempDesignSystem/Link"
import Body from "@/components/TempDesignSystem/Text/Body"
@@ -8,6 +10,8 @@ import styles from "./page.module.css"
export default async function CreditCardSlot() {
const { formatMessage } = await getIntl()
+ const creditCards = await serverClient().user.creditCards()
+
return (
@@ -20,6 +24,21 @@ export default async function CreditCardSlot() {
})}
diff --git a/app/[lang]/(live)/(protected)/my-pages/profile/@membershipCards/[...catchAll]/page.tsx b/app/[lang]/(live)/(protected)/my-pages/profile/@membershipCards/[...catchAll]/page.tsx
new file mode 100644
index 000000000..03a82e5f5
--- /dev/null
+++ b/app/[lang]/(live)/(protected)/my-pages/profile/@membershipCards/[...catchAll]/page.tsx
@@ -0,0 +1 @@
+export { default } from "../page"
diff --git a/app/[lang]/(live)/(protected)/my-pages/profile/@membershipCards/page.module.css b/app/[lang]/(live)/(protected)/my-pages/profile/@membershipCards/page.module.css
new file mode 100644
index 000000000..3b9f6e70a
--- /dev/null
+++ b/app/[lang]/(live)/(protected)/my-pages/profile/@membershipCards/page.module.css
@@ -0,0 +1,22 @@
+.container {
+ display: grid;
+ gap: var(--Spacing-x3);
+ max-width: 510px;
+}
+
+.content {
+ display: grid;
+ gap: var(--Spacing-x1);
+}
+
+.card {
+ margin-top: 2rem;
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ grid-template-rows: repeat(3, auto);
+ gap: 0.5rem;
+}
+
+.subTitle {
+ grid-column: span 2;
+}
diff --git a/app/[lang]/(live)/(protected)/my-pages/profile/@membershipCards/page.tsx b/app/[lang]/(live)/(protected)/my-pages/profile/@membershipCards/page.tsx
new file mode 100644
index 000000000..6da3bb6df
--- /dev/null
+++ b/app/[lang]/(live)/(protected)/my-pages/profile/@membershipCards/page.tsx
@@ -0,0 +1,43 @@
+import { serverClient } from "@/lib/trpc/server"
+
+import { PlusCircleIcon } from "@/components/Icons"
+import Link from "@/components/TempDesignSystem/Link"
+import Body from "@/components/TempDesignSystem/Text/Body"
+import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
+import { getIntl } from "@/i18n"
+
+import styles from "./page.module.css"
+
+export default async function MembershipCardSlot() {
+ const { formatMessage } = await getIntl()
+ const membershipCards = await serverClient().user.membershipCards()
+
+ return (
+
+
+
+ {formatMessage({ id: "My membership cards" })}
+
+
+ {membershipCards &&
+ membershipCards.length > 0 &&
+ membershipCards.map((card, idx) => (
+
+
+ Name: {card.membershipType}
+
+ Current Points: {card.currentPoints}
+ Member Since: {card.memberSince}
+ Number: {card.membershipNumber}
+ Expiration Date: {card.expirationDate.split("T")[0]}
+
+ ))}
+
+
+
+ {formatMessage({ id: "Add new card" })}
+
+
+
+ )
+}
diff --git a/app/[lang]/(live)/(protected)/my-pages/profile/layout.tsx b/app/[lang]/(live)/(protected)/my-pages/profile/layout.tsx
index a5983461d..d067f0277 100644
--- a/app/[lang]/(live)/(protected)/my-pages/profile/layout.tsx
+++ b/app/[lang]/(live)/(protected)/my-pages/profile/layout.tsx
@@ -6,6 +6,7 @@ export default function ProfileLayout({
children,
communication,
creditCards,
+ membershipCards,
profile,
}: React.PropsWithChildren) {
return (
@@ -14,6 +15,7 @@ export default function ProfileLayout({
{profile}
{creditCards}
+ {membershipCards}
{communication}
diff --git a/server/routers/user/output.ts b/server/routers/user/output.ts
index 2e4b8db10..8a7f3b06b 100644
--- a/server/routers/user/output.ts
+++ b/server/routers/user/output.ts
@@ -181,3 +181,13 @@ export const getCreditCardsSchema = z.object({
})
),
})
+
+export const getMembershipCardsSchema = z.array(
+ z.object({
+ currentPoints: z.number(),
+ expirationDate: z.string(),
+ membershipNumber: z.string(),
+ memberSince: z.string(),
+ membershipType: z.string(),
+ })
+)
diff --git a/server/routers/user/query.ts b/server/routers/user/query.ts
index 732ec18a1..0a989b56d 100644
--- a/server/routers/user/query.ts
+++ b/server/routers/user/query.ts
@@ -5,6 +5,7 @@ import { protectedProcedure, router } from "@/server/trpc"
import { countries } from "@/components/TempDesignSystem/Form/Country/countries"
import * as maskValue from "@/utils/maskValue"
+import { getMembershipCards } from "@/utils/user"
import {
friendTransactionsInput,
@@ -14,6 +15,7 @@ import {
import {
getCreditCardsSchema,
getFriendTransactionsSchema,
+ getMembershipCardsSchema,
getStaysSchema,
getUserSchema,
} from "./output"
@@ -348,4 +350,45 @@ export const userQueryRouter = router({
return verifiedData.data.data
}),
+
+ membershipCards: protectedProcedure.query(async function ({ ctx }) {
+ const apiResponse = await api.get(api.endpoints.v1.profile, {
+ cache: "no-store",
+ headers: {
+ Authorization: `Bearer ${ctx.session.token.access_token}`,
+ },
+ })
+
+ if (!apiResponse.ok) {
+ // switch (apiResponse.status) {
+ // case 400:
+ // throw badRequestError()
+ // case 401:
+ // throw unauthorizedError()
+ // case 403:
+ // throw forbiddenError()
+ // default:
+ // throw internalServerError()
+ // }
+ console.info(`API Response Failed - Getting Membership Cards`)
+ console.info(`User: (${JSON.stringify(ctx.session.user)})`)
+ console.error(apiResponse)
+ }
+
+ const apiJson = await apiResponse.json()
+
+ const verifiedData = getMembershipCardsSchema.safeParse(
+ apiJson.data.attributes.memberships
+ )
+
+ if (!verifiedData.success) {
+ console.info(`Failed to validate Memberships Cards Data`)
+ console.info(`User: (${JSON.stringify(ctx.session.user)})`)
+ console.error(verifiedData.error)
+ return null
+ }
+ const cards = getMembershipCards(verifiedData.data)
+
+ return cards
+ }),
})
diff --git a/types/components/myPages/myProfile/layout.ts b/types/components/myPages/myProfile/layout.ts
index be50c920a..3042210f1 100644
--- a/types/components/myPages/myProfile/layout.ts
+++ b/types/components/myPages/myProfile/layout.ts
@@ -1,5 +1,6 @@
export type ProfileLayoutProps = {
communication: React.ReactNode
creditCards: React.ReactNode
+ membershipCards: React.ReactNode
profile: React.ReactNode
}
diff --git a/utils/user.ts b/utils/user.ts
index 9260118b0..052826eaf 100644
--- a/utils/user.ts
+++ b/utils/user.ts
@@ -1,12 +1,33 @@
+import { z } from "zod"
+
+import { getMembershipCardsSchema } from "@/server/routers/user/output"
+
import { User } from "@/types/user"
+enum scandicMemberships {
+ guestpr = "guestpr",
+ scandicfriends = "scandicfriend's",
+}
+
export function getMembership(memberships: User["memberships"]) {
return memberships?.find(
(membership) =>
- membership.membershipType.toLowerCase() === "guestpr" || "scandicfriend's"
+ membership.membershipType.toLowerCase() === scandicMemberships.guestpr
)
}
+export function getMembershipCards(
+ memberships: z.infer
+) {
+ return memberships.filter(function (membership) {
+ return (
+ membership.membershipType.toLowerCase() !== scandicMemberships.guestpr &&
+ membership.membershipType.toLowerCase() !==
+ scandicMemberships.scandicfriends
+ )
+ })
+}
+
export function getInitials(
firstName: User["firstName"],
lastName: User["lastName"]