From f8e7fdfdd79a389f02e326acc0bfff7528fabe15 Mon Sep 17 00:00:00 2001 From: AbhilashG12 Date: Wed, 3 Sep 2025 21:30:38 +0530 Subject: [PATCH] added hide and unhide password button --- package-lock.json | 69 ++- src/ui/Admin/AdminSettings.tsx | 829 ++++++++++++++++--------------- src/ui/Homepage/HomepageAuth.tsx | 139 ++++-- 3 files changed, 595 insertions(+), 442 deletions(-) diff --git a/package-lock.json b/package-lock.json index a3195012..5f0e369d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -564,6 +564,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -580,6 +581,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -596,6 +598,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -612,6 +615,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -628,6 +632,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -644,6 +649,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -660,6 +666,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -676,6 +683,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -692,6 +700,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -708,6 +717,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -724,6 +734,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -740,6 +751,7 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -756,6 +768,7 @@ "cpu": [ "mips64el" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -772,6 +785,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -788,6 +802,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -804,6 +819,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -820,6 +836,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -836,6 +853,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -852,6 +870,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -868,6 +887,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -884,6 +904,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -900,6 +921,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -916,6 +938,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -932,6 +955,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -948,6 +972,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -964,6 +989,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2832,6 +2858,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2845,6 +2872,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2858,6 +2886,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2871,6 +2900,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2884,6 +2914,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2897,6 +2928,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2910,6 +2942,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2923,6 +2956,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2936,6 +2970,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2949,6 +2984,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2962,6 +2998,7 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2975,6 +3012,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2988,6 +3026,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3001,6 +3040,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3014,6 +3054,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3027,6 +3068,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3040,6 +3082,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3053,6 +3096,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3066,6 +3110,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3079,6 +3124,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3671,7 +3717,7 @@ "version": "7.6.13", "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz", "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -3710,6 +3756,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, "license": "MIT" }, "node_modules/@types/express": { @@ -3815,7 +3862,7 @@ "version": "19.1.8", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.0.2" @@ -3825,7 +3872,7 @@ "version": "19.1.6", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", - "devOptional": true, + "dev": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.0.0" @@ -5123,7 +5170,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/data-uri-to-buffer": { @@ -5500,6 +5547,7 @@ "version": "0.25.6", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", + "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -6086,6 +6134,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -7445,6 +7494,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -7473,6 +7523,7 @@ "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, "funding": [ { "type": "opencollective", @@ -7508,6 +7559,7 @@ "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, "funding": [ { "type": "github", @@ -7903,6 +7955,7 @@ "version": "4.45.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.0.tgz", "integrity": "sha512-WLjEcJRIo7i3WDDgOIJqVI2d+lAC3EwvOGy+Xfq6hs+GQuAA4Di/H72xmXkOhrIWFg2PFYSKZYfH0f4vfKXN4A==", + "dev": true, "license": "MIT", "dependencies": { "@types/estree": "1.0.8" @@ -8422,6 +8475,7 @@ "version": "0.2.14", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.4.4", @@ -8438,6 +8492,7 @@ "version": "6.4.6", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, "license": "MIT", "peerDependencies": { "picomatch": "^3 || ^4" @@ -8452,6 +8507,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -8616,7 +8672,7 @@ "version": "5.9.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -8793,6 +8849,7 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.3.tgz", "integrity": "sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==", + "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", @@ -8867,6 +8924,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -8884,6 +8942,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" diff --git a/src/ui/Admin/AdminSettings.tsx b/src/ui/Admin/AdminSettings.tsx index ca1ab4f0..b2bb1f4a 100644 --- a/src/ui/Admin/AdminSettings.tsx +++ b/src/ui/Admin/AdminSettings.tsx @@ -8,443 +8,474 @@ import {Input} from "@/components/ui/input.tsx"; import {Label} from "@/components/ui/label.tsx"; import {Tabs, TabsContent, TabsList, TabsTrigger} from "@/components/ui/tabs.tsx"; import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, +    Table, +    TableBody, +    TableCell, +    TableHead, +    TableHeader, +    TableRow, } from "@/components/ui/table.tsx"; -import {Shield, Trash2, Users} from "lucide-react"; +// 🎯 Import the Eye and EyeOff icons from lucide-react +import {Shield, Trash2, Users, Eye, EyeOff} from "lucide-react"; import {toast} from "sonner"; import {useTranslation} from "react-i18next"; import { - getOIDCConfig, - getRegistrationAllowed, - getUserList, - updateRegistrationAllowed, - updateOIDCConfig, - makeUserAdmin, - removeAdminStatus, - deleteUser +    getOIDCConfig, +    getRegistrationAllowed, +    getUserList, +    updateRegistrationAllowed, +    updateOIDCConfig, +    makeUserAdmin, +    removeAdminStatus, +    deleteUser } from "@/ui/main-axios.ts"; function getCookie(name: string) { - return document.cookie.split('; ').reduce((r, v) => { - const parts = v.split('='); - return parts[0] === name ? decodeURIComponent(parts[1]) : r; - }, ""); +    return document.cookie.split('; ').reduce((r, v) => { +        const parts = v = v.split('='); +        return parts[0] === name ? decodeURIComponent(parts[1]) : r; +    }, ""); } interface AdminSettingsProps { - isTopbarOpen?: boolean; +    isTopbarOpen?: boolean; } export function AdminSettings({isTopbarOpen = true}: AdminSettingsProps): React.ReactElement { - const {t} = useTranslation(); - const {state: sidebarState} = useSidebar(); +    const {t} = useTranslation(); +    const {state: sidebarState} = useSidebar(); - const [allowRegistration, setAllowRegistration] = React.useState(true); - const [regLoading, setRegLoading] = React.useState(false); +    const [allowRegistration, setAllowRegistration] = React.useState(true); +    const [regLoading, setRegLoading] = React.useState(false); - const [oidcConfig, setOidcConfig] = React.useState({ - client_id: '', - client_secret: '', - issuer_url: '', - authorization_url: '', - token_url: '', - identifier_path: 'sub', - name_path: 'name', - scopes: 'openid email profile', - userinfo_url: '' - }); - const [oidcLoading, setOidcLoading] = React.useState(false); - const [oidcError, setOidcError] = React.useState(null); + // 🎯 New state to manage password visibility + const [showClientSecret, setShowClientSecret] = React.useState(false); - const [users, setUsers] = React.useState>([]); - const [usersLoading, setUsersLoading] = React.useState(false); - const [newAdminUsername, setNewAdminUsername] = React.useState(""); - const [makeAdminLoading, setMakeAdminLoading] = React.useState(false); - const [makeAdminError, setMakeAdminError] = React.useState(null); +    const [oidcConfig, setOidcConfig] = React.useState({ +        client_id: '', +        client_secret: '', +        issuer_url: '', +        authorization_url: '', +        token_url: '', +        identifier_path: 'sub', +        name_path: 'name', +        scopes: 'openid email profile', +        userinfo_url: '' +    }); +    const [oidcLoading, setOidcLoading] = React.useState(false); +    const [oidcError, setOidcError] = React.useState(null); - React.useEffect(() => { - const jwt = getCookie("jwt"); - if (!jwt) return; - getOIDCConfig() - .then(res => { - if (res) setOidcConfig(res); - }) - .catch(() => { - }); - fetchUsers(); - }, []); +    const [users, setUsers] = React.useState>([]); +    const [usersLoading, setUsersLoading] = React.useState(false); +    const [newAdminUsername, setNewAdminUsername] = React.useState(""); +    const [makeAdminLoading, setMakeAdminLoading] = React.useState(false); +    const [makeAdminError, setMakeAdminError] = React.useState(null); - React.useEffect(() => { - getRegistrationAllowed() - .then(res => { - if (typeof res?.allowed === 'boolean') { - setAllowRegistration(res.allowed); - } - }) - .catch(() => { - }); - }, []); +    React.useEffect(() => { +        const jwt = getCookie("jwt"); +        if (!jwt) return; +        getOIDCConfig() +            .then(res => { +                if (res) setOidcConfig(res); +            }) +            .catch(() => { +            }); +        fetchUsers(); +    }, []); - const fetchUsers = async () => { - const jwt = getCookie("jwt"); - if (!jwt) return; - setUsersLoading(true); - try { - const response = await getUserList(); - setUsers(response.users); - } finally { - setUsersLoading(false); - } - }; +    React.useEffect(() => { +        getRegistrationAllowed() +            .then(res => { +                if (typeof res?.allowed === 'boolean') { +                    setAllowRegistration(res.allowed); +                } +            }) +            .catch(() => { +            }); +    }, []); - const handleToggleRegistration = async (checked: boolean) => { - setRegLoading(true); - const jwt = getCookie("jwt"); - try { - await updateRegistrationAllowed(checked); - setAllowRegistration(checked); - } finally { - setRegLoading(false); - } - }; +    const fetchUsers = async () => { +        const jwt = getCookie("jwt"); +        if (!jwt) return; +        setUsersLoading(true); +        try { +            const response = await getUserList(); +            setUsers(response.users); +        } finally { +            setUsersLoading(false); +        } +    }; - const handleOIDCConfigSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setOidcLoading(true); - setOidcError(null); +    const handleToggleRegistration = async (checked: boolean) => { +        setRegLoading(true); +        const jwt = getCookie("jwt"); +        try { +            await updateRegistrationAllowed(checked); +            setAllowRegistration(checked); +        } finally { +            setRegLoading(false); +        } +    }; - const required = ['client_id', 'client_secret', 'issuer_url', 'authorization_url', 'token_url']; - const missing = required.filter(f => !oidcConfig[f as keyof typeof oidcConfig]); - if (missing.length > 0) { - setOidcError(t('admin.missingRequiredFields', { fields: missing.join(', ') })); - setOidcLoading(false); - return; - } +    const handleOIDCConfigSubmit = async (e: React.FormEvent) => { +        e.preventDefault(); +        setOidcLoading(true); +        setOidcError(null); - const jwt = getCookie("jwt"); - try { - await updateOIDCConfig(oidcConfig); - toast.success(t('admin.oidcConfigurationUpdated')); - } catch (err: any) { - setOidcError(err?.response?.data?.error || t('admin.failedToUpdateOidcConfig')); - } finally { - setOidcLoading(false); - } - }; +        const required = ['client_id', 'client_secret', 'issuer_url', 'authorization_url', 'token_url']; +        const missing = required.filter(f => !oidcConfig[f as keyof typeof oidcConfig]); +        if (missing.length > 0) { +            setOidcError(t('admin.missingRequiredFields', { fields: missing.join(', ') })); +            setOidcLoading(false); +            return; +        } - const handleOIDCConfigChange = (field: string, value: string) => { - setOidcConfig(prev => ({...prev, [field]: value})); - }; +        const jwt = getCookie("jwt"); +        try { +            await updateOIDCConfig(oidcConfig); +            toast.success(t('admin.oidcConfigurationUpdated')); +        } catch (err: any) { +            setOidcError(err?.response?.data?.error || t('admin.failedToUpdateOidcConfig')); +        } finally { +            setOidcLoading(false); +        } +    }; - const handleMakeUserAdmin = async (e: React.FormEvent) => { - e.preventDefault(); - if (!newAdminUsername.trim()) return; - setMakeAdminLoading(true); - setMakeAdminError(null); - const jwt = getCookie("jwt"); - try { - await makeUserAdmin(newAdminUsername.trim()); - toast.success(t('admin.userIsNowAdmin', { username: newAdminUsername })); - setNewAdminUsername(""); - fetchUsers(); - } catch (err: any) { - setMakeAdminError(err?.response?.data?.error || t('admin.failedToMakeUserAdmin')); - } finally { - setMakeAdminLoading(false); - } - }; +    const handleOIDCConfigChange = (field: string, value: string) => { +        setOidcConfig(prev => ({...prev, [field]: value})); +    }; - const handleRemoveAdminStatus = async (username: string) => { - if (!confirm(t('admin.removeAdminStatus', { username }))) return; - const jwt = getCookie("jwt"); - try { - await removeAdminStatus(username); - toast.success(t('admin.adminStatusRemoved', { username })); - fetchUsers(); - } catch (err: any) { - console.error('Failed to remove admin status:', err); - toast.error(t('admin.failedToRemoveAdminStatus')); - } - }; +    const handleMakeUserAdmin = async (e: React.FormEvent) => { +        e.preventDefault(); +        if (!newAdminUsername.trim()) return; +        setMakeAdminLoading(true); +        setMakeAdminError(null); +        const jwt = getCookie("jwt"); +        try { +            await makeUserAdmin(newAdminUsername.trim()); +            toast.success(t('admin.userIsNowAdmin', { username: newAdminUsername })); +            setNewAdminUsername(""); +            fetchUsers(); +        } catch (err: any) { +            setMakeAdminError(err?.response?.data?.error || t('admin.failedToMakeUserAdmin')); +        } finally { +            setMakeAdminLoading(false); +        } +    }; - const handleDeleteUser = async (username: string) => { - if (!confirm(t('admin.deleteUser', { username }))) return; - const jwt = getCookie("jwt"); - try { - await deleteUser(username); - toast.success(t('admin.userDeletedSuccessfully', { username })); - fetchUsers(); - } catch (err: any) { - console.error('Failed to delete user:', err); - toast.error(t('admin.failedToDeleteUser')); - } - }; +    const handleRemoveAdminStatus = async (username: string) => { +        if (!confirm(t('admin.removeAdminStatus', { username }))) return; +        const jwt = getCookie("jwt"); +        try { +            await removeAdminStatus(username); +            toast.success(t('admin.adminStatusRemoved', { username })); +            fetchUsers(); +        } catch (err: any) { +            console.error('Failed to remove admin status:', err); +            toast.error(t('admin.failedToRemoveAdminStatus')); +        } +    }; - const topMarginPx = isTopbarOpen ? 74 : 26; - const leftMarginPx = sidebarState === 'collapsed' ? 26 : 8; - const bottomMarginPx = 8; - const wrapperStyle: React.CSSProperties = { - marginLeft: leftMarginPx, - marginRight: 17, - marginTop: topMarginPx, - marginBottom: bottomMarginPx, - height: `calc(100vh - ${topMarginPx + bottomMarginPx}px)` - }; +    const handleDeleteUser = async (username: string) => { +        if (!confirm(t('admin.deleteUser', { username }))) return; +        const jwt = getCookie("jwt"); +        try { +            await deleteUser(username); +            toast.success(t('admin.userDeletedSuccessfully', { username })); +            fetchUsers(); +        } catch (err: any) { +            console.error('Failed to delete user:', err); +            toast.error(t('admin.failedToDeleteUser')); +        } +    }; - return ( -
-
-
-

{t('admin.title')}

-
- +    const topMarginPx = isTopbarOpen ? 74 : 26; +    const leftMarginPx = sidebarState === 'collapsed' ? 26 : 8; +    const bottomMarginPx = 8; +    const wrapperStyle: React.CSSProperties = { +        marginLeft: leftMarginPx, +        marginRight: 17, +        marginTop: topMarginPx, +        marginBottom: bottomMarginPx, +        height: `calc(100vh - ${topMarginPx + bottomMarginPx}px)` +    }; -
- - - - - {t('admin.general')} - - - - OIDC - - - - {t('admin.users')} - - - - {t('admin.adminManagement')} - - +    return ( +       
+           
+               
+                   

{t('admin.title')}

+               
+                - -
-

{t('admin.userRegistration')}

- -
-
+               
+                    +                        +                            +                                +                                {t('admin.general')} +                            +                            +                                +                                OIDC +                            +                            +                                +                                {t('admin.users')} +                            +                            +                                +                                {t('admin.adminManagement')} +                            +                        - -
-

{t('admin.externalAuthentication')}

-

{t('admin.configureExternalProvider')}

+                        +                           
+                               

{t('admin.userRegistration')}

+                                +                           
+                       
- {oidcError && ( - - {t('common.error')} - {oidcError} - - )} +                        +                           
+                               

{t('admin.externalAuthentication')}

+                               

{t('admin.configureExternalProvider')}

-
-
- - handleOIDCConfigChange('client_id', e.target.value)} - placeholder={t('placeholders.clientId')} required/> -
-
- - handleOIDCConfigChange('client_secret', e.target.value)} - placeholder={t('placeholders.clientSecret')} required/> -
-
- - handleOIDCConfigChange('authorization_url', e.target.value)} - placeholder={t('placeholders.authUrl')} - required/> -
-
- - handleOIDCConfigChange('issuer_url', e.target.value)} - placeholder={t('placeholders.redirectUrl')} required/> -
-
- - handleOIDCConfigChange('token_url', e.target.value)} - placeholder={t('placeholders.tokenUrl')} required/> -
-
- - handleOIDCConfigChange('identifier_path', e.target.value)} - placeholder={t('placeholders.userIdField')} required/> -
-
- - handleOIDCConfigChange('name_path', e.target.value)} - placeholder={t('placeholders.usernameField')} required/> -
-
- - handleOIDCConfigChange('scopes', e.target.value)} - placeholder={t('placeholders.scopes')} required/> -
-
- - handleOIDCConfigChange('userinfo_url', e.target.value)} - placeholder="https://your-provider.com/application/o/userinfo/"/> -
-
- - -
-
-
-
+                                {oidcError && ( +                                    +                                        {t('common.error')} +                                        {oidcError} +                                    +                                )} - -
-
-

