Merged in feat/sw-2867-move-user-router-to-trpc-package (pull request #2428)
Move user router to trpc package * Move more schemas in hotel router * Fix deps * fix getNonContentstackUrls * Fix import error * Fix entry error handling * Fix generateMetadata metrics * Fix alertType enum * Fix duplicated types * lint:fix * Merge branch 'master' into feat/sw-2863-move-contentstack-router-to-trpc-package * Fix broken imports * Move booking router to trpc package * Move partners router to trpc package * Move autocomplete router to trpc package * Move booking router to trpc package * Remove translations from My Pages navigation trpc procedure * Move navigation router to trpc package * Move user router to trpc package * Merge branch 'master' into feat/sw-2862-move-booking-router-to-trpc-package * Merge branch 'feat/sw-2862-move-booking-router-to-trpc-package' into feat/sw-2865-move-navigation-router-to-trpc-package * Merge branch 'master' into feat/sw-2865-move-navigation-router-to-trpc-package * Merge branch 'master' into feat/sw-2865-move-navigation-router-to-trpc-package * Merge branch 'master' into feat/sw-2865-move-navigation-router-to-trpc-package * Merge branch 'feat/sw-2865-move-navigation-router-to-trpc-package' into feat/sw-2867-move-user-router-to-trpc-package * Merge branch 'master' into feat/sw-2867-move-user-router-to-trpc-package Approved-by: Linus Flood
This commit is contained in:
24
packages/common/constants/routes/signup.ts
Normal file
24
packages/common/constants/routes/signup.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { LangRoute } from "@scandic-hotels/common/constants/routes/langRoute"
|
||||
|
||||
export const signup: LangRoute = {
|
||||
en: "/en/scandic-friends/join",
|
||||
sv: "/sv/scandic-friends/bli-medlem",
|
||||
no: "/no/scandic-friends/registrer-deg",
|
||||
fi: "/fi/scandic-friends/liity-jaseneksi",
|
||||
da: "/da/scandic-friends/tilmeld-dig",
|
||||
de: "/de/scandic-friends/mitglied-werden",
|
||||
}
|
||||
|
||||
export const signupVerify: LangRoute = {
|
||||
en: `${signup.en}/verify`,
|
||||
sv: `${signup.sv}/verifiera`,
|
||||
no: `${signup.no}/bekreft`,
|
||||
fi: `${signup.fi}/vahvista`,
|
||||
da: `${signup.da}/bekraeft`,
|
||||
de: `${signup.de}/verifizieren`,
|
||||
}
|
||||
|
||||
export function isSignupPage(path: string): boolean {
|
||||
const signupPaths = [...Object.values(signup), ...Object.values(signupVerify)]
|
||||
return signupPaths.some((signupPath) => signupPath.includes(path))
|
||||
}
|
||||
@@ -22,9 +22,8 @@
|
||||
"./utils/languages": "./utils/languages.ts",
|
||||
"./utils/chunk": "./utils/chunk.ts",
|
||||
"./utils/isDefined": "./utils/isDefined.ts",
|
||||
"./utils/zod/stringValidator": "./utils/zod/stringValidator.ts",
|
||||
"./utils/zod/numberValidator": "./utils/zod/numberValidator.ts",
|
||||
"./utils/zod/arrayValidator": "./utils/zod/arrayValidator.ts",
|
||||
"./utils/maskValue": "./utils/maskValue.ts",
|
||||
"./utils/zod/*": "./utils/zod/*.ts",
|
||||
"./constants/language": "./constants/language.ts",
|
||||
"./constants/membershipLevels": "./constants/membershipLevels.ts",
|
||||
"./constants/paymentMethod": "./constants/paymentMethod.ts",
|
||||
|
||||
52
packages/common/utils/maskValue.ts
Normal file
52
packages/common/utils/maskValue.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
function maskAll(str: string) {
|
||||
return "*".repeat(str.length)
|
||||
}
|
||||
|
||||
function maskAllButFirstChar(str: string) {
|
||||
const first = str[0]
|
||||
const rest = str.substring(1)
|
||||
const restMasked = maskAll(rest)
|
||||
|
||||
return `${first}${restMasked}`
|
||||
}
|
||||
|
||||
function maskAllButLastTwoChar(str: string) {
|
||||
const lastTwo = str.slice(-2)
|
||||
const rest = str.substring(0, str.length - 2)
|
||||
const restMasked = maskAll(rest)
|
||||
|
||||
return `${restMasked}${lastTwo}`
|
||||
}
|
||||
|
||||
export function email(str: string) {
|
||||
const parts = str.split("@")
|
||||
|
||||
const aliasMasked = maskAllButFirstChar(parts[0])
|
||||
|
||||
if (parts[1]) {
|
||||
const domainParts = parts[1].split(".")
|
||||
if (domainParts.length > 1) {
|
||||
const domainTLD = domainParts.pop()
|
||||
const domainPartsMasked = domainParts
|
||||
.map((domainPart, i) => {
|
||||
return maskAllButFirstChar(domainPart)
|
||||
})
|
||||
.join(".")
|
||||
return `${aliasMasked}@${domainPartsMasked}.${domainTLD}`
|
||||
}
|
||||
}
|
||||
|
||||
return maskAllButFirstChar(str)
|
||||
}
|
||||
|
||||
export function phone(str: string) {
|
||||
return maskAllButLastTwoChar(str)
|
||||
}
|
||||
|
||||
export function text(str: string) {
|
||||
return maskAllButFirstChar(str)
|
||||
}
|
||||
|
||||
export function all(str: string) {
|
||||
return maskAll(str)
|
||||
}
|
||||
27
packages/common/utils/maskvalue.test.ts
Normal file
27
packages/common/utils/maskvalue.test.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { describe, expect, test } from "vitest"
|
||||
|
||||
import { all, email, phone, text } from "./maskValue"
|
||||
|
||||
describe("Mask value", () => {
|
||||
test("masks e-mails properly", () => {
|
||||
expect(email("test@example.com")).toBe("t***@e******.com")
|
||||
expect(email("test@sub.example.com")).toBe("t***@s**.e******.com")
|
||||
expect(email("test_no_atexample.com")).toBe("t********************")
|
||||
expect(email("test_no_dot@examplecom")).toBe("t*********************")
|
||||
expect(email("test_no_at_no_dot_com")).toBe("t********************")
|
||||
})
|
||||
|
||||
test("masks phone number properly", () => {
|
||||
expect(phone("0000000000")).toBe("********00")
|
||||
})
|
||||
|
||||
test("masks text strings properly", () => {
|
||||
expect(text("test")).toBe("t***")
|
||||
expect(text("test.with.dot")).toBe("t************")
|
||||
})
|
||||
|
||||
test("masks whole string properly", () => {
|
||||
expect(all("test")).toBe("****")
|
||||
expect(all("123jknasd@iajsd.c")).toBe("*****************")
|
||||
})
|
||||
})
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Lang } from "../constants/language"
|
||||
|
||||
export function removeMultipleSlashes(pathname: string) {
|
||||
return pathname.replaceAll(/\/\/+/g, "/")
|
||||
}
|
||||
@@ -9,3 +11,52 @@ export function removeTrailingSlash(pathname: string) {
|
||||
}
|
||||
return pathname
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the TLD (top-level domain) for a given language.
|
||||
* @param lang - The language to get the TLD for
|
||||
* @returns The TLD for the given language
|
||||
*/
|
||||
export function getTldForLanguage(lang: Lang): string {
|
||||
switch (lang) {
|
||||
case Lang.sv:
|
||||
return "se"
|
||||
case Lang.no:
|
||||
return "no"
|
||||
case Lang.da:
|
||||
return "dk"
|
||||
case Lang.fi:
|
||||
return "fi"
|
||||
case Lang.de:
|
||||
return "de"
|
||||
default:
|
||||
return "com"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a URL with the correct TLD (top-level domain) based on lang, for current web.
|
||||
* @param params - Object containing path, lang, and baseUrl
|
||||
* @param params.path - The path to append to the URL
|
||||
* @param params.lang - The language to use for TLD
|
||||
* @param params.baseUrl - The base URL to use (e.g. https://www.scandichotels.com)
|
||||
* @returns The complete URL with language-specific TLD
|
||||
*/
|
||||
export function getCurrentWebUrl({
|
||||
path,
|
||||
lang,
|
||||
baseUrl = "https://www.scandichotels.com", // Fallback for ephemeral environments (e.g. deploy previews).
|
||||
}: {
|
||||
path: string
|
||||
lang: Lang
|
||||
baseUrl?: string
|
||||
}): string {
|
||||
const tld = getTldForLanguage(lang)
|
||||
const url = new URL(path, baseUrl)
|
||||
|
||||
if (tld !== "com") {
|
||||
url.host = url.host.replace(".com", `.${tld}`)
|
||||
}
|
||||
|
||||
return url.toString()
|
||||
}
|
||||
|
||||
46
packages/common/utils/zod/passwordValidator.ts
Normal file
46
packages/common/utils/zod/passwordValidator.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { z } from "zod"
|
||||
|
||||
export const passwordValidators = {
|
||||
length: {
|
||||
matcher: (password: string) =>
|
||||
password.length >= 10 && password.length <= 40,
|
||||
message: "10 to 40 characters",
|
||||
},
|
||||
hasUppercase: {
|
||||
matcher: (password: string) => /[A-Z]/.test(password),
|
||||
message: "1 uppercase letter",
|
||||
},
|
||||
hasLowercase: {
|
||||
matcher: (password: string) => /[a-z]/.test(password),
|
||||
message: "1 lowercase letter",
|
||||
},
|
||||
hasNumber: {
|
||||
matcher: (password: string) => /[0-9]/.test(password),
|
||||
message: "1 number",
|
||||
},
|
||||
hasSpecialChar: {
|
||||
matcher: (password: string) =>
|
||||
/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]+/.test(password),
|
||||
message: "1 special character",
|
||||
},
|
||||
}
|
||||
|
||||
export const passwordValidator = (msg = "Required field") =>
|
||||
z
|
||||
.string()
|
||||
.min(1, msg)
|
||||
.refine(passwordValidators.length.matcher, {
|
||||
message: passwordValidators.length.message,
|
||||
})
|
||||
.refine(passwordValidators.hasUppercase.matcher, {
|
||||
message: passwordValidators.hasUppercase.message,
|
||||
})
|
||||
.refine(passwordValidators.hasLowercase.matcher, {
|
||||
message: passwordValidators.hasLowercase.message,
|
||||
})
|
||||
.refine(passwordValidators.hasNumber.matcher, {
|
||||
message: passwordValidators.hasNumber.message,
|
||||
})
|
||||
.refine(passwordValidators.hasSpecialChar.matcher, {
|
||||
message: passwordValidators.hasSpecialChar.message,
|
||||
})
|
||||
26
packages/common/utils/zod/phoneValidator.ts
Normal file
26
packages/common/utils/zod/phoneValidator.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { z } from "zod"
|
||||
|
||||
export const phoneErrors = {
|
||||
PHONE_NUMBER_TOO_SHORT: "PHONE_NUMBER_TOO_SHORT",
|
||||
PHONE_REQUESTED: "PHONE_REQUESTED",
|
||||
} as const
|
||||
|
||||
export function phoneValidator(
|
||||
msg = "Required field",
|
||||
invalidMsg = "Invalid type"
|
||||
) {
|
||||
return z
|
||||
.string({ invalid_type_error: invalidMsg, required_error: msg })
|
||||
.min(5, phoneErrors.PHONE_NUMBER_TOO_SHORT)
|
||||
.superRefine((value, ctx) => {
|
||||
if (value) {
|
||||
const containsAlphabeticChars = /[a-z]/gi.test(value)
|
||||
if (containsAlphabeticChars) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: phoneErrors.PHONE_REQUESTED,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user