more modals replaced
This commit is contained in:
parent
2c9541734a
commit
a3c6d9b42e
|
@ -12,23 +12,17 @@ type Props = {
|
|||
export default function Checkbox({ label, state, className, onClick }: Props) {
|
||||
return (
|
||||
<label
|
||||
className={`cursor-pointer flex items-center gap-2 ${className || ""}`}
|
||||
className={`label cursor-pointer flex gap-2 justify-start ${
|
||||
className || ""
|
||||
}`}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={state}
|
||||
onChange={onClick}
|
||||
className="peer sr-only"
|
||||
className="checkbox checkbox-primary"
|
||||
/>
|
||||
<FontAwesomeIcon
|
||||
icon={faSquareCheck}
|
||||
className="w-5 h-5 text-primary peer-checked:block hidden"
|
||||
/>
|
||||
<FontAwesomeIcon
|
||||
icon={faSquare}
|
||||
className="w-5 h-5 text-primary peer-checked:hidden block"
|
||||
/>
|
||||
<span className="rounded select-none">{label}</span>
|
||||
<span className="label-text">{label}</span>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import getPublicUserData from "@/lib/client/getPublicUserData";
|
|||
import useAccountStore from "@/store/account";
|
||||
import EditCollectionModal from "./Modals/EditCollectionModal";
|
||||
import EditCollectionSharingModal from "./Modals/EditCollectionSharingModal";
|
||||
import DeleteCollectionModal from "./Modals/DeleteCollectionModal";
|
||||
|
||||
type Props = {
|
||||
collection: CollectionIncludingMembersAndLinkCount;
|
||||
|
@ -62,6 +63,7 @@ export default function CollectionCard({ collection, className }: Props) {
|
|||
const [editCollectionModal, setEditCollectionModal] = useState(false);
|
||||
const [editCollectionSharingModal, setEditCollectionSharingModal] =
|
||||
useState(false);
|
||||
const [deleteCollectionModal, setDeleteCollectionModal] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
|
@ -109,15 +111,7 @@ export default function CollectionCard({ collection, className }: Props) {
|
|||
tabIndex={0}
|
||||
onClick={() => {
|
||||
(document?.activeElement as HTMLElement)?.blur();
|
||||
collection &&
|
||||
setModal({
|
||||
modal: "COLLECTION",
|
||||
state: true,
|
||||
method: "UPDATE",
|
||||
isOwner: permissions === true,
|
||||
active: collection,
|
||||
defaultIndex: permissions === true ? 2 : 1,
|
||||
});
|
||||
setDeleteCollectionModal(true);
|
||||
}}
|
||||
>
|
||||
{permissions === true ? "Delete Collection" : "Leave Collection"}
|
||||
|
@ -132,7 +126,7 @@ export default function CollectionCard({ collection, className }: Props) {
|
|||
{collectionOwner.id ? (
|
||||
<ProfilePhoto
|
||||
src={collectionOwner.image || undefined}
|
||||
className="w-7 h-7"
|
||||
dimensionClass="w-7 h-7"
|
||||
/>
|
||||
) : undefined}
|
||||
{collection.members
|
||||
|
@ -212,6 +206,12 @@ export default function CollectionCard({ collection, className }: Props) {
|
|||
modalId={"edit-collection-sharing-modal" + collection.id}
|
||||
activeCollection={collection}
|
||||
/>
|
||||
<DeleteCollectionModal
|
||||
isOpen={deleteCollectionModal}
|
||||
onClose={() => setDeleteCollectionModal(false)}
|
||||
modalId={"delete-collection-modal" + collection.id}
|
||||
activeCollection={collection}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import TextInput from "@/components/TextInput";
|
||||
import useCollectionStore from "@/store/collections";
|
||||
import toast, { Toaster } from "react-hot-toast";
|
||||
import {
|
||||
faFolder,
|
||||
faRightFromBracket,
|
||||
faTrashCan,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { HexColorPicker } from "react-colorful";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { CollectionIncludingMembersAndLinkCount } from "@/types/global";
|
||||
import { useRouter } from "next/router";
|
||||
import usePermissions from "@/hooks/usePermissions";
|
||||
|
||||
type Props = {
|
||||
modalId: string;
|
||||
isOpen: boolean;
|
||||
onClose: Function;
|
||||
activeCollection: CollectionIncludingMembersAndLinkCount;
|
||||
};
|
||||
|
||||
export default function DeleteCollectionModal({
|
||||
modalId,
|
||||
isOpen,
|
||||
onClose,
|
||||
activeCollection,
|
||||
}: Props) {
|
||||
const modal = document.getElementById(modalId);
|
||||
|
||||
useEffect(() => {
|
||||
modal?.addEventListener("close", () => {
|
||||
onClose();
|
||||
});
|
||||
|
||||
return () => {
|
||||
modal?.addEventListener("close", () => {
|
||||
onClose();
|
||||
});
|
||||
};
|
||||
}, [isOpen]);
|
||||
|
||||
const [collection, setCollection] =
|
||||
useState<CollectionIncludingMembersAndLinkCount>(activeCollection);
|
||||
|
||||
useEffect(() => {
|
||||
setCollection(activeCollection);
|
||||
setInputField("");
|
||||
}, [isOpen]);
|
||||
|
||||
const [submitLoader, setSubmitLoader] = useState(false);
|
||||
const { removeCollection } = useCollectionStore();
|
||||
const router = useRouter();
|
||||
const [inputField, setInputField] = useState("");
|
||||
|
||||
const permissions = usePermissions(collection.id as number);
|
||||
|
||||
const submit = async () => {
|
||||
if (permissions === true) if (collection.name !== inputField) return null;
|
||||
|
||||
if (!submitLoader) {
|
||||
setSubmitLoader(true);
|
||||
if (!collection) return null;
|
||||
|
||||
setSubmitLoader(true);
|
||||
|
||||
const load = toast.loading("Deleting...");
|
||||
|
||||
let response;
|
||||
|
||||
response = await removeCollection(collection.id as any);
|
||||
|
||||
toast.dismiss(load);
|
||||
|
||||
if (response.ok) {
|
||||
toast.success(`Deleted.`);
|
||||
(document.getElementById(modalId) as any).close();
|
||||
router.push("/collections");
|
||||
} else toast.error(response.data as string);
|
||||
|
||||
setSubmitLoader(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<dialog
|
||||
id={modalId}
|
||||
className="modal backdrop-blur-sm overflow-y-auto"
|
||||
open={isOpen}
|
||||
>
|
||||
<Toaster
|
||||
position="top-center"
|
||||
reverseOrder={false}
|
||||
toastOptions={{
|
||||
className:
|
||||
"border border-sky-100 dark:border-neutral-700 dark:bg-neutral-900 dark:text-white",
|
||||
}}
|
||||
/>
|
||||
<div className="modal-box max-h-full overflow-y-visible border border-neutral-content w-11/12 max-w-2xl">
|
||||
<form method="dialog">
|
||||
<button className="btn btn-sm outline-none btn-circle btn-ghost absolute right-3 top-3">
|
||||
✕
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p className="text-xl mb-5 font-thin text-red-500">
|
||||
{permissions === true ? "Delete" : "Leave"} Collection
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
{permissions === true ? (
|
||||
<>
|
||||
<div className="flex flex-col gap-3">
|
||||
<p>
|
||||
To confirm, type "
|
||||
<span className="font-bold">{collection.name}</span>
|
||||
" in the box below:
|
||||
</p>
|
||||
|
||||
<TextInput
|
||||
value={inputField}
|
||||
onChange={(e) => setInputField(e.target.value)}
|
||||
placeholder={`Type "${collection.name}" Here.`}
|
||||
className="w-3/4 mx-auto"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div role="alert" className="alert alert-warning">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="stroke-current shrink-0 h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
||||
/>
|
||||
</svg>
|
||||
<span>
|
||||
<b>
|
||||
Warning: Deleting this collection will permanently erase all
|
||||
its contents
|
||||
</b>
|
||||
, and it will become inaccessible to everyone, including
|
||||
members with previous access.
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<p>Click the button below to leave the current collection.</p>
|
||||
)}
|
||||
|
||||
<button
|
||||
disabled={permissions === true && inputField !== collection.name}
|
||||
className={`ml-auto btn w-fit text-white flex items-center gap-2 duration-100 ${
|
||||
permissions === true
|
||||
? inputField === collection.name
|
||||
? "bg-red-500 hover:bg-red-400 hover:dark:bg-red-600 cursor-pointer"
|
||||
: "cursor-not-allowed bg-red-300 dark:bg-red-900"
|
||||
: "bg-red-500 hover:bg-red-400 hover:dark:bg-red-600 cursor-pointer"
|
||||
}`}
|
||||
onClick={submit}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={permissions === true ? faTrashCan : faRightFromBracket}
|
||||
className="h-5"
|
||||
/>
|
||||
{permissions === true ? "Delete" : "Leave"} Collection
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<form method="dialog" className="modal-backdrop">
|
||||
<button>close</button>
|
||||
</form>
|
||||
</dialog>
|
||||
);
|
||||
}
|
|
@ -4,7 +4,6 @@ import useCollectionStore from "@/store/collections";
|
|||
import toast, { Toaster } from "react-hot-toast";
|
||||
import { faFolder } from "@fortawesome/free-solid-svg-icons";
|
||||
import { HexColorPicker } from "react-colorful";
|
||||
import { Collection } from "@prisma/client";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { CollectionIncludingMembersAndLinkCount } from "@/types/global";
|
||||
|
||||
|
|
|
@ -5,11 +5,8 @@ import toast, { Toaster } from "react-hot-toast";
|
|||
import {
|
||||
faClose,
|
||||
faCrown,
|
||||
faFolder,
|
||||
faUserPlus,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { HexColorPicker } from "react-colorful";
|
||||
import { Collection } from "@prisma/client";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { CollectionIncludingMembersAndLinkCount, Member } from "@/types/global";
|
||||
import getPublicUserData from "@/lib/client/getPublicUserData";
|
||||
|
|
|
@ -110,7 +110,6 @@ export default function Navbar() {
|
|||
<ProfilePhoto
|
||||
src={account.image ? account.image : undefined}
|
||||
priority={true}
|
||||
className=""
|
||||
/>
|
||||
</div>
|
||||
<ul className="dropdown-content z-[1] menu p-1 shadow bg-base-200 border border-neutral-content rounded-xl w-40 mt-1">
|
||||
|
|
|
@ -8,6 +8,7 @@ type Props = {
|
|||
className?: string;
|
||||
priority?: boolean;
|
||||
name?: string;
|
||||
dimensionClass?: string;
|
||||
};
|
||||
|
||||
export default function ProfilePhoto({
|
||||
|
@ -15,6 +16,7 @@ export default function ProfilePhoto({
|
|||
className,
|
||||
priority,
|
||||
name,
|
||||
dimensionClass,
|
||||
}: Props) {
|
||||
const [image, setImage] = useState("");
|
||||
|
||||
|
@ -28,7 +30,11 @@ export default function ProfilePhoto({
|
|||
}, [src]);
|
||||
|
||||
return !image ? (
|
||||
<div className={`avatar w-8 h-8 placeholder ${className || ""}`}>
|
||||
<div
|
||||
className={`avatar placeholder ${className || ""} ${
|
||||
dimensionClass || "w-8 h-8 "
|
||||
}`}
|
||||
>
|
||||
<div className="bg-base-100 text-neutral rounded-full w-full h-full ring-2 ring-neutral-content">
|
||||
{name ? (
|
||||
<span className="text-2xl capitalize">{name.slice(0, 1)}</span>
|
||||
|
@ -41,7 +47,11 @@ export default function ProfilePhoto({
|
|||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className={`avatar w-8 h-8 drop-shadow-md ${className || ""}`}>
|
||||
<div
|
||||
className={`avatar drop-shadow-md ${className || ""} ${
|
||||
dimensionClass || "w-8 h-8 "
|
||||
}`}
|
||||
>
|
||||
<div className="rounded-full w-full h-full ring-2 ring-neutral-content">
|
||||
<Image
|
||||
alt=""
|
||||
|
|
|
@ -112,10 +112,7 @@ export default function Index() {
|
|||
onClick={() => setEditCollectionSharingModal(true)}
|
||||
>
|
||||
{collectionOwner.id ? (
|
||||
<ProfilePhoto
|
||||
src={collectionOwner.image || undefined}
|
||||
className="w-7 h-7"
|
||||
/>
|
||||
<ProfilePhoto src={collectionOwner.image || undefined} />
|
||||
) : undefined}
|
||||
{activeCollection.members
|
||||
.sort((a, b) => (a.userId as number) - (b.userId as number))
|
||||
|
|
|
@ -153,7 +153,7 @@ export default function PublicCollections() {
|
|||
{collectionOwner.id ? (
|
||||
<ProfilePhoto
|
||||
src={collectionOwner.image || undefined}
|
||||
className="w-7 h-7"
|
||||
dimensionClass="w-7 h-7"
|
||||
/>
|
||||
) : undefined}
|
||||
{collection.members
|
||||
|
|
|
@ -162,6 +162,7 @@ export default function Account() {
|
|||
<p className="mb-2">Display Name</p>
|
||||
<TextInput
|
||||
value={user.name || ""}
|
||||
className="bg-base-200"
|
||||
onChange={(e) => setUser({ ...user, name: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
|
@ -169,6 +170,7 @@ export default function Account() {
|
|||
<p className="mb-2">Username</p>
|
||||
<TextInput
|
||||
value={user.username || ""}
|
||||
className="bg-base-200"
|
||||
onChange={(e) => setUser({ ...user, username: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
|
@ -184,6 +186,7 @@ export default function Account() {
|
|||
) : undefined}
|
||||
<TextInput
|
||||
value={user.email || ""}
|
||||
className="bg-base-200"
|
||||
onChange={(e) => setUser({ ...user, email: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
|
@ -196,7 +199,7 @@ export default function Account() {
|
|||
<ProfilePhoto
|
||||
priority={true}
|
||||
src={user.image ? user.image : undefined}
|
||||
className="h-auto border-none w-28"
|
||||
dimensionClass="w-28 h-28"
|
||||
/>
|
||||
{user.image && (
|
||||
<div
|
||||
|
@ -206,13 +209,13 @@ export default function Account() {
|
|||
image: "",
|
||||
})
|
||||
}
|
||||
className="absolute top-1 left-1 w-5 h-5 flex items-center justify-center border p-1 border-slate-200 rounded-full bg-base-200 text-center select-none cursor-pointer duration-100 hover:text-red-500"
|
||||
className="absolute top-1 left-1 btn btn-xs btn-circle btn-neutral btn-outline bg-base-100"
|
||||
>
|
||||
<FontAwesomeIcon icon={faClose} className="w-3 h-3" />
|
||||
</div>
|
||||
)}
|
||||
<div className="absolute -bottom-3 left-0 right-0 mx-auto w-fit text-center">
|
||||
<label className="border border-slate-200 rounded-md bg-base-200 px-2 text-center select-none cursor-pointer duration-100 hover:border-primary">
|
||||
<label className="btn btn-xs btn-secondary btn-outline bg-base-100">
|
||||
Browse...
|
||||
<input
|
||||
type="file"
|
||||
|
@ -240,71 +243,60 @@ export default function Account() {
|
|||
<div className="flex gap-3 flex-col">
|
||||
<div>
|
||||
<p className="mb-2">Import your data from other platforms.</p>
|
||||
<div
|
||||
onClick={() => setImportDropdown(true)}
|
||||
className="w-fit relative"
|
||||
id="import-dropdown"
|
||||
>
|
||||
<div
|
||||
<details className="dropdown">
|
||||
<summary
|
||||
className="flex gap-2 text-sm btn btn-outline btn-secondary btn-xs"
|
||||
id="import-dropdown"
|
||||
className="border border-slate-200 rounded-md bg-base-200 px-2 text-center select-none cursor-pointer duration-100 hover:border-primary"
|
||||
>
|
||||
Import From
|
||||
</div>
|
||||
{importDropdown ? (
|
||||
<ClickAwayHandler
|
||||
onClickOutside={(e: Event) => {
|
||||
const target = e.target as HTMLInputElement;
|
||||
if (target.id !== "import-dropdown")
|
||||
setImportDropdown(false);
|
||||
}}
|
||||
className={`absolute top-7 left-0 w-52 py-1 shadow-md border border-neutral-content bg-base-200 rounded-md flex flex-col z-20`}
|
||||
>
|
||||
<div className="cursor-pointer rounded-md">
|
||||
<label
|
||||
htmlFor="import-linkwarden-file"
|
||||
title="JSON File"
|
||||
className="flex items-center gap-2 py-1 px-2 hover:bg-neutral-content duration-100 cursor-pointer"
|
||||
>
|
||||
Linkwarden File...
|
||||
<input
|
||||
type="file"
|
||||
name="photo"
|
||||
id="import-linkwarden-file"
|
||||
accept=".json"
|
||||
className="hidden"
|
||||
onChange={(e) =>
|
||||
importBookmarks(e, MigrationFormat.linkwarden)
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
htmlFor="import-html-file"
|
||||
title="HTML File"
|
||||
className="flex items-center gap-2 py-1 px-2 hover:bg-neutral-content duration-100 cursor-pointer"
|
||||
>
|
||||
Bookmarks HTML file...
|
||||
<input
|
||||
type="file"
|
||||
name="photo"
|
||||
id="import-html-file"
|
||||
accept=".html"
|
||||
className="hidden"
|
||||
onChange={(e) =>
|
||||
importBookmarks(e, MigrationFormat.htmlFile)
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</ClickAwayHandler>
|
||||
) : null}
|
||||
</div>
|
||||
</summary>
|
||||
<ul className="shadow menu dropdown-content z-[1] p-1 bg-base-200 border border-neutral-content rounded-xl mt-1 w-60">
|
||||
<li>
|
||||
<label
|
||||
className="px-2 py-1 rounded-lg"
|
||||
htmlFor="import-linkwarden-file"
|
||||
title="JSON File"
|
||||
>
|
||||
From Linkwarden
|
||||
<input
|
||||
type="file"
|
||||
name="photo"
|
||||
id="import-linkwarden-file"
|
||||
accept=".json"
|
||||
className="hidden"
|
||||
onChange={(e) =>
|
||||
importBookmarks(e, MigrationFormat.linkwarden)
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label
|
||||
className="px-2 py-1 rounded-lg"
|
||||
htmlFor="import-html-file"
|
||||
title="HTML File"
|
||||
>
|
||||
From Bookmarks HTML file
|
||||
<input
|
||||
type="file"
|
||||
name="photo"
|
||||
id="import-html-file"
|
||||
accept=".html"
|
||||
className="hidden"
|
||||
onChange={(e) =>
|
||||
importBookmarks(e, MigrationFormat.htmlFile)
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="mb-2">Download your data instantly.</p>
|
||||
<Link className="w-fit" href="/api/v1/migration">
|
||||
<div className="border w-fit border-slate-200 rounded-md bg-base-200 px-2 text-center select-none cursor-pointer duration-100 hover:border-primary">
|
||||
<div className="btn btn-outline btn-secondary btn-xs">
|
||||
Export Data
|
||||
</div>
|
||||
</Link>
|
||||
|
@ -339,7 +331,7 @@ export default function Account() {
|
|||
visibility to your profile. Separated by comma.
|
||||
</p>
|
||||
<textarea
|
||||
className="w-full resize-none border rounded-md duration-100 bg-base-200 p-2 outline-none border-neutral-content focus:border-sky-300 dark:focus:border-sky-600"
|
||||
className="w-full resize-none border rounded-md duration-100 bg-base-200 p-2 outline-none border-neutral-content focus:border-primary"
|
||||
placeholder="Your profile is hidden from everyone right now..."
|
||||
value={whitelistedUsersTextbox}
|
||||
onChange={(e) => setWhiteListedUsersTextbox(e.target.value)}
|
||||
|
|
|
@ -59,7 +59,7 @@ export default function Api() {
|
|||
<div className="divider my-3"></div>
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="badge bg-orange-500 rounded-md border border-black w-fit px-2 text-black">
|
||||
<div className="badge badge-warning rounded-md w-fit p-4">
|
||||
Status: Under Development
|
||||
</div>
|
||||
|
||||
|
|
|
@ -2,13 +2,9 @@ import SettingsLayout from "@/layouts/SettingsLayout";
|
|||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faSun, faMoon } from "@fortawesome/free-solid-svg-icons";
|
||||
import { useState, useEffect } from "react";
|
||||
import { faClose } from "@fortawesome/free-solid-svg-icons";
|
||||
import useAccountStore from "@/store/account";
|
||||
import { AccountSettings } from "@/types/global";
|
||||
import { toast } from "react-hot-toast";
|
||||
import TextInput from "@/components/TextInput";
|
||||
import { resizeImage } from "@/lib/client/resizeImage";
|
||||
import ProfilePhoto from "@/components/ProfilePhoto";
|
||||
import SubmitButton from "@/components/SubmitButton";
|
||||
import React from "react";
|
||||
import Checkbox from "@/components/Checkbox";
|
||||
|
@ -16,7 +12,7 @@ import LinkPreview from "@/components/LinkPreview";
|
|||
import useLocalSettingsStore from "@/store/localSettings";
|
||||
|
||||
export default function Appearance() {
|
||||
const { settings, updateSettings } = useLocalSettingsStore();
|
||||
const { updateSettings } = useLocalSettingsStore();
|
||||
const submit = async () => {
|
||||
setSubmitLoader(true);
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ export default function Password() {
|
|||
|
||||
<TextInput
|
||||
value={newPassword}
|
||||
className="bg-base-200"
|
||||
onChange={(e) => setNewPassword1(e.target.value)}
|
||||
placeholder="••••••••••••••"
|
||||
type="password"
|
||||
|
@ -67,6 +68,7 @@ export default function Password() {
|
|||
|
||||
<TextInput
|
||||
value={newPassword2}
|
||||
className="bg-base-200"
|
||||
onChange={(e) => setNewPassword2(e.target.value)}
|
||||
placeholder="••••••••••••••"
|
||||
type="password"
|
||||
|
|
Ŝarĝante…
Reference in New Issue