Compare commits
2 Commits
380b794ab1
...
040ee4b1f7
| Author | SHA1 | Date | |
|---|---|---|---|
| 040ee4b1f7 | |||
| de74db2703 |
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Qlockify.ir | Time Tracking for Modern Teams</title>
|
||||
<title>Qlockify</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -334,7 +334,7 @@ export const Sidebar = ({ mobileOpen = false, onMobileClose }: SidebarProps) =>
|
||||
<div className="flex min-h-0 flex-1 flex-col overflow-y-auto">
|
||||
<div className="flex shrink-0 items-center justify-between border-b border-slate-200 px-5 py-5 dark:border-slate-800">
|
||||
<h2 className="truncate text-lg font-bold text-slate-800 dark:text-white">
|
||||
Qlockify.ir
|
||||
{t.title || "Qlockify"}
|
||||
</h2>
|
||||
|
||||
<button
|
||||
|
||||
@@ -56,11 +56,14 @@ export const en = {
|
||||
continueToResetPassword: "Continue to reset password",
|
||||
resetPasswordTitle: "Choose a new password",
|
||||
resetPasswordDescription: "Set a new password for your account and confirm it.",
|
||||
resetPasswordCta: "Reset password",
|
||||
newPasswordPlaceholder: "New password",
|
||||
confirmPasswordPlaceholder: "Confirm password",
|
||||
passwordMismatch: "The password confirmation does not match.",
|
||||
enterPassword: "Enter your password",
|
||||
resetPasswordCta: "Reset password",
|
||||
newPasswordPlaceholder: "New password",
|
||||
confirmPasswordPlaceholder: "Confirm password",
|
||||
passwordMismatch: "The password confirmation does not match.",
|
||||
passwordRequirements:
|
||||
"Password must be at least 8 characters and include at least one lowercase letter, one uppercase letter, one digit, and one symbol.",
|
||||
passwordReuse: "New password must be different from your previous password.",
|
||||
enterPassword: "Enter your password",
|
||||
verifyNumber: "Verify your number",
|
||||
enterMobileDesc: "Enter your mobile number to continue",
|
||||
signInDesc: "Sign in using your account password",
|
||||
|
||||
@@ -55,11 +55,14 @@ export const fa = {
|
||||
continueToResetPassword: "ادامه برای تعیین رمز جدید",
|
||||
resetPasswordTitle: "انتخاب رمز عبور جدید",
|
||||
resetPasswordDescription: "رمز عبور جدید خود را وارد کنید و آن را تایید کنید.",
|
||||
resetPasswordCta: "تغییر رمز عبور",
|
||||
newPasswordPlaceholder: "رمز عبور جدید",
|
||||
confirmPasswordPlaceholder: "تکرار رمز عبور",
|
||||
passwordMismatch: "رمز عبور و تکرار آن یکسان نیستند.",
|
||||
welcome: (title: string = "Qlockifiy") => `به ${title} خوش آمدید`,
|
||||
resetPasswordCta: "تغییر رمز عبور",
|
||||
newPasswordPlaceholder: "رمز عبور جدید",
|
||||
confirmPasswordPlaceholder: "تکرار رمز عبور",
|
||||
passwordMismatch: "رمز عبور و تکرار آن یکسان نیستند.",
|
||||
passwordRequirements:
|
||||
"رمز عبور باید حداقل 8 کاراکتر باشد و حداقل یک حرف کوچک، یک حرف بزرگ، یک عدد و یک نماد داشته باشد.",
|
||||
passwordReuse: "رمز عبور جدید نباید با رمز عبور قبلی یکسان باشد.",
|
||||
welcome: (title: string = "Qlockifiy") => `به ${title} خوش آمدید`,
|
||||
enterPassword: "رمز عبور خود را وارد کنید",
|
||||
verifyNumber: "تایید شماره موبایل",
|
||||
enterMobileDesc: "برای ادامه، شماره موبایل خود را وارد کنید",
|
||||
|
||||
@@ -12,10 +12,11 @@ import { Button } from "../components/ui/button"
|
||||
import { Camera, Edit2, Trash2, User as UserIcon, UploadCloud, X, Check } from "lucide-react"
|
||||
import JalaliDatePicker from "../components/ui/JalaliDatePicker"
|
||||
import { toast } from "sonner"
|
||||
import { Modal } from "../components/Modal"
|
||||
import { Input } from "../components/ui/input"
|
||||
import { TextAreaInput } from "../components/ui/TextAreaInput"
|
||||
import { AuthPasswordField } from "./auth/AuthPasswordField"
|
||||
import { Modal } from "../components/Modal"
|
||||
import { Input } from "../components/ui/input"
|
||||
import { TextAreaInput } from "../components/ui/TextAreaInput"
|
||||
import { AuthPasswordField } from "./auth/AuthPasswordField"
|
||||
import { getPasswordValidationMessage } from "./auth/utils"
|
||||
|
||||
export interface UserProfile {
|
||||
id?: string;
|
||||
@@ -187,12 +188,23 @@ export default function Profile() {
|
||||
return
|
||||
}
|
||||
|
||||
if (passwordForm.newPassword !== passwordForm.confirmPassword) {
|
||||
toast.error(t.login.passwordMismatch)
|
||||
return
|
||||
}
|
||||
|
||||
setIsSaving(true)
|
||||
if (passwordForm.newPassword !== passwordForm.confirmPassword) {
|
||||
toast.error(t.login.passwordMismatch)
|
||||
return
|
||||
}
|
||||
|
||||
const passwordValidationMessage = getPasswordValidationMessage(passwordForm.newPassword, t.login)
|
||||
if (passwordValidationMessage) {
|
||||
toast.error(passwordValidationMessage)
|
||||
return
|
||||
}
|
||||
|
||||
if (passwordForm.currentPassword === passwordForm.newPassword) {
|
||||
toast.error(t.login.passwordReuse)
|
||||
return
|
||||
}
|
||||
|
||||
setIsSaving(true)
|
||||
try {
|
||||
await changePassword(
|
||||
passwordForm.currentPassword,
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useAuthFlow } from "../../context/AuthFlowContext"
|
||||
import { useTranslation } from "../../hooks/useTranslation"
|
||||
import { AuthPanel } from "./AuthPanel"
|
||||
import { AuthPasswordField } from "./AuthPasswordField"
|
||||
import { getApiErrorMessage } from "./utils"
|
||||
import { getApiErrorMessage, getPasswordValidationMessage } from "./utils"
|
||||
|
||||
export function ForgotPasswordPasswordPage() {
|
||||
const navigate = useNavigate()
|
||||
@@ -40,6 +40,12 @@ export function ForgotPasswordPasswordPage() {
|
||||
return
|
||||
}
|
||||
|
||||
const passwordValidationMessage = getPasswordValidationMessage(password, t.login)
|
||||
if (passwordValidationMessage) {
|
||||
toast.error(passwordValidationMessage)
|
||||
return
|
||||
}
|
||||
|
||||
setLoading(true)
|
||||
try {
|
||||
await resetPasswordWithOtp(state.forgotPassword.mobile, state.forgotPassword.code, password, confirmation)
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useAuthFlow } from "../../context/AuthFlowContext"
|
||||
import { useTranslation } from "../../hooks/useTranslation"
|
||||
import { AuthPanel } from "./AuthPanel"
|
||||
import { AuthPasswordField } from "./AuthPasswordField"
|
||||
import { completeAuthentication, getApiErrorMessage } from "./utils"
|
||||
import { completeAuthentication, getApiErrorMessage, getPasswordValidationMessage } from "./utils"
|
||||
|
||||
export function SignupPasswordPage() {
|
||||
const navigate = useNavigate()
|
||||
@@ -40,6 +40,12 @@ export function SignupPasswordPage() {
|
||||
return
|
||||
}
|
||||
|
||||
const passwordValidationMessage = getPasswordValidationMessage(password, t.login)
|
||||
if (passwordValidationMessage) {
|
||||
toast.error(passwordValidationMessage)
|
||||
return
|
||||
}
|
||||
|
||||
setLoading(true)
|
||||
try {
|
||||
const data = await registerWithOtp(state.signup.mobile, state.signup.code, password, confirmation)
|
||||
|
||||
@@ -29,6 +29,24 @@ export const getApiErrorMessage = (error: unknown, fallbackMessage: string) => {
|
||||
return fallbackMessage
|
||||
}
|
||||
|
||||
export const getPasswordValidationMessage = (
|
||||
password: string,
|
||||
copy: {
|
||||
passwordRequirements: string
|
||||
},
|
||||
) => {
|
||||
const hasLowercase = /[a-z]/.test(password)
|
||||
const hasUppercase = /[A-Z]/.test(password)
|
||||
const hasDigit = /\d/.test(password)
|
||||
const hasSymbol = /[^A-Za-z0-9]/.test(password)
|
||||
|
||||
if (password.length < 8 || !hasLowercase || !hasUppercase || !hasDigit || !hasSymbol) {
|
||||
return copy.passwordRequirements
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export const handleThrottleError = ({
|
||||
error,
|
||||
cooldownKey,
|
||||
|
||||
Reference in New Issue
Block a user