Skip to content

Commit

Permalink
Start API implementation for editing and deleting dreams, tags and ch…
Browse files Browse the repository at this point in the history
…aracters
  • Loading branch information
bombies committed Oct 26, 2023
1 parent dbd0eab commit 531fcb9
Show file tree
Hide file tree
Showing 19 changed files with 529 additions and 130 deletions.
38 changes: 19 additions & 19 deletions package-lock.json

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

11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,26 @@
"private": true,
"scripts": {
"dev": "next dev",
"devturbo": "next dev --turbo",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@auth/prisma-adapter": "^1.0.5",
"@nextui-org/react": "^2.1.13",
"@prisma/client": "^5.5.0",
"@prisma/client": "^5.5.2",
"@theinternetfolks/snowflake": "^1.3.0",
"@uidotdev/usehooks": "^2.4.1",
"axios": "^1.5.1",
"bcrypt": "^5.1.1",
"framer-motion": "^10.16.4",
"next": "13.5.6",
"next": "^13.5.6",
"next-auth": "^4.24.3",
"next-nprogress-bar": "^2.1.2",
"next-themes": "^0.2.1",
"react": "^18",
"react-dom": "^18",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.47.0",
"react-hot-toast": "^2.4.1",
"sass": "^1.69.4",
Expand All @@ -39,7 +40,7 @@
"eslint": "^8",
"eslint-config-next": "13.5.6",
"postcss": "^8",
"prisma": "^5.5.0",
"prisma": "^5.5.2",
"tailwindcss": "^3",
"typescript": "^5"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ const CurrentDreamsContainer: FC = () => {
})
.sort((a, b) => new Date(b.createdAt.toString()).getTime() - new Date(a.createdAt.toString()).getTime())
.map(dream => (
<DreamCard key={dream.id} dream={dream}/>
)), [dreams.data, endOfToday, startOfToday])
<DreamCard
key={dream.id}
dream={dream}
optimisticRemove={dreams.optimisticData.removeOptimisticData}
/>
)), [dreams.data, dreams.optimisticData.removeOptimisticData, endOfToday, startOfToday])

