Handle phone numbers
This commit is contained in:
parent
42fd2f1377
commit
b9f8e5d771
|
|
@ -12,6 +12,8 @@ import { getVVCourseName } from "./composables";
|
||||||
import ItToggleSwitch from "@/components/ui/ItToggleSwitch.vue";
|
import ItToggleSwitch from "@/components/ui/ItToggleSwitch.vue";
|
||||||
import DatatransCembraDeviceFingerprint from "@/components/onboarding/DatatransCembraDeviceFingerprint.vue";
|
import DatatransCembraDeviceFingerprint from "@/components/onboarding/DatatransCembraDeviceFingerprint.vue";
|
||||||
import { getLocalSessionKey } from "@/statistics";
|
import { getLocalSessionKey } from "@/statistics";
|
||||||
|
import log from "loglevel";
|
||||||
|
import { normalizeSwissPhoneNumber, validatePhoneNumber } from "@/utils/phone";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
courseType: {
|
courseType: {
|
||||||
|
|
@ -126,6 +128,14 @@ function validateAddress() {
|
||||||
formErrors.value.personal.push(t("a.Land"));
|
formErrors.value.personal.push(t("a.Land"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (address.value.phone_number) {
|
||||||
|
const normalizedPhoneNumber = normalizeSwissPhoneNumber(address.value.phone_number);
|
||||||
|
log.debug("normalizedPhoneNumber", normalizedPhoneNumber);
|
||||||
|
if (!validatePhoneNumber(normalizedPhoneNumber)) {
|
||||||
|
formErrors.value.personal.push(t("a.Telefonnummer hat das falsche Format"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (withCembraInvoice.value) {
|
if (withCembraInvoice.value) {
|
||||||
if (!address.value.phone_number) {
|
if (!address.value.phone_number) {
|
||||||
formErrors.value.personal.push(t("a.Telefonnummer"));
|
formErrors.value.personal.push(t("a.Telefonnummer"));
|
||||||
|
|
@ -164,7 +174,8 @@ function validateAddress() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveAddress() {
|
async function saveAddress() {
|
||||||
const { country_code, organisation_country_code, ...profileData } = address.value;
|
const { country_code, organisation_country_code, phone_number, ...profileData } =
|
||||||
|
address.value;
|
||||||
const typedProfileData: Partial<User> = { ...profileData };
|
const typedProfileData: Partial<User> = { ...profileData };
|
||||||
|
|
||||||
typedProfileData.country = countries.value.find(
|
typedProfileData.country = countries.value.find(
|
||||||
|
|
@ -173,6 +184,7 @@ async function saveAddress() {
|
||||||
typedProfileData.organisation_country = countries.value.find(
|
typedProfileData.organisation_country = countries.value.find(
|
||||||
(c) => c.country_code === organisation_country_code
|
(c) => c.country_code === organisation_country_code
|
||||||
);
|
);
|
||||||
|
typedProfileData.phone_number = normalizeSwissPhoneNumber(phone_number);
|
||||||
|
|
||||||
await user.updateUserProfile(typedProfileData);
|
await user.updateUserProfile(typedProfileData);
|
||||||
}
|
}
|
||||||
|
|
@ -192,6 +204,8 @@ const executePayment = async () => {
|
||||||
// anyway, so it seems fine to do it here.
|
// anyway, so it seems fine to do it here.
|
||||||
const fullHost = `${window.location.protocol}//${window.location.host}`;
|
const fullHost = `${window.location.protocol}//${window.location.host}`;
|
||||||
|
|
||||||
|
address.value.phone_number = normalizeSwissPhoneNumber(address.value.phone_number);
|
||||||
|
|
||||||
itPost("/api/shop/vv/checkout/", {
|
itPost("/api/shop/vv/checkout/", {
|
||||||
redirect_url: fullHost,
|
redirect_url: fullHost,
|
||||||
address: address.value,
|
address: address.value,
|
||||||
|
|
@ -343,9 +357,7 @@ const executePayment = async () => {
|
||||||
data-cy="continue-pay"
|
data-cy="continue-pay"
|
||||||
@click="executePayment"
|
@click="executePayment"
|
||||||
>
|
>
|
||||||
<template v-if="withCembraInvoice">
|
<template v-if="withCembraInvoice">TODO: Mit Rechnung/Cembra bezahlen</template>
|
||||||
TODO: Mit Rechnung/Cembra bezahlen
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{ $t("a.Mit Kreditkarte bezahlen") }}
|
{{ $t("a.Mit Kreditkarte bezahlen") }}
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { normalizeSwissPhoneNumber, validatePhoneNumber } from "../phone";
|
||||||
|
|
||||||
|
describe("normalizeSwissPhoneNumber", () => {
|
||||||
|
it("should normalize a Swiss phone number", () => {
|
||||||
|
expect(normalizeSwissPhoneNumber("079 123 45 67")).toBe("+41791234567");
|
||||||
|
expect(normalizeSwissPhoneNumber("00 41 79 123 45 67")).toBe("+41791234567");
|
||||||
|
expect(normalizeSwissPhoneNumber("+41 (0)79 201 85 86")).toBe("+41792018586");
|
||||||
|
expect(normalizeSwissPhoneNumber("+41 79 201 85 86")).toBe("+41792018586");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should normalize remove spaces and special chars from foreign numbers", () => {
|
||||||
|
expect(normalizeSwissPhoneNumber("+49 30 12345678")).toBe("+493012345678");
|
||||||
|
expect(normalizeSwissPhoneNumber("+49-30-12345678")).toBe("+493012345678");
|
||||||
|
|
||||||
|
expect(normalizeSwissPhoneNumber("+33 1 23 45 67 89")).toBe("+33123456789");
|
||||||
|
expect(normalizeSwissPhoneNumber("+33-1-23-45-67-89")).toBe("+33123456789");
|
||||||
|
|
||||||
|
expect(normalizeSwissPhoneNumber("+39 06 12345678")).toBe("+390612345678");
|
||||||
|
expect(normalizeSwissPhoneNumber("+43 1 2345678")).toBe("+4312345678");
|
||||||
|
expect(normalizeSwissPhoneNumber("+423 234 5678")).toBe("+4232345678");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("validatePhoneNumber", () => {
|
||||||
|
it("should validate a Swiss phone number", () => {
|
||||||
|
expect(validatePhoneNumber("079 123 45 67")).toBe(true);
|
||||||
|
expect(validatePhoneNumber("00 41 79 123 45 67")).toBe(true);
|
||||||
|
expect(validatePhoneNumber("+41 (0)79 201 85 86")).toBe(true);
|
||||||
|
expect(validatePhoneNumber("+41 79 201 85 86")).toBe(true);
|
||||||
|
expect(validatePhoneNumber("026 418 01 31")).toBe(true);
|
||||||
|
|
||||||
|
expect(validatePhoneNumber("+41 79 201 85 86 8")).toBe(false);
|
||||||
|
expect(validatePhoneNumber("079 201 85 8")).toBe(false);
|
||||||
|
expect(validatePhoneNumber("aaa aaa aaa aaa")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should validate a foreign phone number", () => {
|
||||||
|
expect(validatePhoneNumber("+49 30 12345678")).toBe(true);
|
||||||
|
expect(validatePhoneNumber("+49 30 1234567")).toBe(false);
|
||||||
|
expect(validatePhoneNumber("+49 30 123456789")).toBe(false);
|
||||||
|
|
||||||
|
expect(validatePhoneNumber("+33 1 23 45 67 89")).toBe(true);
|
||||||
|
expect(validatePhoneNumber("+33 1 23 45 67 89 9")).toBe(false);
|
||||||
|
|
||||||
|
expect(validatePhoneNumber("+39 06 12345678")).toBe(true);
|
||||||
|
expect(validatePhoneNumber("+39 06 1234567")).toBe(false);
|
||||||
|
|
||||||
|
expect(validatePhoneNumber("+43 1 2345678")).toBe(true);
|
||||||
|
expect(validatePhoneNumber("+43 1 23456789")).toBe(false);
|
||||||
|
|
||||||
|
expect(validatePhoneNumber("+423 235 09 09")).toBe(true);
|
||||||
|
expect(validatePhoneNumber("+423 235 09 09 8")).toBe(false);
|
||||||
|
|
||||||
|
expect(validatePhoneNumber("+354 123 4567")).toBe(true);
|
||||||
|
expect(validatePhoneNumber("+55 12 34567 8901")).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
export function normalizeSwissPhoneNumber(input: string) {
|
||||||
|
return input
|
||||||
|
.replace(/\s+/g, "")
|
||||||
|
.replace("(0)", "")
|
||||||
|
.replaceAll("-", "")
|
||||||
|
.replaceAll("/", "")
|
||||||
|
.replaceAll("(", "")
|
||||||
|
.replaceAll(")", "")
|
||||||
|
.replace(/^0041/, "+41")
|
||||||
|
.replace(/^\+410/, "+41")
|
||||||
|
.replace(/^0/, "+41");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validatePhoneNumber(input: string) {
|
||||||
|
const normalized = normalizeSwissPhoneNumber(input);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!normalized.startsWith("+") ||
|
||||||
|
isNaN(Number(normalized.slice(1))) ||
|
||||||
|
normalized[1] === "0"
|
||||||
|
) {
|
||||||
|
// phone number can only start with a + and must be followed by numbers
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (["+41", "+43", "+49", "+39", "+33", "+42"].includes(normalized.slice(0, 3))) {
|
||||||
|
if (
|
||||||
|
// Swiss and French phone numbers
|
||||||
|
(normalized.startsWith("+41") || normalized.startsWith("+33")) &&
|
||||||
|
normalized.length === 12
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
} else if (
|
||||||
|
// German and Italian phone numbers
|
||||||
|
(normalized.startsWith("+49") || normalized.startsWith("+39")) &&
|
||||||
|
normalized.length === 13
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
} else if (
|
||||||
|
// Austrian and Liechtenstein phone numbers
|
||||||
|
(normalized.startsWith("+43") || normalized.startsWith("+423")) &&
|
||||||
|
normalized.length === 11
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// every other country
|
||||||
|
if (normalized.length >= 10 || normalized.length <= 13) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function displaySwissPhoneNumber(input: string) {
|
||||||
|
if (input && input.length === 12 && input.startsWith("+41")) {
|
||||||
|
input = input.replace("+41", "0");
|
||||||
|
let result = "";
|
||||||
|
result += input.substring(0, 3) + " ";
|
||||||
|
result += input.substring(3, 6) + " ";
|
||||||
|
result += input.substring(6, 8) + " ";
|
||||||
|
result += input.substring(8);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
@ -126,7 +126,7 @@ describe("checkout.cy.js", () => {
|
||||||
cy.get("#postal-code").type("1719");
|
cy.get("#postal-code").type("1719");
|
||||||
cy.get("#city").type("Zumholz");
|
cy.get("#city").type("Zumholz");
|
||||||
|
|
||||||
cy.get("#phone").type("+41 79 201 85 86");
|
cy.get("#phone").type("079 201 85 86");
|
||||||
cy.get("#birth-date").type("1982-06-09");
|
cy.get("#birth-date").type("1982-06-09");
|
||||||
|
|
||||||
cy.get('[data-cy="continue-pay"]').click();
|
cy.get('[data-cy="continue-pay"]').click();
|
||||||
|
|
@ -141,7 +141,7 @@ describe("checkout.cy.js", () => {
|
||||||
expect(ci.postal_code).to.equal("1719");
|
expect(ci.postal_code).to.equal("1719");
|
||||||
expect(ci.city).to.equal("Zumholz");
|
expect(ci.city).to.equal("Zumholz");
|
||||||
expect(ci.country).to.equal("CH");
|
expect(ci.country).to.equal("CH");
|
||||||
expect(ci.phone_number).to.equal("+41 79 201 85 86");
|
expect(ci.phone_number).to.equal("+41792018586");
|
||||||
expect(ci.birth_date).to.equal("1982-06-09");
|
expect(ci.birth_date).to.equal("1982-06-09");
|
||||||
expect(ci.cembra_invoice).to.be.true;
|
expect(ci.cembra_invoice).to.be.true;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue