Skip to content

Commit

Permalink
profile crud
Browse files Browse the repository at this point in the history
  • Loading branch information
sinamics committed Apr 5, 2023
1 parent 1b0b524 commit abf7001
Show file tree
Hide file tree
Showing 9 changed files with 535 additions and 48 deletions.
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"classnames": "^2.3.2",
"daisyui": "^2.51.5",
"next": "^13.2.1",
"next-auth": "^4.19.0",
"next-auth": "^4.21.1",
"next-themes": "^0.2.1",
"prisma": "^4.12.0",
"react": "18.2.0",
Expand Down
42 changes: 42 additions & 0 deletions src/components/elements/input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useEffect, useRef } from "react";

interface PasswordInputProps {
placeholder: string;
value: string;
name: string;
type: string;
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
focus?: boolean;
}

const Input = ({
placeholder,
value,
name,
onChange,
type,
focus = false,
...rest
}: PasswordInputProps) => {
const inputRef = useRef<HTMLInputElement>(null);

useEffect(() => {
if (focus && inputRef.current) {
inputRef.current.focus();
}
}, [focus]);
return (
<input
type={type}
name={name}
placeholder={placeholder}
value={value}
onChange={onChange}
className="input w-full max-w-xs"
ref={inputRef}
{...rest}
/>
);
};

export default Input;
125 changes: 125 additions & 0 deletions src/components/elements/inputField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */
import { useState } from "react";
import Input from "~/components/elements/input";
import EditIcon from "~/icons/edit";

interface FieldConfig {
name: string;
initialValue?: string;
type: string;
placeholder: string;
displayValue?: string;
}

interface FormProps {
label: string;
isLoading?: boolean;
placeholder?: string;
fields: FieldConfig[];
submitHandler: (formValues: {
[key: string]: string;
}) => Promise<unknown> | string | void;
badge?: {
text: string;
color: string;
};
}

const InputField = ({
label,
placeholder,
fields,
submitHandler,
badge,
isLoading,
}: FormProps) => {
const [showInputs, setShowInputs] = useState(false);
const [formValues, setFormValues] = useState(
fields.reduce((acc, field) => {
acc[field.name] = field.initialValue || "";
return acc;
}, {})
);

const handleEditClick = () => setShowInputs(!showInputs);

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const targetName = e.target.name;
const targetValue = e.target.value;
setFormValues({ ...formValues, [targetName]: targetValue });
};

const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const response = await submitHandler(formValues);
if (response) {
setShowInputs(false);
}
};
const renderInputs = () => (
<form
onSubmit={(event) => {
void handleSubmit(event);
}}
className="my-3 space-y-3"
>
{fields.map((field, i) => (
<Input
focus={i === 0}
type={field.type}
key={i}
placeholder={field.placeholder}
value={formValues[field.name]}
onChange={handleChange}
name={field.name}
/>
))}
<div className="flex gap-3">
<button className="btn-primary btn" type="submit">
Submit
</button>
<button
className="btn"
onClick={(e) => {
e.preventDefault();
handleEditClick();
}}
>
Cancel
</button>
</div>
</form>
);
const renderLoading = () => (
<div className="mt-1 text-sm">
<progress className="progress w-56"></progress>
</div>
);
return (
<>
<dt className="flex items-center gap-2 text-sm font-medium">
{label}
<EditIcon onClick={handleEditClick} />
</dt>
{showInputs ? (
isLoading ? (
renderLoading()
) : (
renderInputs()
)
) : (
<dd className="mt-1 flex items-center gap-2 text-sm">
{placeholder ?? fields[0].placeholder}
{badge && (
<div className={`badge badge-${badge.color}`}>{badge.text}</div>
)}
</dd>
)}
</>
);
};

export default InputField;
17 changes: 11 additions & 6 deletions src/components/modules/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,15 @@ const Sidebar = (): JSX.Element => {
Account
</span>
</li>
{/* <li className="my-px">
<a
href="#"
className="flex h-10 flex-row items-center rounded-lg px-3 text-gray-300 hover:bg-gray-100 hover:text-gray-700"
<li className="my-px">
<Link
href="/profile"
className={`flex h-10 flex-row items-center rounded-lg px-3
${
router.pathname === "/profile"
? "bg-gray-100 text-gray-700"
: "hover:bg-slate-700"
}`}
>
<span className="flex items-center justify-center text-lg text-gray-400">
<svg
Expand All @@ -125,8 +130,8 @@ const Sidebar = (): JSX.Element => {
</svg>
</span>
<span className="ml-3">Profile</span>
</a>
</li> */}
</Link>
</li>
{/* <li className="my-px">
<a
href="#"
Expand Down
29 changes: 29 additions & 0 deletions src/icons/edit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
interface IeditIcon {
// add optional className prop
className?: string;
onClick?: () => void;
}

const EditIcon = ({ className, onClick, ...rest }: IeditIcon) => {
return (
<span onClick={onClick}>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth="1.5"
stroke="currentColor"
className={`h-4 w-4 cursor-pointer text-primary ${className}`}
{...rest}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10"
/>
</svg>
</span>
);
};

export default EditIcon;
Loading

0 comments on commit abf7001

Please sign in to comment.