{t('admin.userManagement')}

- -
- {usersLoading ? ( -
{t('admin.loadingUsers')}
- ) : ( -
- - - - {t('admin.username')} - {t('admin.type')} - {t('admin.actions')} - - - - {users.map((user) => ( - - - {user.username} - {user.is_admin && ( - {t('admin.adminBadge')} - )} - - {user.is_oidc ? t('admin.external') : t('admin.local')} - - - - - ))} - -
-
- )} -
-
- - -
-

{t('admin.adminManagement')}

-
-

{t('admin.makeUserAdmin')}

-
-
- -
- setNewAdminUsername(e.target.value)} - placeholder={t('admin.enterUsernameToMakeAdmin')} required/> - -
+                                +                                   
+                                        +                                        handleOIDCConfigChange('client_id', e.target.value)} +                                               placeholder={t('placeholders.clientId')} required/> +                                   
+ {/* 🎯 Updated block for client_secret input */} +                                   
+                                        +      
+ handleOIDCConfigChange('client_secret', e.target.value)} + placeholder={t('placeholders.clientSecret')} + required + // 🎯 Add padding to the right for the button + className="pr-10" + /> +
- {makeAdminError && ( - - {t('common.error')} - {makeAdminError} - - )} +                                   
+ {/* 🎯 End of updated block */} +                                   
+                                        +                                        handleOIDCConfigChange('authorization_url', e.target.value)} +                                               placeholder={t('placeholders.authUrl')} +                                               required/> +                                   
+                                   
+                                        +                                        handleOIDCConfigChange('issuer_url', e.target.value)} +                                               placeholder={t('placeholders.redirectUrl')} required/> +                                   
+                                   
+                                        +                                        handleOIDCConfigChange('token_url', e.target.value)} +                                               placeholder={t('placeholders.tokenUrl')} required/> +                                   
+                                   
+                                        +                                        handleOIDCConfigChange('identifier_path', e.target.value)} +                                               placeholder={t('placeholders.userIdField')} required/> +                                   
+                                   
+                                        +                                        handleOIDCConfigChange('name_path', e.target.value)} +                                               placeholder={t('placeholders.usernameField')} required/> +                                   
+                                   
+                                        +                                        handleOIDCConfigChange('scopes', e.target.value)} +                                               placeholder={t('placeholders.scopes')} required/> +                                   
+                                   
+                                        +                                        handleOIDCConfigChange('userinfo_url', e.target.value)} +                                               placeholder="https://your-provider.com/application/o/userinfo/"/> +                                   
+                                   
+                                        +                                        +                                   
+                                +                           
+                        - -
+                        +                           
+                               
+                                   

