From 06976f7972d7333fe8b3089d3dff9cd09d6f5f86 Mon Sep 17 00:00:00 2001 From: Theis Gaedigk Date: Sat, 14 Feb 2026 19:01:59 +0100 Subject: [PATCH] added pasword input to admin panel --- admin/src/Layout/Login.tsx | 4 +- admin/src/components/ui/password-input.tsx | 159 +++++++++++++++++++++ admin/tsconfig.app.json | 10 +- 3 files changed, 165 insertions(+), 8 deletions(-) create mode 100644 admin/src/components/ui/password-input.tsx diff --git a/admin/src/Layout/Login.tsx b/admin/src/Layout/Login.tsx index 813a7e6..590826c 100644 --- a/admin/src/Layout/Login.tsx +++ b/admin/src/Layout/Login.tsx @@ -3,6 +3,7 @@ import { useState } from "react"; import { loginFunc } from "@/utils/loginUser"; import MyAlert from "../components/myChakra/MyAlert"; import { Button, Card, Field, Input, Stack } from "@chakra-ui/react"; +import { PasswordInput } from "@/components/ui/password-input"; const Login: React.FC<{ onSuccess: () => void }> = ({ onSuccess }) => { const [username, setUsername] = useState(""); @@ -43,8 +44,7 @@ const Login: React.FC<{ onSuccess: () => void }> = ({ onSuccess }) => { password - setPassword(e.target.value)} /> diff --git a/admin/src/components/ui/password-input.tsx b/admin/src/components/ui/password-input.tsx new file mode 100644 index 0000000..3b9af80 --- /dev/null +++ b/admin/src/components/ui/password-input.tsx @@ -0,0 +1,159 @@ +"use client" + +import type { + ButtonProps, + GroupProps, + InputProps, + StackProps, +} from "@chakra-ui/react" +import { + Box, + HStack, + IconButton, + Input, + InputGroup, + Stack, + mergeRefs, + useControllableState, +} from "@chakra-ui/react" +import * as React from "react" +import { LuEye, LuEyeOff } from "react-icons/lu" + +export interface PasswordVisibilityProps { + /** + * The default visibility state of the password input. + */ + defaultVisible?: boolean + /** + * The controlled visibility state of the password input. + */ + visible?: boolean + /** + * Callback invoked when the visibility state changes. + */ + onVisibleChange?: (visible: boolean) => void + /** + * Custom icons for the visibility toggle button. + */ + visibilityIcon?: { on: React.ReactNode; off: React.ReactNode } +} + +export interface PasswordInputProps + extends InputProps, + PasswordVisibilityProps { + rootProps?: GroupProps +} + +export const PasswordInput = React.forwardRef< + HTMLInputElement, + PasswordInputProps +>(function PasswordInput(props, ref) { + const { + rootProps, + defaultVisible, + visible: visibleProp, + onVisibleChange, + visibilityIcon = { on: , off: }, + ...rest + } = props + + const [visible, setVisible] = useControllableState({ + value: visibleProp, + defaultValue: defaultVisible || false, + onChange: onVisibleChange, + }) + + const inputRef = React.useRef(null) + + return ( + { + if (rest.disabled) return + if (e.button !== 0) return + e.preventDefault() + setVisible(!visible) + }} + > + {visible ? visibilityIcon.off : visibilityIcon.on} + + } + {...rootProps} + > + + + ) +}) + +const VisibilityTrigger = React.forwardRef( + function VisibilityTrigger(props, ref) { + return ( + + ) + }, +) + +interface PasswordStrengthMeterProps extends StackProps { + max?: number + value: number +} + +export const PasswordStrengthMeter = React.forwardRef< + HTMLDivElement, + PasswordStrengthMeterProps +>(function PasswordStrengthMeter(props, ref) { + const { max = 4, value, ...rest } = props + + const percent = (value / max) * 100 + const { label, colorPalette } = getColorPalette(percent) + + return ( + + + {Array.from({ length: max }).map((_, index) => ( + + ))} + + {label && {label}} + + ) +}) + +function getColorPalette(percent: number) { + switch (true) { + case percent < 33: + return { label: "Low", colorPalette: "red" } + case percent < 66: + return { label: "Medium", colorPalette: "orange" } + default: + return { label: "High", colorPalette: "green" } + } +} diff --git a/admin/tsconfig.app.json b/admin/tsconfig.app.json index 8ad072c..a824e0e 100644 --- a/admin/tsconfig.app.json +++ b/admin/tsconfig.app.json @@ -1,10 +1,11 @@ { "compilerOptions": { "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", - "target": "ESNext", + "target": "ES2022", "useDefineForClassFields": true, "lib": ["ES2022", "DOM", "DOM.Iterable"], "module": "ESNext", + "types": ["vite/client"], "skipLibCheck": true, /* Bundler mode */ @@ -23,13 +24,10 @@ "noFallthroughCasesInSwitch": true, "noUncheckedSideEffectImports": true, - /* Chakra / Pfad Aliases */ - "baseUrl": ".", + /* Path aliases */ "paths": { "@/*": ["./src/*"] - }, - - "forceConsistentCasingInFileNames": true + } }, "include": ["src"] }