|
@ -80,16 +80,16 @@ export default function ChangePassword({
|
|||
value={newPassword}
|
||||
onChange={(e) => setNewPassword1(e.target.value)}
|
||||
type="password"
|
||||
placeholder="*****************"
|
||||
placeholder="***********"
|
||||
className="w-full rounded-md p-3 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||
/>
|
||||
<p className="text-sm text-sky-500">Re-enter New Password</p>
|
||||
<p className="text-sm text-sky-500">Confirm New Password</p>
|
||||
|
||||
<input
|
||||
value={newPassword2}
|
||||
onChange={(e) => setNewPassword2(e.target.value)}
|
||||
type="password"
|
||||
placeholder="*****************"
|
||||
placeholder="***********"
|
||||
className="w-full rounded-md p-3 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||
/>
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ import useCollectionStore from "@/store/collections";
|
|||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faFolder,
|
||||
faBox,
|
||||
faHashtag,
|
||||
faChartSimple,
|
||||
faChevronDown,
|
||||
|
@ -13,6 +12,7 @@ import Link from "next/link";
|
|||
import { useRouter } from "next/router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Disclosure, Transition } from "@headlessui/react";
|
||||
import Image from "next/image";
|
||||
|
||||
export default function Sidebar({ className }: { className?: string }) {
|
||||
const [tagDisclosure, setTagDisclosure] = useState<boolean>(() => {
|
||||
|
@ -53,55 +53,50 @@ export default function Sidebar({ className }: { className?: string }) {
|
|||
<div
|
||||
className={`bg-gray-100 h-screen w-64 xl:w-80 overflow-y-auto border-solid border-r-sky-100 px-2 border z-20 ${className}`}
|
||||
>
|
||||
<p className="p-2 text-sky-500 font-bold text-2xl my-2 leading-4">
|
||||
Linkwarden
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col gap-1">
|
||||
<Link href="/dashboard">
|
||||
<div
|
||||
className={`${
|
||||
active === "/dashboard"
|
||||
? "bg-sky-200"
|
||||
: "hover:bg-slate-200 bg-gray-100"
|
||||
} outline-sky-100 outline-1 duration-100 py-1 px-2 rounded-md cursor-pointer flex items-center gap-2`}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faChartSimple}
|
||||
className={`w-6 h-6 drop-shadow text-sky-500`}
|
||||
/>
|
||||
<p className="text-sky-600">Dashboard</p>
|
||||
</div>
|
||||
<div className="flex justify-center gap-2 mt-2">
|
||||
<Link
|
||||
href="/dashboard"
|
||||
className={`${
|
||||
active === "/dashboard"
|
||||
? "bg-sky-200"
|
||||
: "hover:bg-slate-200 bg-gray-100"
|
||||
} outline-sky-100 outline-1 duration-100 py-1 px-2 rounded-md cursor-pointer flex justify-center flex-col items-center gap-1`}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faChartSimple}
|
||||
className={`w-8 h-8 drop-shadow text-sky-500`}
|
||||
/>
|
||||
<p className="text-sky-600 text-xs font-semibold">Dashboard</p>
|
||||
</Link>
|
||||
|
||||
<Link href="/links">
|
||||
<div
|
||||
className={`${
|
||||
active === "/links"
|
||||
? "bg-sky-200"
|
||||
: "hover:bg-slate-200 bg-gray-100"
|
||||
} outline-sky-100 outline-1 duration-100 py-1 px-2 rounded-md cursor-pointer flex items-center gap-2`}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faLink}
|
||||
className={`w-6 h-6 drop-shadow text-sky-500`}
|
||||
/>
|
||||
<p className="text-sky-600">All Links</p>
|
||||
</div>
|
||||
<Link
|
||||
href="/links"
|
||||
className={`${
|
||||
active === "/links"
|
||||
? "bg-sky-200"
|
||||
: "hover:bg-slate-200 bg-gray-100"
|
||||
} outline-sky-100 outline-1 duration-100 py-1 px-2 rounded-md cursor-pointer flex justify-center flex-col items-center gap-1 w-full`}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faLink}
|
||||
className={`w-8 h-8 drop-shadow text-sky-500`}
|
||||
/>
|
||||
<p className="text-sky-600 text-xs font-semibold">All Links</p>
|
||||
</Link>
|
||||
|
||||
<Link href="/collections">
|
||||
<div
|
||||
className={`${
|
||||
active === "/collections" ? "bg-sky-200" : "hover:bg-slate-200"
|
||||
} outline-sky-100 outline-1 duration-100 py-1 px-2 rounded-md cursor-pointer flex items-center gap-2`}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faBox}
|
||||
className={`w-6 h-6 drop-shadow text-sky-500`}
|
||||
/>
|
||||
<p className="text-sky-600">All Collections</p>
|
||||
</div>
|
||||
<Link
|
||||
href="/collections"
|
||||
className={`${
|
||||
active === "/collections" ? "bg-sky-200" : "hover:bg-slate-200"
|
||||
} outline-sky-100 outline-1 duration-100 py-1 px-2 rounded-md cursor-pointer flex justify-center flex-col items-center gap-1 w-full`}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faFolder}
|
||||
className={`w-8 h-8 drop-shadow text-sky-500`}
|
||||
/>
|
||||
<p className="text-sky-600 text-xs font-semibold">
|
||||
<span className="hidden xl:inline-block">All</span> Collections
|
||||
</p>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -20,14 +20,22 @@ export default function AuthRedirect({ children }: Props) {
|
|||
if (!router.pathname.startsWith("/public")) {
|
||||
if (
|
||||
status === "authenticated" &&
|
||||
(router.pathname === "/login" || router.pathname === "/register")
|
||||
(router.pathname === "/login" ||
|
||||
router.pathname === "/register" ||
|
||||
router.pathname === "/confirmation" ||
|
||||
router.pathname === "/forgot")
|
||||
) {
|
||||
router.push("/").then(() => {
|
||||
setRedirect(false);
|
||||
});
|
||||
} else if (
|
||||
status === "unauthenticated" &&
|
||||
!(router.pathname === "/login" || router.pathname === "/register")
|
||||
!(
|
||||
router.pathname === "/login" ||
|
||||
router.pathname === "/register" ||
|
||||
router.pathname === "/confirmation" ||
|
||||
router.pathname === "/forgot"
|
||||
)
|
||||
) {
|
||||
router.push("/login").then(() => {
|
||||
setRedirect(false);
|
||||
|
|
|
@ -5,8 +5,6 @@ import { createTransport } from "nodemailer";
|
|||
export default async function sendVerificationRequest(
|
||||
params: SendVerificationRequestParams
|
||||
) {
|
||||
console.log(params);
|
||||
|
||||
const { identifier, url, provider, theme } = params;
|
||||
const { host } = new URL(url);
|
||||
const transport = createTransport(provider.server);
|
||||
|
|
|
@ -19,7 +19,7 @@ const addMemberToCollection = async (
|
|||
// member can't be empty
|
||||
memberUsername.trim() !== "" &&
|
||||
// member can't be the owner
|
||||
memberUsername.trim() !== ownerUsername
|
||||
memberUsername.trim().toLowerCase() !== ownerUsername.toLowerCase()
|
||||
) {
|
||||
// Lookup, get data/err, list ...
|
||||
const user = await getPublicUserDataByUsername(
|
||||
|
@ -40,7 +40,7 @@ const addMemberToCollection = async (
|
|||
});
|
||||
}
|
||||
} else if (checkIfMemberAlreadyExists) toast.error("User already exists.");
|
||||
else if (memberUsername.trim() === ownerUsername)
|
||||
else if (memberUsername.trim().toLowerCase() === ownerUsername.toLowerCase())
|
||||
toast.error("You are already the collection owner.");
|
||||
};
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
"@types/nodemailer": "^6.4.8",
|
||||
"@types/react": "18.2.14",
|
||||
"@types/react-dom": "18.2.6",
|
||||
|
||||
"bcrypt": "^5.1.0",
|
||||
"colorthief": "^2.4.0",
|
||||
"crypto-js": "^4.1.1",
|
||||
|
|
|
@ -10,6 +10,8 @@ import { Adapter } from "next-auth/adapters";
|
|||
import sendVerificationRequest from "@/lib/api/sendVerificationRequest";
|
||||
import { Provider } from "next-auth/providers";
|
||||
|
||||
let email;
|
||||
|
||||
const providers: Provider[] = [
|
||||
CredentialsProvider({
|
||||
type: "credentials",
|
||||
|
@ -63,6 +65,7 @@ if (process.env.EMAIL_SERVER && process.env.EMAIL_FROM)
|
|||
from: process.env.EMAIL_FROM,
|
||||
maxAge: 600,
|
||||
sendVerificationRequest(params) {
|
||||
email = params.identifier;
|
||||
sendVerificationRequest(params);
|
||||
},
|
||||
})
|
||||
|
@ -76,6 +79,7 @@ export const authOptions: AuthOptions = {
|
|||
providers,
|
||||
pages: {
|
||||
signIn: "/login",
|
||||
verifyRequest: "/confirmation",
|
||||
},
|
||||
callbacks: {
|
||||
session: async ({ session, token }: { session: Session; token: JWT }) => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import useCollectionStore from "@/store/collections";
|
||||
import {
|
||||
faBox,
|
||||
faEllipsis,
|
||||
faFolder,
|
||||
faPlus,
|
||||
faSort,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
|
@ -36,7 +36,7 @@ export default function Collections() {
|
|||
<div className="flex gap-3 items-end">
|
||||
<div className="flex gap-2">
|
||||
<FontAwesomeIcon
|
||||
icon={faBox}
|
||||
icon={faFolder}
|
||||
className="sm:w-8 sm:h-8 w-6 h-6 mt-2 text-sky-500 drop-shadow"
|
||||
/>
|
||||
<p className="sm:text-4xl text-3xl capitalize bg-gradient-to-tr from-sky-500 to-slate-400 bg-clip-text text-transparent font-bold">
|
||||
|
|
|
@ -1,23 +1,24 @@
|
|||
import { signIn } from "next-auth/react";
|
||||
import React from "react";
|
||||
|
||||
export default function EmailConfirmaion({ email }: { email: string }) {
|
||||
export default function EmailConfirmaion() {
|
||||
return (
|
||||
<div className="overflow-y-auto py-2 fixed top-0 bottom-0 right-0 left-0 bg-gray-500 bg-opacity-10 backdrop-blur-sm flex items-center fade-in z-30">
|
||||
<div className="mx-auto p-3 rounded-xl border border-sky-100 shadow-lg bg-gray-100 text-sky-800">
|
||||
<div className="mx-auto p-3 text-center rounded-xl border border-sky-100 shadow-lg bg-gray-100 text-sky-800">
|
||||
<p className="text-center text-2xl mb-2">Please check your email</p>
|
||||
<p>A sign in link has been sent to your email address.</p>
|
||||
<div
|
||||
<p>You can safely close this page.</p>
|
||||
{/* <div
|
||||
onClick={() =>
|
||||
signIn("email", {
|
||||
email,
|
||||
email: email,
|
||||
redirect: false,
|
||||
})
|
||||
}
|
||||
className="mx-auto font-semibold mt-2 cursor-pointer w-fit"
|
||||
>
|
||||
Resend?
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
|
@ -1,6 +1,6 @@
|
|||
import EmailConfirmaion from "@/components/Modal/EmailConfirmaion";
|
||||
import SubmitButton from "@/components/SubmitButton";
|
||||
import { signIn } from "next-auth/react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
|
@ -11,7 +11,6 @@ interface FormData {
|
|||
|
||||
export default function Forgot() {
|
||||
const [submitLoader, setSubmitLoader] = useState(false);
|
||||
const [showConfirmation, setShowConfirmation] = useState(false);
|
||||
|
||||
const [form, setForm] = useState<FormData>({
|
||||
email: "",
|
||||
|
@ -23,20 +22,16 @@ export default function Forgot() {
|
|||
|
||||
const load = toast.loading("Sending login link...");
|
||||
|
||||
const res = await signIn("email", {
|
||||
await signIn("email", {
|
||||
email: form.email,
|
||||
callbackUrl: window.location.origin,
|
||||
callbackUrl: "/",
|
||||
});
|
||||
|
||||
setShowConfirmation(true);
|
||||
|
||||
toast.dismiss(load);
|
||||
|
||||
setSubmitLoader(false);
|
||||
|
||||
if (!res?.ok) {
|
||||
toast.error("Invalid login.");
|
||||
}
|
||||
toast.success("Login link sent.");
|
||||
} else {
|
||||
toast.error("Please fill out all the fields.");
|
||||
}
|
||||
|
@ -44,30 +39,35 @@ export default function Forgot() {
|
|||
|
||||
return (
|
||||
<>
|
||||
{showConfirmation && form.email ? (
|
||||
<EmailConfirmaion email={form.email} />
|
||||
) : undefined}
|
||||
<p className="text-xl font-bold text-center text-sky-500 mt-10 mb-3">
|
||||
Linkwarden
|
||||
</p>
|
||||
<div className="p-5 mx-auto flex flex-col gap-3 justify-between sm:w-[28rem] w-80 bg-slate-50 rounded-md border border-sky-100">
|
||||
<div className="my-5 text-center">
|
||||
<p className="text-3xl font-bold text-sky-500">Password reset</p>
|
||||
<div className="p-5 mt-10 mx-auto flex flex-col gap-3 justify-between sm:w-[28rem] w-80 bg-slate-50 rounded-md border border-sky-100">
|
||||
<div className="flex flex-col gap-2 sm:flex-row justify-between items-center mb-5">
|
||||
<Image
|
||||
src="/linkwarden.png"
|
||||
width={1694}
|
||||
height={483}
|
||||
alt="Linkwarden"
|
||||
className="h-12 w-fit"
|
||||
/>
|
||||
<div className="text-center sm:text-right">
|
||||
<p className="text-3xl font-bold text-sky-500">Password Reset</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-sky-500 w-fit font-semibold">Email</p>
|
||||
<div>
|
||||
<p className="text-sm text-sky-500 w-fit font-semibold mb-1">Email</p>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
placeholder="johnny@example.com"
|
||||
value={form.email}
|
||||
onChange={(e) => setForm({ ...form, email: e.target.value })}
|
||||
className="w-full rounded-md p-3 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||
/>
|
||||
|
||||
<p className="text-md text-gray-500">
|
||||
Make sure to change your password in the profile settings afterwards.
|
||||
</p>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="johnny@example.com"
|
||||
value={form.email}
|
||||
onChange={(e) => setForm({ ...form, email: e.target.value })}
|
||||
className="w-full rounded-md p-2 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||
/>
|
||||
<p className="text-md text-gray-500 mt-1">
|
||||
Make sure to change your password in the profile settings
|
||||
afterwards.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<SubmitButton
|
||||
onClick={loginUser}
|
||||
|
@ -75,11 +75,11 @@ export default function Forgot() {
|
|||
className="mt-2 w-full text-center"
|
||||
loading={submitLoader}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-baseline gap-1 justify-center my-3">
|
||||
<Link href={"/login"} className="block text-sky-500 font-bold">
|
||||
Go back
|
||||
</Link>
|
||||
<div className="flex items-baseline gap-1 justify-center">
|
||||
<Link href={"/login"} className="block text-sky-500 font-bold">
|
||||
Go back
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import SubmitButton from "@/components/SubmitButton";
|
||||
import { signIn } from "next-auth/react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
|
@ -45,60 +46,72 @@ export default function Login() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<p className="text-xl font-bold text-center text-sky-500 mt-10 mb-3">
|
||||
Linkwarden
|
||||
</p>
|
||||
<div className="p-5 mx-auto flex flex-col gap-3 justify-between sm:w-[28rem] w-80 bg-slate-50 rounded-md border border-sky-100">
|
||||
<div className="my-5 text-center">
|
||||
<p className="text-3xl font-bold text-sky-500">Welcome back</p>
|
||||
<p className="text-md font-semibold text-sky-400">
|
||||
Sign in to your account
|
||||
</p>
|
||||
<div className="p-5 my-10 mx-auto flex flex-col gap-3 justify-between sm:w-[28rem] w-80 bg-slate-50 rounded-md border border-sky-100">
|
||||
<div className="text-right flex flex-col gap-2 sm:flex-row justify-between items-center mb-5">
|
||||
<Image
|
||||
src="/linkwarden.png"
|
||||
width={1694}
|
||||
height={483}
|
||||
alt="Linkwarden"
|
||||
className="h-12 w-fit"
|
||||
/>
|
||||
<div className="text-center sm:text-right">
|
||||
<p className="text-3xl font-bold text-sky-500">Welcome back</p>
|
||||
<p className="text-md font-semibold text-sky-400">
|
||||
Sign in to your account
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-sky-500 w-fit font-semibold">
|
||||
Username
|
||||
{EmailProvider ? " or Email" : undefined}
|
||||
</p>
|
||||
<div>
|
||||
<p className="text-sm text-sky-500 w-fit font-semibold mb-1">
|
||||
Username
|
||||
{EmailProvider ? "/Email" : undefined}
|
||||
</p>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
placeholder="johnny"
|
||||
value={form.username}
|
||||
onChange={(e) => setForm({ ...form, username: e.target.value })}
|
||||
className="w-full rounded-md p-3 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="johnny"
|
||||
value={form.username}
|
||||
onChange={(e) => setForm({ ...form, username: e.target.value })}
|
||||
className="w-full rounded-md p-2 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-sky-500 w-fit font-semibold">Password</p>
|
||||
<div>
|
||||
<p className="text-sm text-sky-500 w-fit font-semibold mb-1">
|
||||
Password
|
||||
</p>
|
||||
|
||||
<input
|
||||
type="password"
|
||||
placeholder="***********"
|
||||
value={form.password}
|
||||
onChange={(e) => setForm({ ...form, password: e.target.value })}
|
||||
className="w-full rounded-md p-2 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||
/>
|
||||
{EmailProvider && (
|
||||
<div className="w-fit ml-auto mt-1">
|
||||
<Link href={"/forgot"} className="text-gray-500 font-semibold">
|
||||
Forgot Password?
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<input
|
||||
type="password"
|
||||
placeholder="*****************"
|
||||
value={form.password}
|
||||
onChange={(e) => setForm({ ...form, password: e.target.value })}
|
||||
className="w-full rounded-md p-3 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||
/>
|
||||
<SubmitButton
|
||||
onClick={loginUser}
|
||||
label="Login"
|
||||
className="mt-2 w-full text-center"
|
||||
loading={submitLoader}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-baseline gap-1 justify-center my-3">
|
||||
<p className="w-fit text-gray-500">New here?</p>
|
||||
<Link href={"/register"} className="block text-sky-500 font-bold">
|
||||
Sign Up
|
||||
</Link>
|
||||
</div>
|
||||
{EmailProvider && (
|
||||
<div className="flex items-baseline gap-1 justify-center mb-3">
|
||||
<p className="w-fit text-gray-500">Forgot your password?</p>
|
||||
<Link href={"/forgot"} className="block text-sky-500 font-bold">
|
||||
Send login link
|
||||
<div className="flex items-baseline gap-1 justify-center">
|
||||
<p className="w-fit text-gray-500">New here?</p>
|
||||
<Link href={"/register"} className="block text-sky-500 font-bold">
|
||||
Sign Up
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -63,9 +63,9 @@ export default function PublicCollections() {
|
|||
})}
|
||||
</div>
|
||||
|
||||
<p className="text-center font-bold text-gray-500">
|
||||
{/* <p className="text-center font-bold text-gray-500">
|
||||
List created with <span className="text-sky-500">Linkwarden.</span>
|
||||
</p>
|
||||
</p> */}
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useState } from "react";
|
|||
import { toast } from "react-hot-toast";
|
||||
import SubmitButton from "@/components/SubmitButton";
|
||||
import { signIn } from "next-auth/react";
|
||||
import EmailConfirmaion from "@/components/Modal/EmailConfirmaion";
|
||||
import Image from "next/image";
|
||||
|
||||
const EmailProvider = process.env.NEXT_PUBLIC_EMAIL_PROVIDER;
|
||||
|
||||
|
@ -17,7 +17,6 @@ type FormData = {
|
|||
|
||||
export default function Register() {
|
||||
const [submitLoader, setSubmitLoader] = useState(false);
|
||||
const [showConfirmation, setShowConfirmation] = useState(false);
|
||||
|
||||
const [form, setForm] = useState<FormData>({
|
||||
name: "",
|
||||
|
@ -50,7 +49,7 @@ export default function Register() {
|
|||
const sendConfirmation = async () => {
|
||||
await signIn("email", {
|
||||
email: form.email,
|
||||
redirect: false,
|
||||
callbackUrl: "/",
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -76,10 +75,7 @@ export default function Register() {
|
|||
setSubmitLoader(false);
|
||||
|
||||
if (response.ok) {
|
||||
if (form.email) {
|
||||
await sendConfirmation();
|
||||
setShowConfirmation(true);
|
||||
}
|
||||
if (form.email) await sendConfirmation();
|
||||
|
||||
toast.success(
|
||||
EmailProvider
|
||||
|
@ -99,89 +95,110 @@ export default function Register() {
|
|||
|
||||
return (
|
||||
<>
|
||||
{showConfirmation && form.email ? (
|
||||
<EmailConfirmaion email={form.email} />
|
||||
) : undefined}
|
||||
<p className="text-xl font-bold text-center my-10 mb-3 text-sky-500">
|
||||
Linkwarden
|
||||
</p>
|
||||
<div className="p-5 mx-auto flex flex-col gap-3 justify-between sm:w-[28rem] w-80 bg-slate-50 rounded-md border border-sky-100">
|
||||
<div className="my-5 text-center">
|
||||
<p className="text-3xl font-bold text-sky-500">Get started</p>
|
||||
<p className="text-md font-semibold text-sky-400">
|
||||
Create a new account
|
||||
</p>
|
||||
<div className="p-5 mx-auto my-10 flex flex-col gap-3 justify-between sm:w-[28rem] w-80 bg-slate-50 rounded-md border border-sky-100">
|
||||
<div className="flex flex-col gap-2 sm:flex-row justify-between items-center mb-5">
|
||||
<Image
|
||||
src="/linkwarden.png"
|
||||
width={1694}
|
||||
height={483}
|
||||
alt="Linkwarden"
|
||||
className="h-12 w-fit"
|
||||
/>
|
||||
<div className="text-center sm:text-right">
|
||||
<p className="text-3xl font-bold text-sky-500">Get started</p>
|
||||
<p className="text-md font-semibold text-sky-400">
|
||||
Create a new account
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-sky-500 w-fit font-semibold">Display Name</p>
|
||||
<div>
|
||||
<p className="text-sm text-sky-500 w-fit font-semibold mb-1">
|
||||
Display Name
|
||||
</p>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Johnny"
|
||||
value={form.name}
|
||||
onChange={(e) => setForm({ ...form, name: e.target.value })}
|
||||
className="w-full rounded-md p-3 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Johnny"
|
||||
value={form.name}
|
||||
onChange={(e) => setForm({ ...form, name: e.target.value })}
|
||||
className="w-full rounded-md p-2 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-sky-500 w-fit font-semibold">Username</p>
|
||||
<div>
|
||||
<p className="text-sm text-sky-500 w-fit font-semibold mb-1">
|
||||
Username
|
||||
</p>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
placeholder="john"
|
||||
value={form.username}
|
||||
onChange={(e) => setForm({ ...form, username: e.target.value })}
|
||||
className="w-full rounded-md p-3 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="john"
|
||||
value={form.username}
|
||||
onChange={(e) => setForm({ ...form, username: e.target.value })}
|
||||
className="w-full rounded-md p-2 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{EmailProvider ? (
|
||||
<>
|
||||
<p className="text-sm text-sky-500 w-fit font-semibold">Email</p>
|
||||
<div>
|
||||
<p className="text-sm text-sky-500 w-fit font-semibold mb-1">
|
||||
Email
|
||||
</p>
|
||||
|
||||
<input
|
||||
type="email"
|
||||
placeholder="johnny@example.com"
|
||||
value={form.email}
|
||||
onChange={(e) => setForm({ ...form, email: e.target.value })}
|
||||
className="w-full rounded-md p-3 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||
className="w-full rounded-md p-2 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||
/>
|
||||
</>
|
||||
</div>
|
||||
) : undefined}
|
||||
|
||||
<p className="text-sm text-sky-500 w-fit font-semibold">Password</p>
|
||||
<div className="flex item-center gap-5">
|
||||
<div>
|
||||
<p className="text-sm text-sky-500 w-fit font-semibold mb-1">
|
||||
Password
|
||||
</p>
|
||||
|
||||
<input
|
||||
type="password"
|
||||
placeholder="*****************"
|
||||
value={form.password}
|
||||
onChange={(e) => setForm({ ...form, password: e.target.value })}
|
||||
className="w-full rounded-md p-3 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="***********"
|
||||
value={form.password}
|
||||
onChange={(e) => setForm({ ...form, password: e.target.value })}
|
||||
className="w-full rounded-md p-2 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-sky-500 w-fit font-semibold">
|
||||
Re-enter Password
|
||||
</p>
|
||||
<div>
|
||||
<p className="text-sm text-sky-500 w-fit font-semibold mb-1">
|
||||
Confirm Password
|
||||
</p>
|
||||
|
||||
<input
|
||||
type="password"
|
||||
placeholder="*****************"
|
||||
value={form.passwordConfirmation}
|
||||
onChange={(e) =>
|
||||
setForm({ ...form, passwordConfirmation: e.target.value })
|
||||
}
|
||||
className="w-full rounded-md p-3 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="***********"
|
||||
value={form.passwordConfirmation}
|
||||
onChange={(e) =>
|
||||
setForm({ ...form, passwordConfirmation: e.target.value })
|
||||
}
|
||||
className="w-full rounded-md p-2 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SubmitButton
|
||||
onClick={registerUser}
|
||||
label="Sign Up"
|
||||
className="mt-2 w-full text-center"
|
||||
loading={submitLoader}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-baseline gap-1 justify-center my-3">
|
||||
<p className="w-fit text-gray-500">Already have an account?</p>
|
||||
<Link href={"/login"} className="block w-min text-sky-500 font-bold">
|
||||
Login
|
||||
</Link>
|
||||
<div className="flex items-baseline gap-1 justify-center">
|
||||
<p className="w-fit text-gray-500">Already have an account?</p>
|
||||
<Link href={"/login"} className="block text-sky-500 font-bold">
|
||||
Login
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 215 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 645 B After Width: | Height: | Size: 650 B |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
BIN
public/icon.png
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 190 KiB |
After Width: | Height: | Size: 31 KiB |
|
@ -1,19 +1 @@
|
|||
{
|
||||
"name": "",
|
||||
"short_name": "",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|