{t('admin.userManagement')}

+                                    +                               
+                                {usersLoading ? ( +                                   
{t('admin.loadingUsers')}
+                                ) : ( +                                   
+                                        +                                            +                                                +                                                    {t('admin.username')} +                                                    {t('admin.type')} +                                                    {t('admin.actions')} +                                                +                                            +                                            +                                                {users.map((user) => ( +                                                    +                                                        +                                                            {user.username} +                                                            {user.is_admin && ( +                                                                {t('admin.adminBadge')} +                                                            )} +                                                        +                                                        {user.is_oidc ? t('admin.external') : t('admin.local')} +                                                        +                                                            +                                                        +                                                    +                                                ))} +                                            +                                       
+                                   
+                                )} +                           
+                       
-
-

{t('admin.currentAdmins')}

-
- - - - {t('admin.username')} - {t('admin.type')} - {t('admin.actions')} - - - - {users.filter(u => u.is_admin).map((admin) => ( - - - {admin.username} - {t('admin.adminBadge')} - - {admin.is_oidc ? t('admin.external') : t('admin.local')} - - - - - ))} - -
-
-
-
-
- -
-
-
- ); +                        +                           
+                               

{t('admin.adminManagement')}

+                               
+                                   

{t('admin.makeUserAdmin')}