return (
<div className="flex flex-col">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,51 @@
"use client"

import {FC, Fragment, useState} from "react";
import {FC, Fragment, useCallback, useState} from "react";
import {Dream} from "@prisma/client";
import {CardBody, CardHeader} from "@nextui-org/card";
import Card from "@/app/(site)/components/Card";
import DreamModal from "@/app/(site)/(internal)/dashboard/components/dreams/card/DreamModal";
import {OptimisticWorker} from "@/utils/client/client-data-utils";
import toast from "react-hot-toast";
import useSWRMutation from "swr/mutation";
import {deleteMutator, handleAxiosError} from "@/utils/client/client-utils";

type Props = {
dream: Dream
dream: Dream,
optimisticRemove?: OptimisticWorker<Dream>,
}

const DreamCard: FC<Props> = ({dream}) => {
const DeleteDream = (dreamId: string) => {
return useSWRMutation(`/api/me/dreams/${dreamId}`, deleteMutator<Dream>())
}

const DreamCard: FC<Props> = ({dream, optimisticRemove}) => {
const [modalOpen, setModalOpen] = useState(false)
const {trigger: deleteDream} = DeleteDream(dream.id)

const doDelete = useCallback(() => (
deleteDream()
.then(res => res.data)
.catch(handleAxiosError)
), [deleteDream])

return (
<Fragment>
<DreamModal
dream={dream}
isOpen={modalOpen}
onClose={() => setModalOpen(false)}
onDelete={() => {
if (optimisticRemove)
optimisticRemove(
doDelete, // TODO: Replace with API call
dream
)
.then(() => {
toast.success("Successfully removed that dream!")
})
setModalOpen(false)
}}
/>
<Card
isPressable
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
"use client"

import {FC, Fragment, useEffect, useMemo} from "react";
import {FC, Fragment, useEffect, useMemo, useState} from "react";
import Modal from "@/app/(site)/components/Modal";
import {Dream} from "@prisma/client";
import useSWR from "swr";
import {calcEstimatedReadingTime, fetcher} from "@/utils/client/client-utils";
import {DreamWithRelations} from "@/app/api/me/dreams/dreams.dto";
import {Chip} from "@nextui-org/chip";
import {Divider} from "@nextui-org/divider";
import {Button} from "@nextui-org/react";
import ConfirmationModal from "@/app/(site)/components/ConfirmationModal";
import TrashIcon from "@/app/(site)/components/icons/TrashIcon";

type Props = {
dream: Dream,
isOpen?: boolean,
onClose?: () => void
onClose?: () => void,
onDelete?: () => void,
}

const FetchFullDream = (dream: Dream, modalOpen: boolean) => {
return useSWR(modalOpen && `/api/me/dreams/${dream.id}?tags=true&characters=true`, fetcher<DreamWithRelations | null>, {refreshInterval: 0})
}

const DreamModal: FC<Props> = ({dream, isOpen, onClose}) => {
const DreamModal: FC<Props> = ({dream, isOpen, onClose, onDelete}) => {
const {data: fullDream, error: fullDreamError} = FetchFullDream(dream, isOpen ?? false)
const [deleteModalOpen, setDeleteModalOpen] = useState(false)

const tagChips = useMemo(() => fullDream?.tags?.map(tag => (
<Chip key={tag.id} color="primary" variant="flat" size="sm">
Expand All @@ -33,30 +39,56 @@ const DreamModal: FC<Props> = ({dream, isOpen, onClose}) => {
}, [fullDreamError])

return (
<Modal
size="2xl"
header={
<Fragment>
{(tagChips || fullDreamError) && (
<div className="flex flex-wrap gap-2 mb-3">
{tagChips ?? (fullDreamError &&
<Chip color="danger" variant="flat" size="sm">
Error Loading Tags
</Chip>
)}
</div>
)}
<h1 className="text-4xl phone:text-2xl">{dream.title}</h1>
<h3 className="text-subtext text-sm font-semibold italic">{dream.comments}</h3>
<h3 className="text-subtext text-xs italic">~{calcEstimatedReadingTime(dream.description)} min. read</h3>
</Fragment>
}
isOpen={isOpen}
onClose={onClose}
>
<article
className="text-[#EAE0FF] phone:text-sm whitespace-pre-wrap rounded-3xl border border-primary/40 bg-[#0C0015]/50 p-6">{dream.description}</article>
</Modal>
<Fragment>
<ConfirmationModal
title={`Delete Dream`}
size="xl"
isOpen={deleteModalOpen}
onAccept={onDelete}
onReject={() => setDeleteModalOpen(false)}
>
Are you sure you want to delete this dream?
</ConfirmationModal>
<Modal
size="2xl"
header={
<Fragment>
{(tagChips || fullDreamError) && (
<div className="flex flex-wrap gap-2 mb-3">
{tagChips ?? (fullDreamError &&
<Chip color="danger" variant="flat" size="sm">
Error Loading Tags
</Chip>
)}
</div>
)}
<h1 className="text-4xl phone:text-2xl">{dream.title}</h1>
<h3 className="text-subtext text-sm font-semibold italic">{dream.comments}</h3>
<h3 className="text-subtext text-xs italic">~{calcEstimatedReadingTime(dream.description)} min.
read</h3>
</Fragment>
}
isOpen={isOpen}
onClose={onClose}
>
<article
className="text-[#EAE0FF] phone:text-sm whitespace-pre-wrap rounded-3xl border border-primary/40 bg-[#0C0015]/50 p-6">{dream.description}</article>
<Divider className="my-6"/>
<div className="flex justify-end">
<Button
fullWidth={false}
size="lg"
color="danger"
variant="flat"
onPress={() => setDeleteModalOpen(true)}
startContent={<TrashIcon width={18} />}
>
Delete Dream
</Button>
</div>
</Modal>
</Fragment>

)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import CloseIcon from "@/app/(site)/components/icons/CloseIcon";
import axios from "axios";
import useSWRMutation from "swr/mutation";
import {Dream} from "@prisma/client";
import {handleAxiosError} from "@/utils/client/client-utils";
import {handleAxiosError, MutatorArgs, postMutator} from "@/utils/client/client-utils";
import {useSession} from "next-auth/react";
import toast from "react-hot-toast";
import AddTagModal from "@/app/(site)/(internal)/dashboard/components/dreams/forms/tags/AddTagModal";
Expand All @@ -31,15 +31,8 @@ type Props = {
onCreate?: (dream: Dream) => void;
}

type CreateDreamArgs = {
arg: {
dto: PostDreamDto
}
}

const CreateDream = () => {
const mutator = (url: string, {arg}: CreateDreamArgs) => axios.post<Dream | null>(url, arg.dto)
return useSWRMutation('/api/me/dreams', mutator)
return useSWRMutation('/api/me/dreams', postMutator<PostDreamDto, Dream | null>())
}

const LogDreamForm: FC<Props> = ({onCreate, onForget}) => {
Expand All @@ -51,7 +44,7 @@ const LogDreamForm: FC<Props> = ({onCreate, onForget}) => {
const {trigger: createDream, isMutating: dreamIsCreating} = CreateDream()

const handleDreamCreation = useCallback(async (dto: PostDreamDto) => (
createDream({dto})
createDream({body: dto})
.then(res => {
const dream = res.data!!
if (onCreate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import useSWR, {KeyedMutator} from "swr";
import {fetcher} from "@/utils/client/client-utils";
import {Dream} from "@prisma/client";
import {useCallback} from "react";
import {DataContextState} from "@/utils/client/client-data-utils";
import {DataContextState, OptimisticWorker} from "@/utils/client/client-data-utils";

export type DreamsState = DataContextState<Dream[], Dream>

const useDreams = (): DreamsState => {
const {data: dreams, isLoading: dreamsLoading, mutate: mutateDreams} = useSWR('/api/me/dreams', fetcher<Dream[]>)

const addOptimisticDream = useCallback(async (work: () => Promise<Dream | undefined | null>, optimisticDream: Dream) => {
const addOptimisticDream = useCallback<OptimisticWorker<Dream>>(async (work, optimisticDream) => {
if (!dreams)
return
const mutate = mutateDreams as KeyedMutator<Dream[]>
Expand All @@ -21,11 +21,12 @@ const useDreams = (): DreamsState => {
}

await mutate(doWork, {
optimisticData: [...dreams, optimisticDream]
optimisticData: [...dreams, optimisticDream],
rollbackOnError: true
})
}, [dreams, mutateDreams])

const removeOptimisticDream = useCallback(async (work: () => Promise<Dream | undefined | null>, removedOptimisticDream: Dream) => {
const removeOptimisticDream = useCallback<OptimisticWorker<Dream>>(async (work, removedOptimisticDream) => {
if (!dreams)
return
const mutate = mutateDreams as KeyedMutator<Dream[]>
Expand All @@ -37,7 +38,8 @@ const useDreams = (): DreamsState => {
}

await mutate(doWork, {
optimisticData: dreams.filter(dream => dream.id !== removedOptimisticDream.id)
optimisticData: dreams.filter(dream => dream.id !== removedOptimisticDream.id),
rollbackOnError: true,
})
}, [dreams, mutateDreams])

Expand Down
Loading

0 comments on commit 531fcb9

Please sign in to comment.