Skip to content

Commit

Permalink
feat(fe): show update information modal for existing users (#2006)
Browse files Browse the repository at this point in the history
  • Loading branch information
jwoojin9 authored Aug 26, 2024
1 parent ba4c760 commit 1988ba9
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 67 deletions.
126 changes: 80 additions & 46 deletions apps/frontend/components/auth/HeaderAuthPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@ import {
DropdownMenuItem,
DropdownMenuSeparator
} from '@/components/ui/dropdown-menu'
import { cn } from '@/lib/utils'
import { cn, fetcherWithAuth } from '@/lib/utils'
import useAuthModalStore from '@/stores/authModal'
import { LogOut, UserRoundCog, ChevronDown } from 'lucide-react'
import type { Session } from 'next-auth'
import { signOut } from 'next-auth/react'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { useEffect, useState } from 'react'
import { BiSolidUser } from 'react-icons/bi'
import { RxHamburgerMenu } from 'react-icons/rx'
import AuthModal from './AuthModal'
import UpdateInformation from './UpdateInformation'

interface HeaderAuthPanelProps {
session: Session | null
Expand All @@ -38,38 +41,73 @@ export default function HeaderAuthPanel({
)
const isUser = session?.user.role === 'User'
const isEditor = group === 'editor'
const [needsUpdate, setNeedsUpdate] = useState(false)
const pathname = usePathname()

useEffect(() => {
const checkIfNeedsUpdate = async () => {
const userResponse = await fetcherWithAuth.get('user')
const user: { studentId: string; major: string } =
await userResponse.json()
const updateNeeded =
user.studentId === '0000000000' ||
user.major === 'Department Information Unavailable / 학과 정보 없음'

setNeedsUpdate(updateNeeded)
}
if (session) {
checkIfNeedsUpdate()
}
}, [session])

const shouldShowDialog =
needsUpdate && pathname.split('/').pop() !== 'settings'

return (
<div className="ml-2 flex items-center gap-2">
{session ? (
<DropdownMenu>
<DropdownMenuTrigger
className={cn(
'hidden items-center gap-2 rounded-md px-4 py-1 md:flex',
isEditor ? 'border-0 ring-offset-0' : 'bg-primary text-white'
)}
>
<BiSolidUser
<>
<DropdownMenu>
<DropdownMenuTrigger
className={cn(
'h-4 w-4',
isEditor ? 'size-6 rounded-none text-gray-300' : 'text-white'
'hidden items-center gap-2 rounded-md px-4 py-1 md:flex',
isEditor ? 'border-0 ring-offset-0' : 'bg-primary text-white'
)}
/>
{!isEditor && (
<p className="font-semibold text-white">
{session?.user.username}
</p>
)}
<ChevronDown className="w-4" />
</DropdownMenuTrigger>
<DropdownMenuContent
className={cn(
isEditor &&
'mr-5 rounded-sm border-none bg-[#4C5565] px-0 font-normal text-white'
)}
>
{!isUser && (
<Link href="/admin">
>
<BiSolidUser
className={cn(
'h-4 w-4',
isEditor ? 'size-6 rounded-none text-gray-300' : 'text-white'
)}
/>
{!isEditor && (
<p className="font-semibold text-white">
{session?.user.username}
</p>
)}
<ChevronDown className="w-4" />
</DropdownMenuTrigger>
<DropdownMenuContent
className={cn(
isEditor &&
'mr-5 rounded-sm border-none bg-[#4C5565] px-0 font-normal text-white'
)}
>
{!isUser && (
<Link href="/admin">
<DropdownMenuItem
className={cn(
'flex cursor-pointer items-center gap-1',
isEditor
? 'rounded-none text-white focus:bg-[#222939] focus:text-white'
: 'font-semibold'
)}
>
<UserRoundCog className="size-4" /> Management
</DropdownMenuItem>
</Link>
)}
<Link href="/settings">
<DropdownMenuItem
className={cn(
'flex cursor-pointer items-center gap-1',
Expand All @@ -78,37 +116,33 @@ export default function HeaderAuthPanel({
: 'font-semibold'
)}
>
<UserRoundCog className="size-4" /> Management
<UserRoundCog className="size-4" /> Settings
</DropdownMenuItem>
</Link>
)}
<Link href="/settings">
<DropdownMenuItem
className={cn(
'flex cursor-pointer items-center gap-1',
isEditor
? 'rounded-none text-white focus:bg-[#222939] focus:text-white'
: 'font-semibold'
)}
onClick={() => {
signOut()
}}
>
<UserRoundCog className="size-4" /> Settings
<LogOut className="size-4" /> LogOut
</DropdownMenuItem>
</Link>
<DropdownMenuItem
className={cn(
'flex cursor-pointer items-center gap-1',
isEditor
? 'rounded-none text-white focus:bg-[#222939] focus:text-white'
: 'font-semibold'
)}
onClick={() => {
signOut()
}}
</DropdownMenuContent>
</DropdownMenu>
<Dialog open={shouldShowDialog}>
<DialogContent
className="min-h-[30rem] max-w-[20.5rem]"
hideCloseButton={true}
>
<LogOut className="size-4" /> LogOut
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<UpdateInformation />
</DialogContent>
</Dialog>
</>
) : (
<Dialog open={currentModal !== ''} onOpenChange={hideModal}>
<DialogTrigger asChild>
Expand Down
47 changes: 47 additions & 0 deletions apps/frontend/components/auth/UpdateInformation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use client'

import { Button } from '@/components/ui/button'
import CodedangLogo from '@/public/codedang.svg'
import Image from 'next/image'
import { useRouter } from 'next/navigation'
import { IoWarningOutline } from 'react-icons/io5'

export default function UpdateInformation() {
const router = useRouter()
return (
<div className="flex w-full flex-col items-center gap-1">
<div className="flex justify-center pt-4">
<Image
className="absolute top-4"
src={CodedangLogo}
alt="codedang"
width={100}
/>
</div>
<div className="mt-12 inline-flex items-center text-center font-mono text-xl font-bold text-red-500">
<IoWarningOutline />
<p className="pl-2">Update Information</p>
</div>
<div className="mt-5 text-center text-xs text-neutral-700">
<p>To continue using the service,</p>
<p>you must update your information.</p>
</div>
<div className="text-center text-xs text-neutral-700">
<p>Your account will be DELETED if you do not </p>
<p>update your information by August 31.</p>
</div>
<div className="my-8 w-36 border-b" />
<p className="text-xs font-medium">We need the information</p>
<ul className="mt-1 text-xs font-medium text-neutral-700">
<li>Your Student ID</li>
<li>Your First Major</li>
</ul>
<Button
className="mt-10 w-full bg-red-500 font-semibold hover:bg-red-600"
onClick={() => router.push('/settings')}
>
Update Now
</Button>
</div>
)
}
50 changes: 29 additions & 21 deletions apps/frontend/components/ui/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type DialogOverlayProps = React.ComponentPropsWithoutRef<
interface ExtendedDialogContentProps
extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> {
showDarkOverlay?: boolean
hideCloseButton?: boolean
}

const Dialog = DialogPrimitive.Root
Expand Down Expand Up @@ -47,28 +48,35 @@ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
ExtendedDialogContentProps
>(({ className, children, showDarkOverlay, ...props }, ref) => (
<DialogPortal>
<DialogOverlay showDarkOverlay={showDarkOverlay} />
<DialogPrimitive.Content
ref={ref}
className={cn(
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-gray-200 bg-white p-6 shadow-lg duration-200 sm:rounded-lg dark:border-gray-800 dark:bg-gray-950',
className
)}
{...props}
>
{children}
<DialogPrimitive.Close
id="closeDialog"
className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-gray-950 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-gray-100 data-[state=open]:text-gray-500 dark:ring-offset-gray-950 dark:focus:ring-gray-300 dark:data-[state=open]:bg-gray-800 dark:data-[state=open]:text-gray-400"
>(
(
{ className, children, showDarkOverlay, hideCloseButton = false, ...props },
ref
) => (
<DialogPortal>
<DialogOverlay showDarkOverlay={showDarkOverlay} />
<DialogPrimitive.Content
ref={ref}
className={cn(
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-gray-200 bg-white p-6 shadow-lg duration-200 sm:rounded-lg dark:border-gray-800 dark:bg-gray-950',
className
)}
{...props}
>
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
))
{children}
{!hideCloseButton && (
<DialogPrimitive.Close
id="closeDialog"
className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-gray-950 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-gray-100 data-[state=open]:text-gray-500 dark:ring-offset-gray-950 dark:focus:ring-gray-300 dark:data-[state=open]:bg-gray-800 dark:data-[state=open]:text-gray-400"
>
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
)}
</DialogPrimitive.Content>
</DialogPortal>
)
)
DialogContent.displayName = DialogPrimitive.Content.displayName

const DialogHeader = ({
Expand Down

0 comments on commit 1988ba9

Please sign in to comment.