+                                   
+                                       
+                                            +                                           
+                                                setNewAdminUsername(e.target.value)} +                                                       placeholder={t('admin.enterUsernameToMakeAdmin')} required/> +                                                +                                           
+                                       
+                                        {makeAdminError && ( +                                            +                                                {t('common.error')} +                                                {makeAdminError} +                                            +                                        )} + +                                   
+                               
+ +                               
+                                   

{t('admin.currentAdmins')}

+                                   
+                                        +                                            +                                                +                                                    {t('admin.username')} +                                                    {t('admin.type')} +                                                    {t('admin.actions')} +                                                +                                            +                                            +                                                {users.filter(u => u.is_admin).map((admin) => ( +                                                    +                                                        +                                                            {admin.username} +                                                            {t('admin.adminBadge')} +                                                        +                                                        {admin.is_oidc ? t('admin.external') : t('admin.local')} +                                                        +                                                            +                                                        +                                                    +                                                ))} +                                            +                                       
+                                   
+                               
+                           
+                       
+                    +               
+           
+       
+    ); } export default AdminSettings; \ No newline at end of file diff --git a/src/ui/Homepage/HomepageAuth.tsx b/src/ui/Homepage/HomepageAuth.tsx index 5b1ca837..38865075 100644 --- a/src/ui/Homepage/HomepageAuth.tsx +++ b/src/ui/Homepage/HomepageAuth.tsx @@ -1,4 +1,5 @@ import React, {useState, useEffect} from "react"; +import {Eye, EyeOff} from "lucide-react"; import {cn} from "../../lib/utils.ts"; import {Button} from "../../components/ui/button.tsx"; import {Input} from "../../components/ui/input.tsx"; @@ -64,6 +65,16 @@ export function HomepageAuth({ const [signupConfirmPassword, setSignupConfirmPassword] = useState(""); const [loading, setLoading] = useState(false); const [oidcLoading, setOidcLoading] = useState(false); + const [visibility, setVisibility] = useState({ + password: false, + signupConfirm: false, + resetNew: false, + resetConfirm: false +}); + const toggleVisibility = (field: keyof typeof visibility) => { + setVisibility(prev => ({ ...prev, [field]: !prev[field] })); +}; + const [error, setError] = useState(null); const [internalLoggedIn, setInternalLoggedIn] = useState(false); const [firstUser, setFirstUser] = useState(false); @@ -678,31 +689,53 @@ export function HomepageAuth({
- - setNewPassword(e.target.value)} - disabled={resetLoading} - autoComplete="new-password" - /> -
-
- - setConfirmPassword(e.target.value)} - disabled={resetLoading} - autoComplete="new-password" - /> -
+ +
+ setNewPassword(e.target.value)} + disabled={resetLoading} + autoComplete="new-password" + /> + +
+
+
+ +
+ setConfirmPassword(e.target.value)} + disabled={resetLoading} + autoComplete="new-password" + /> + +
+
- {tab === "signup" && ( -
- - setSignupConfirmPassword(e.target.value)} - disabled={loading || internalLoggedIn}/> -
+ + {tab === "signup" && ( +
+ +
+ setSignupConfirmPassword(e.target.value)} + disabled={loading || internalLoggedIn}/> + +
+
)}