Skip to content

Commit

Permalink
add conversation modal and settings modal
Browse files Browse the repository at this point in the history
  • Loading branch information
ngquyduc committed Jul 23, 2023
1 parent a423874 commit ee82e23
Show file tree
Hide file tree
Showing 13 changed files with 635 additions and 81 deletions.
2 changes: 1 addition & 1 deletion app/(site)/components/AuthForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useCallback, useEffect, useState } from 'react'
import { FieldValues, SubmitHandler, useForm } from 'react-hook-form'
import { BsGoogle } from 'react-icons/bs'

import Button from '@/app/components/Buttons'
import Button from '@/app/components/Button'
import Input from '@/app/components/inputs/input'
import axios from 'axios'
import { signIn, useSession } from 'next-auth/react'
Expand Down
47 changes: 47 additions & 0 deletions app/api/conversations/[conversationId]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import getCurrentUser from '@/app/actions/getCurrentUser'
import prisma from '@/app/libs/prismadb'
import { NextResponse } from 'next/server'

interface IParams {
conversationId?: string
}

export async function DELETE(
request: Request,
{ params }: { params: IParams }
) {
try {
const { conversationId } = params
const currentUser = await getCurrentUser()
if (!currentUser?.id) {
return new NextResponse('Unauthorized', { status: 401 })
}

const existingConversation = await prisma.conversation.findUnique({
where: {
id: conversationId,
},
include: {
users: true,
},
})

if (!existingConversation) {
return new NextResponse('Invalid ID', { status: 400 })
}

const deletedConversation = await prisma.conversation.deleteMany({
where: {
id: conversationId,
userIds: {
hasSome: [currentUser.id],
},
},
})

return NextResponse.json(deletedConversation)
} catch (error) {
console.log(error, 'ERROR DELETE CONVERSATION')
return new NextResponse('Interal Error', { status: 500 })
}
}
29 changes: 29 additions & 0 deletions app/api/settings/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { NextResponse } from 'next/server'

import getCurrentUser from '@/app/actions/getCurrentUser'
import prisma from '@/app/libs/prismadb'

export async function POST(request: Request) {
try {
const currentUser = await getCurrentUser()
const body = await request.json()
const { name, image } = body

if (!currentUser?.id) {
return new NextResponse('Unauthorized', { status: 401 })
}

const updatedUser = await prisma.user.update({
where: { id: currentUser.id },
data: {
image: image,
name: name,
},
})

return NextResponse.json(updatedUser)
} catch (error) {
console.log(error, 'ERROR_MESSAGES')
return new NextResponse('Error', { status: 500 })
}
}
57 changes: 57 additions & 0 deletions app/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use client'

import clsx from 'clsx'

interface ButtonProps {
type?: 'button' | 'submit' | 'reset' | undefined
fullWidth?: boolean
children?: React.ReactNode
onClick?: () => void
secondary?: boolean
danger?: boolean
disabled?: boolean
}

const Button: React.FC<ButtonProps> = ({
type,
fullWidth,
children,
onClick,
secondary,
danger,
disabled,
}) => {
return (
<button
onClick={onClick}
type={type}
disabled={disabled}
className={clsx(
`
flex
justify-center
rounded-md
px-3
py-2
text-sm
font-semibold
focus-visible:outline
focus-visible:outline-2
focus-visible:outline-offset-2
`,
disabled && 'opacity-50 cursor-default',
fullWidth && 'w-full',
secondary ? 'text-gray-900' : 'text-white',
danger &&
'bg-rose-500 hover:bg-rose-600 focus-visible:outline-rose-600',
!secondary &&
!danger &&
'bg-sky-500 hover:bg-sky-600 focus-visible:outline-sky-600'
)}
>
{children}
</button>
)
}

export default Button
53 changes: 0 additions & 53 deletions app/components/Buttons.tsx

This file was deleted.

72 changes: 72 additions & 0 deletions app/components/ConfirmModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
'use client'

import Button from '@/app/components/Button'
import { Dialog } from '@headlessui/react'
import axios from 'axios'
import { useRouter } from 'next/navigation'
import { useCallback, useState } from 'react'
import { toast } from 'react-hot-toast'
import { FiAlertTriangle } from 'react-icons/fi'
import useConversation from '../hooks/useConversation'
import Modal from './Modal'

interface ConfirmModalProps {
isOpen?: boolean
onClose: () => void
}

const ConfirmModal: React.FC<ConfirmModalProps> = ({ isOpen, onClose }) => {
const router = useRouter()
const { conversationId } = useConversation()
const [isLoading, setIsLoading] = useState(false)

const onDelete = useCallback(() => {
setIsLoading(true)

axios
.delete(`/api/conversations/${conversationId}`)
.then(() => {
onClose()
router.push('/conversations`')
router.refresh()
})
.catch(() => toast.error('Something went wrong!'))
.finally(() => setIsLoading(false))
}, [conversationId, router, onClose])

return (
<Modal isOpen={isOpen} onClose={onClose}>
<div className="sm:flex sm:items-start">
<div
className="mx-auto flex h-12 w-12 flex-shrink-0 items-center
rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10 justify-center"
>
<FiAlertTriangle className="h-6 w-6 text-red-600" />
</div>
<div className="mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left">
<Dialog.Title
as="h3"
className="text-based font-semibold leading-6 text-gray-900"
>
Delete Conversation
</Dialog.Title>
<div className="mt-2">
<p className="text-sm text-gray-500">
Are you sure you want to delete this conversation?
</p>
</div>
</div>
</div>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<Button disabled={isLoading} danger onClick={onDelete}>
Delete
</Button>
<Button disabled={isLoading} secondary onClick={onClose}>
Cancel
</Button>
</div>
</Modal>
)
}

export default ConfirmModal
72 changes: 72 additions & 0 deletions app/components/Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
'use client'

import { Dialog, Transition } from '@headlessui/react'
import { Fragment } from 'react'
import { IoClose } from 'react-icons/io5'

interface ModalProps {
isOpen?: boolean
onClose: () => void
children: React.ReactNode
}

const Modal: React.FC<ModalProps> = ({ isOpen, onClose, children }) => {
return (
<Transition.Root show={isOpen} as={Fragment}>
<Dialog as="div" className="relative z-50" onClose={onClose}>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div
className="fixed inset-0 bg-gray-500 bg-opacity-75
transition-opacity"
/>
</Transition.Child>
<div className="fixed inset-0 z-10 overflow-y-auto">
<div
className="flex min-h-full items-center justify-center p-4
text-center sm:p-0"
>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel
className="relative transform overflow-hidden
rounded-lg bg-white px-4 pb-3 text-left shadow-xsl transition-all
w-full sm:my-8 sm:w-full sm:max-w-lg sm:p-6"
>
<div className="absolute right-0 top-0 hidden pr-4 pt-4 sm:block z-10">
<button
type="button"
className="rounded-md bg-white
text-gray-400 hover:text-gray-500 focus:outline-none
focus:ring-2 focus:ring-sky-500 focus:ring-offset-2"
onClick={onClose}
>
<span className="sr-only">Close</span>
<IoClose className="h-6 w-6" />
</button>
</div>
{children}
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition.Root>
)
}

export default Modal
Loading

0 comments on commit ee82e23

Please sign in to comment.