mirror of
https://github.com/Since-3/Kuma.git
synced 2026-10-12 21:41:17 +00:00
added number input with dynamic formatting
This commit is contained in:
parent
a050a23cbe
commit
cdc16a0663
6 changed files with 270 additions and 13 deletions
62
package-lock.json
generated
62
package-lock.json
generated
|
|
@ -36,6 +36,7 @@
|
||||||
"react": "19.2.0",
|
"react": "19.2.0",
|
||||||
"react-dom": "19.2.0",
|
"react-dom": "19.2.0",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
|
"react-phone-number-input": "^3.4.14",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"zod": "^4.1.12",
|
"zod": "^4.1.12",
|
||||||
|
|
@ -7641,6 +7642,12 @@
|
||||||
"url": "https://polar.sh/cva"
|
"url": "https://polar.sh/cva"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/classnames": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/cli-cursor": {
|
"node_modules/cli-cursor": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
|
||||||
|
|
@ -7764,6 +7771,12 @@
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/country-flag-icons": {
|
||||||
|
"version": "1.6.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/country-flag-icons/-/country-flag-icons-1.6.8.tgz",
|
||||||
|
"integrity": "sha512-VpRAbqZ+uUuC9OHgIZwqeDnWG4YwkD8s6jBZXlTK4WBYj3QXeS+/uCi4u2eAUdZ5eKjIAlkU5UYV/Z4RPvhYJg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/crelt": {
|
"node_modules/crelt": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
|
||||||
|
|
@ -9525,6 +9538,27 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/input-format": {
|
||||||
|
"version": "0.3.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/input-format/-/input-format-0.3.14.tgz",
|
||||||
|
"integrity": "sha512-gHMrgrbCgmT4uK5Um5eVDUohuV9lcs95ZUUN9Px2Y0VIfjTzT2wF8Q3Z4fwLFm7c5Z2OXCm53FHoovj6SlOKdg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"prop-types": "^15.8.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=18.1.0",
|
||||||
|
"react-dom": ">=18.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/internal-slot": {
|
"node_modules/internal-slot": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
|
||||||
|
|
@ -10023,7 +10057,6 @@
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
|
|
@ -10186,6 +10219,12 @@
|
||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/libphonenumber-js": {
|
||||||
|
"version": "1.12.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.35.tgz",
|
||||||
|
"integrity": "sha512-T/Cz6iLcsZdb5jDncDcUNhSAJ0VlSC9TnsqtBNdpkaAmy24/R1RhErtNWVWBrcUZKs9hSgaVsBkc7HxYnazIfw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/lightningcss": {
|
"node_modules/lightningcss": {
|
||||||
"version": "1.30.2",
|
"version": "1.30.2",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
|
||||||
|
|
@ -10552,7 +10591,6 @@
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||||
|
|
@ -10930,7 +10968,6 @@
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
|
|
@ -11347,7 +11384,6 @@
|
||||||
"version": "15.8.1",
|
"version": "15.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.4.0",
|
"loose-envify": "^1.4.0",
|
||||||
|
|
@ -11650,9 +11686,25 @@
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/react-phone-number-input": {
|
||||||
|
"version": "3.4.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-phone-number-input/-/react-phone-number-input-3.4.14.tgz",
|
||||||
|
"integrity": "sha512-T9MziNuvthzv6+JAhKD71ab/jVXW5U20nQZRBJd6+q+ujmkC+/ISOf2GYo8pIi4VGjdIYRIHDftMAYn3WKZT3w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"classnames": "^2.5.1",
|
||||||
|
"country-flag-icons": "^1.5.17",
|
||||||
|
"input-format": "^0.3.14",
|
||||||
|
"libphonenumber-js": "^1.12.27",
|
||||||
|
"prop-types": "^15.8.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8",
|
||||||
|
"react-dom": ">=16.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-refresh": {
|
"node_modules/react-refresh": {
|
||||||
"version": "0.18.0",
|
"version": "0.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@
|
||||||
"react": "19.2.0",
|
"react": "19.2.0",
|
||||||
"react-dom": "19.2.0",
|
"react-dom": "19.2.0",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
|
"react-phone-number-input": "^3.4.14",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"zod": "^4.1.12",
|
"zod": "^4.1.12",
|
||||||
|
|
|
||||||
56
src/components/layout/PhoneInputComponent.tsx
Normal file
56
src/components/layout/PhoneInputComponent.tsx
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
"use client";
|
||||||
|
import { Label } from "../ui/label";
|
||||||
|
import PhoneInput, { type Value } from "react-phone-number-input";
|
||||||
|
import "react-phone-number-input/style.css";
|
||||||
|
import "./css/phone-input.css";
|
||||||
|
|
||||||
|
interface PhoneInputProps {
|
||||||
|
label?: string;
|
||||||
|
id: string;
|
||||||
|
isLabel?: boolean;
|
||||||
|
placeholder?: string;
|
||||||
|
onChange?: (value: string) => void;
|
||||||
|
value?: string;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PhoneInputComponent: React.FC<PhoneInputProps> = ({
|
||||||
|
label,
|
||||||
|
id,
|
||||||
|
placeholder,
|
||||||
|
isLabel,
|
||||||
|
onChange,
|
||||||
|
value,
|
||||||
|
error,
|
||||||
|
}) => {
|
||||||
|
const handleChange = (newValue: Value) => {
|
||||||
|
if (onChange) {
|
||||||
|
onChange(newValue || "");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{isLabel && (
|
||||||
|
<Label htmlFor={id} className="p-1 mb-2 text-blue text-lg font-semibold">
|
||||||
|
{label}
|
||||||
|
</Label>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="relative">
|
||||||
|
<PhoneInput
|
||||||
|
international
|
||||||
|
defaultCountry="DE"
|
||||||
|
value={value as Value}
|
||||||
|
onChange={handleChange}
|
||||||
|
placeholder={placeholder || "Telefonnummer eingeben"}
|
||||||
|
id={id}
|
||||||
|
className="phone-input-custom"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{error && <p className="text-red-500 text-sm mt-1">{error}</p>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PhoneInputComponent;
|
||||||
125
src/components/layout/css/phone-input.css
Normal file
125
src/components/layout/css/phone-input.css
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
.phone-input-custom {
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main input field - matching InputComponent styling */
|
||||||
|
.phone-input-custom .PhoneInputInput {
|
||||||
|
height: 50px;
|
||||||
|
border: 1px solid;
|
||||||
|
border-color: #1e293b;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.25rem 1rem 0.25rem 4.5rem;
|
||||||
|
font-size: 1.125rem; /* text-lg */
|
||||||
|
line-height: 1.75rem;
|
||||||
|
outline: none;
|
||||||
|
transition: all 0.2s;
|
||||||
|
background-color: transparent;
|
||||||
|
color: hsl(var(--foreground));
|
||||||
|
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.phone-input-custom .PhoneInputInput {
|
||||||
|
font-size: 1rem; /* text-base on mobile */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Country selector container */
|
||||||
|
.phone-input-custom .PhoneInputCountry {
|
||||||
|
position: absolute;
|
||||||
|
left: 0.75rem;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
z-index: 10;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Flag icon */
|
||||||
|
.phone-input-custom .PhoneInputCountryIcon {
|
||||||
|
width: 1.5rem;
|
||||||
|
height: 1.125rem;
|
||||||
|
border-radius: 0.125rem;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.phone-input-custom .PhoneInputCountryIcon--border {
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.phone-input-custom .PhoneInputCountryIcon img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Country select dropdown (invisible overlay) */
|
||||||
|
.phone-input-custom .PhoneInputCountrySelect {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 4rem;
|
||||||
|
opacity: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 11;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dropdown arrow */
|
||||||
|
.phone-input-custom .PhoneInputCountrySelectArrow {
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
color: hsl(var(--blue));
|
||||||
|
opacity: 0.6;
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Focus state */
|
||||||
|
.phone-input-custom .PhoneInputInput:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: hsl(var(--blue));
|
||||||
|
box-shadow: 0 0 0 2px hsl(var(--blue) / 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Placeholder */
|
||||||
|
.phone-input-custom .PhoneInputInput::placeholder {
|
||||||
|
color: hsl(var(--muted-foreground));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disabled state */
|
||||||
|
.phone-input-custom .PhoneInputInput:disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark mode support */
|
||||||
|
.dark .phone-input-custom .PhoneInputInput {
|
||||||
|
background-color: hsl(var(--input) / 0.3);
|
||||||
|
color: hsl(var(--foreground));
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .phone-input-custom .PhoneInputCountryIcon--border {
|
||||||
|
border-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Error state */
|
||||||
|
.phone-input-custom.error .PhoneInputInput {
|
||||||
|
border-color: hsl(var(--destructive));
|
||||||
|
}
|
||||||
|
|
||||||
|
.phone-input-custom.error .PhoneInputInput:focus {
|
||||||
|
box-shadow: 0 0 0 2px hsl(var(--destructive) / 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Selection styling */
|
||||||
|
.phone-input-custom .PhoneInputInput::selection {
|
||||||
|
background-color: hsl(var(--primary));
|
||||||
|
color: hsl(var(--primary-foreground));
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import AuthSidebarComponent from "../components/AuthSidebarComponent";
|
import AuthSidebarComponent from "../components/AuthSidebarComponent";
|
||||||
import InputComponent from "@/src/components/layout/InputComponent";
|
import InputComponent from "@/src/components/layout/InputComponent";
|
||||||
|
import PhoneInputComponent from "@/src/components/layout/PhoneInputComponent";
|
||||||
import { Button } from "@/src/components/ui/button";
|
import { Button } from "@/src/components/ui/button";
|
||||||
import { useAuthFormStore } from "../../store/authFormState";
|
import { useAuthFormStore } from "../../store/authFormState";
|
||||||
import { useAuth } from "@/src/hooks/useAuth";
|
import { useAuth } from "@/src/hooks/useAuth";
|
||||||
|
|
@ -146,13 +147,12 @@ const RegisterAdmin = () => {
|
||||||
onChange={(e) => handleChange("email", e.target.value)}
|
onChange={(e) => handleChange("email", e.target.value)}
|
||||||
error={errors.email}
|
error={errors.email}
|
||||||
/>
|
/>
|
||||||
<InputComponent
|
<PhoneInputComponent
|
||||||
isLabel
|
isLabel
|
||||||
label="Telefonnummer"
|
label="Telefonnummer"
|
||||||
type="text"
|
|
||||||
id="register-admin-tel"
|
id="register-admin-tel"
|
||||||
value={formData.tel}
|
value={formData.tel}
|
||||||
onChange={(e) => handleChange("tel", e.target.value)}
|
onChange={(value) => handleChange("tel", value || "")}
|
||||||
error={errors.tel}
|
error={errors.tel}
|
||||||
/>
|
/>
|
||||||
<InputComponent
|
<InputComponent
|
||||||
|
|
@ -265,13 +265,12 @@ const RegisterAdmin = () => {
|
||||||
onChange={(e) => handleChange("companyMail", e.target.value)}
|
onChange={(e) => handleChange("companyMail", e.target.value)}
|
||||||
error={errors.companyMail}
|
error={errors.companyMail}
|
||||||
/>
|
/>
|
||||||
<InputComponent
|
<PhoneInputComponent
|
||||||
isLabel
|
isLabel
|
||||||
label="Geschäftsnummer (falls unterschiedlich)"
|
label="Geschäftsnummer (falls unterschiedlich)"
|
||||||
type="text"
|
|
||||||
id="register-admin-companyNumber"
|
id="register-admin-companyNumber"
|
||||||
value={formData.companyNumber}
|
value={formData.companyNumber}
|
||||||
onChange={(e) => handleChange("companyNumber", e.target.value)}
|
onChange={(value) => handleChange("companyNumber", value || "")}
|
||||||
error={errors.companyNumber}
|
error={errors.companyNumber}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,23 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
// Helper function to validate international phone numbers
|
||||||
|
const isValidPhoneNumber = (phone: string): boolean => {
|
||||||
|
// Check if phone starts with + and has at least country code and number
|
||||||
|
const phoneRegex = /^\+[1-9]\d{1,14}$/;
|
||||||
|
return phoneRegex.test(phone);
|
||||||
|
};
|
||||||
|
|
||||||
// Step 1: Personal Info
|
// Step 1: Personal Info
|
||||||
export const adminRegisterStep1Schema = z
|
export const adminRegisterStep1Schema = z
|
||||||
.object({
|
.object({
|
||||||
fullName: z.string().min(2, "Name muss mindestens 2 Zeichen lang sein"),
|
fullName: z.string().min(2, "Name muss mindestens 2 Zeichen lang sein"),
|
||||||
email: z.string().email("Bitte eine gültige E-Mail-Adresse eingeben"),
|
email: z.string().email("Bitte eine gültige E-Mail-Adresse eingeben"),
|
||||||
tel: z.string().min(5, "Telefonnummer muss mindestens 5 Zeichen lang sein"),
|
tel: z
|
||||||
|
.string()
|
||||||
|
.min(1, "Telefonnummer ist erforderlich")
|
||||||
|
.refine((val) => isValidPhoneNumber(val), {
|
||||||
|
message: "Bitte eine gültige Telefonnummer eingeben",
|
||||||
|
}),
|
||||||
password: z.string().min(8, "Passwort muss mindestens 8 Zeichen lang sein"),
|
password: z.string().min(8, "Passwort muss mindestens 8 Zeichen lang sein"),
|
||||||
passwordConfirm: z.string(),
|
passwordConfirm: z.string(),
|
||||||
})
|
})
|
||||||
|
|
@ -35,7 +47,19 @@ export const adminRegisterStep3Schema = z.object({
|
||||||
.email("Bitte eine gültige E-Mail-Adresse eingeben")
|
.email("Bitte eine gültige E-Mail-Adresse eingeben")
|
||||||
.optional()
|
.optional()
|
||||||
.or(z.literal("")),
|
.or(z.literal("")),
|
||||||
companyNumber: z.string().optional().or(z.literal("")),
|
companyNumber: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.or(z.literal(""))
|
||||||
|
.refine(
|
||||||
|
(val) => {
|
||||||
|
if (!val || val === "") return true;
|
||||||
|
return isValidPhoneNumber(val);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message: "Bitte eine gültige Telefonnummer eingeben",
|
||||||
|
}
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Full schema for complete validation
|
// Full schema for complete validation
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue