Skip to content

Commit

Permalink
Merge pull request #9 from samuliasmala/antti/json-server-edit-button
Browse files Browse the repository at this point in the history
Muokkaus-näppäin json-serveriin. Mergetään main-branchiin.
  • Loading branch information
anttiasmala authored Feb 2, 2024
2 parents fcf52d9 + 0cc2f8e commit 9ac02bb
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 49 deletions.
28 changes: 18 additions & 10 deletions components/DeleteModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,35 @@ import React, { Dispatch, SetStateAction } from 'react';
import { Gift } from '~/pages';
import { Modal } from './Modal';
import { Button } from './Button';
import { getGift, removeGift } from '~/utils/jsonServerFunctions';
import { removeGift } from '~/utils/jsonServerFunctions';
import { SvgCheckMarkIcon } from '~/icons/CheckMarkIcon';
import { SvgDeclineIcon } from '~/icons/DeclineIcon';
import { isAxiosError } from 'axios';

type ModalType = {
type DeleteModal = {
gift: Gift;
giftListRefreshFunction: () => void;
refreshGiftList: () => void;
setIsModalOpen: Dispatch<SetStateAction<boolean>>;
};

export function DeleteModal({
gift,
giftListRefreshFunction,
refreshGiftList,
setIsModalOpen,
}: ModalType) {
}: DeleteModal) {
async function handleDeletion() {
const giftToBeDeleted = await getGift(gift.id);
if (giftToBeDeleted !== null) {
try {
await removeGift(gift.id);
} catch (e) {
if (isAxiosError(e) && e.response?.status === 404) {
console.error('Lahjaa ei löytynyt palvelimelta!');
} else if (e instanceof Error) {
console.error(e.message);
} else {
console.error(e);
}
}
giftListRefreshFunction();
refreshGiftList();
setIsModalOpen(false);
}

Expand All @@ -38,7 +46,7 @@ export function DeleteModal({
<Button className="border border-yellow-500 mt-3 p-0 row-start-3 row-end-3 col-start-1 col-end-1 w-[66px] h-[66px]">
<SvgCheckMarkIcon
className="bg-gray-300 hover:bg-gray-600 group/checkMarkIcon"
backgroundClassName="fill-black group-hover/checkMarkIcon:fill-yellow-400"
circleClassName="fill-black group-hover/checkMarkIcon:fill-yellow-400"
checkMarkClassName="fill-gray-300 group-hover/checkMarkIcon:fill-gray-600"
width={64}
height={64}
Expand All @@ -48,7 +56,7 @@ export function DeleteModal({
<Button className="border border-yellow-500 relative mt-3 p-0 left-32 sm:left-28 row-start-3 row-end-3 col-start-1 col-end-1 w-[66px] h-[66px] ">
<SvgDeclineIcon
className="group/declineIcon bg-gray-300 hover:bg-gray-600"
backgroundClassName="fill-black group-hover/declineIcon:fill-yellow-400"
circleClassName="fill-black group-hover/declineIcon:fill-yellow-400"
width={64}
height={64}
onClick={() => setIsModalOpen(false)}
Expand Down
115 changes: 115 additions & 0 deletions components/EditModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import {
Dispatch,
FormEvent,
SetStateAction,
useEffect,
useState,
} from 'react';
import { Gift } from '~/pages';
import { Modal } from './Modal';
import { TitleText } from './TitleText';
import { Button } from './Button';
import { updateGift } from '~/utils/jsonServerFunctions';
import { Input } from './Input';
import { SvgCheckMarkIcon } from '~/icons/CheckMarkIcon';
import { SvgDeclineIcon } from '~/icons/DeclineIcon';
import { isAxiosError } from 'axios';

type EditModal = {
gift: Gift;
refreshGiftList: () => void;
setIsModalOpen: Dispatch<SetStateAction<boolean>>;
};

export function EditModal({
gift,
refreshGiftList,
setIsModalOpen,
}: EditModal) {
const [giftReceiver, setGiftReceiver] = useState(gift.name);
const [giftName, setGiftName] = useState(gift.gift);

useEffect(() => {
function handleKeyDown(e: KeyboardEvent) {
if (e.key === 'Escape') {
setIsModalOpen(false);
}
}
document.addEventListener('keydown', handleKeyDown);
return function clearFunctions() {
document.removeEventListener('keydown', handleKeyDown);
};
}, [refreshGiftList, setIsModalOpen]);

async function handleEdit(e: FormEvent<HTMLElement>) {
e.preventDefault();
try {
await updateGift(gift.id, { name: giftReceiver, gift: giftName });
} catch (e) {
if (isAxiosError(e) && e.response?.status === 404) {
console.error('Lahjaa ei löytynyt palvelimelta!');
} else if (e instanceof Error) {
console.error(e.message);
} else {
console.error(e);
}
}
refreshGiftList();
setIsModalOpen(false);
}
return (
<Modal className="sm:w-[26rem]">
<form onSubmit={(e) => void handleEdit(e)}>
<TitleText className="row-start-1 row-end-1 ps-3 font-bold text-lg">
Muokkaus
</TitleText>
<div className="row-start-2 row-end-2 grid mt-1 pt-3">
<label className="row-start-1 row-end-1">Saaja</label>
<Input
className="row-start-2 row-end-2 ps-3 pt-5 text-lg w-full h-full font-bold"
onChange={(e) => setGiftReceiver(e.target.value)}
value={giftReceiver}
autoComplete="off"
/>
</div>
<div className="row-start-3 row-end-3 grid pt-3">
<label className="row-start-1 row-end-1">Lahja</label>
<Input
className="row-start-2 row-end-2 ps-3 pt-5 text-lg w-full h-full font-bold border"
onChange={(e) => setGiftName(e.target.value)}
value={giftName}
name="giftName"
autoComplete="off"
/>
</div>
<div className="row-start-4 row-end-4 grid">
<Button
className="relative mt-2 left-24 border border-yellow-500 p-0 row-start-1 row-end-1 col-start-1 col-end-1 w-[66px] h-[66px] "
type="submit"
>
<SvgCheckMarkIcon
width={64}
height={64}
className="bg-gray-300 hover:bg-gray-600 group/checkMarkIcon"
circleClassName="fill-black group-hover/checkMarkIcon:fill-yellow-400 "
checkMarkClassName="fill-gray-300 group-hover/checkMarkIcon:fill-gray-600"
/>
</Button>

<Button
className="mt-2 border border-yellow-500 relative p-0 row-start-1 row-end-1 col-start-2 col-end-2 w-[66px] h-[66px]"
type="button"
>
<SvgDeclineIcon
className="bg-gray-300 hover:bg-gray-600 group/declineIcon"
circleClassName="fill-black group-hover/declineIcon:fill-yellow-400"
width={64}
height={64}
onClick={() => setIsModalOpen(false)}
/>
</Button>
</div>
</form>
</Modal>
);
}
16 changes: 14 additions & 2 deletions components/Modal.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
import { ReactNode } from 'react';
import { createPortal } from 'react-dom';
import { twMerge } from 'tailwind-merge';

export function Modal({ children }: { children: ReactNode }) {
export function Modal({
children,
className,
}: {
children: ReactNode;
className?: string;
}) {
return createPortal(
<>
<div className="fixed left-0 top-0 z-[98] h-full w-full bg-black opacity-20" />
<div className="absolute z-[99] top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] ">
<div className="grid w-96 sm:w-96 border border-yellow-300 bg-gray-200">
<div
className={twMerge(
'grid w-96 sm:w-96 border border-yellow-300 bg-gray-200',
className,
)}
>
{children}
</div>
</div>
Expand Down
6 changes: 3 additions & 3 deletions icons/CheckMarkIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import type { SVGProps } from 'react';

type CheckMarkIcon = SVGProps<SVGSVGElement> & {
backgroundClassName?: string;
circleClassName?: string;
checkMarkClassName?: string;
};

export const SvgCheckMarkIcon = ({
backgroundClassName,
circleClassName,
checkMarkClassName,
...rest
}: CheckMarkIcon) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" {...rest}>
<path
className={backgroundClassName}
className={circleClassName}
d="M16 2a14 14 0 1 0 14 14A14 14 0 0 0 16 2Zm-2 19.59-5-5L10.59 15 14 18.41 21.41 11l1.596 1.586Z"
/>
<path
Expand Down
9 changes: 3 additions & 6 deletions icons/DeclineIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import type { SVGProps } from 'react';

type DeclineIcon = SVGProps<SVGSVGElement> & {
backgroundClassName?: string;
circleClassName?: string;
};

export const SvgDeclineIcon = ({
backgroundClassName,
...rest
}: DeclineIcon) => (
export const SvgDeclineIcon = ({ circleClassName, ...rest }: DeclineIcon) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...rest}>
<path
className={backgroundClassName}
className={circleClassName}
d="m8.4 17 3.6-3.6 3.6 3.6 1.4-1.4-3.6-3.6L17 8.4 15.6 7 12 10.6 8.4 7 7 8.4l3.6 3.6L7 15.6 8.4 17Zm3.6 5q-2.075 0-3.9-.788t-3.175-2.137q-1.35-1.35-2.137-3.175T2 12q0-2.075.788-3.9t2.137-3.175q1.35-1.35 3.175-2.137T12 2q2.075 0 3.9.788t3.175 2.137q1.35 1.35 2.138 3.175T22 12q0 2.075-.788 3.9t-2.137 3.175q-1.35 1.35-3.175 2.138T12 22Z"
/>
</svg>
Expand Down
80 changes: 54 additions & 26 deletions pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Button } from '~/components/Button';
import { TitleText } from '~/components/TitleText';
import { Input } from '../components/Input';
import { DeleteModal } from '~/components/DeleteModal';
import { EditModal } from '~/components/EditModal';
import { createGift, getAllGifts } from '~/utils/jsonServerFunctions';

const inter = Inter({ subsets: ['latin'] });
Expand All @@ -22,8 +23,10 @@ export default function Home() {
const [receiverError, setReceiverError] = useState(false);
const [newReceiver, setNewReceiver] = useState('');
const [newGiftName, setNewGiftName] = useState('');
const [isModalOpen, setIsModalOpen] = useState(false);
const [modalGiftData, setModalGiftData] = useState<Gift>();
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [deleteModalGiftData, setDeleteModalGiftData] = useState<Gift>();
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
const [editModalGiftData, setEditModalGiftData] = useState<Gift>();

useEffect(() => {
console.log('effect');
Expand Down Expand Up @@ -83,20 +86,6 @@ export default function Home() {
<div className="mt-5">
<form onSubmit={(e) => void handleSubmit(e)}>
<TitleText>Lahjalistaidea</TitleText>
<div className="pt-4 grid">
<label htmlFor="giftName">Lahja</label>
<Input
onChange={(event) => setNewGiftName(event.target.value)}
autoComplete="off"
type="text"
placeholder="Kortti"
name="giftName"
value={newGiftName}
/>
{giftNameError && (
<div className="text-red-500">Lahja on pakollinen</div>
)}
</div>
<div className="pt-4 grid">
<label htmlFor="receiver">Saaja</label>
<Input
Expand All @@ -111,6 +100,20 @@ export default function Home() {
<div className="text-red-500">Lahjansaaja on pakollinen</div>
)}
</div>
<div className="pt-4 grid">
<label htmlFor="giftName">Lahja</label>
<Input
onChange={(event) => setNewGiftName(event.target.value)}
autoComplete="off"
type="text"
placeholder="Kortti"
name="giftName"
value={newGiftName}
/>
{giftNameError && (
<div className="text-red-500">Lahja on pakollinen</div>
)}
</div>
<Button type="submit">Lisää</Button>
</form>
</div>
Expand All @@ -122,30 +125,55 @@ export default function Home() {
key={`${giftItem.id}_divbutton`}
className="animate-width whitespace-nowrap overflow-hidden"
>
<li
key={giftItem.id}
className="hover:line-through pointer-events-none"
>
<li key={giftItem.id}>
{giftItem.name} - {giftItem.gift}
<Button
onMouseOver={(e) =>
e.currentTarget.parentElement?.setAttribute(
'class',
'line-through',
)
}
onMouseOut={(e) =>
e.currentTarget.parentElement?.removeAttribute('class')
}
key={`${giftItem.id}_deletebutton`}
className="ms-5 p-0 w-16 h-8 hover:text-red-600 pointer-events-auto"
onClick={() => {
setModalGiftData(giftItem);
setIsModalOpen(true);
setDeleteModalGiftData(giftItem);
setIsDeleteModalOpen(true);
}}
type="button"
>
Poista
</Button>
<Button
key={`${giftItem.id}_editbutton`}
className="ms-3 p-0 w-20 h-8 hover:text-yellow-400"
onClick={() => {
setEditModalGiftData(giftItem);
setIsEditModalOpen(true);
}}
type="button"
>
Muokkaa
</Button>
</li>
</div>
))}
{isModalOpen && modalGiftData && (
{isEditModalOpen && editModalGiftData && (
<EditModal
gift={editModalGiftData}
refreshGiftList={() => void refreshGiftList()}
setIsModalOpen={setIsEditModalOpen}
/>
)}

{isDeleteModalOpen && deleteModalGiftData && (
<DeleteModal
gift={modalGiftData}
giftListRefreshFunction={() => void refreshGiftList()}
setIsModalOpen={setIsModalOpen}
gift={deleteModalGiftData}
refreshGiftList={() => void refreshGiftList()}
setIsModalOpen={setIsDeleteModalOpen}
/>
)}
</div>
Expand Down
4 changes: 2 additions & 2 deletions utils/jsonServerFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ export async function createGift(newObject: object) {
* @param id should be given a string that contains search parameters as query strings.
* @param newObject a new object of Gifts that will be replacing the old object
*/
export async function updateGift(id: string, newObject: Gift) {
await axios.put(`${baseURL}/${id}`, newObject);
export async function updateGift(id: string, newObject: Partial<Gift>) {
await axios.patch(`${baseURL}/${id}`, newObject);
}

/**
Expand Down

0 comments on commit 9ac02bb

Please sign in to comment.