Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions quotevote-backend/app/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ async function startServer() {
issuer: String
message: String
}

scalar JSON

type PortableState {
Expand All @@ -85,7 +85,7 @@ async function startServer() {
version: String
collections: [JSON]
}

input ActivityEventInput {
type: String!
payload: JSON!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,22 +255,6 @@ describe('SettingsContent', () => {
expect(screen.getByText('Save')).toBeInTheDocument()
})

it('renders Manage Invites button for admin users', () => {
mockUseAppStore.mockImplementation((selector: (state: Record<string, unknown>) => unknown) => {
const state = {
user: {
data: { ...mockUserData, admin: true },
loading: false,
},
setUserData: jest.fn(),
logout: jest.fn(),
}
return selector(state)
})

renderComponent()
expect(screen.getByText('Manage Invites')).toBeInTheDocument()
})

it('renders Forgot Password link', () => {
renderComponent()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { useState, useCallback } from 'react'
import { useRouter, useSearchParams } from 'next/navigation'
import Image from 'next/image'
import { Dialog, DialogContent } from '@/components/ui/dialog'
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog'
import PaginatedPostsList from '@/components/Post/PaginatedPostsList'
import { useAppStore } from '@/store'
import SearchGuestSections from '@/components/SearchContainer/SearchGuestSections'
Expand Down Expand Up @@ -224,6 +224,7 @@ export default function ExploreContent() {
{/* Create Quote Dialog */}
<Dialog open={submitDialogOpen} onOpenChange={setSubmitDialogOpen}>
<DialogContent className="max-w-md p-0" showCloseButton={false}>
<DialogTitle className="sr-only">Create Quote</DialogTitle>
<SubmitPost setOpen={setSubmitDialogOpen} />
</DialogContent>
</Dialog>
Expand Down
14 changes: 2 additions & 12 deletions quotevote-frontend/src/app/dashboard/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
User,
Settings2,
ShieldCheck,
Mail,
LogOut,
ChevronDown,
} from 'lucide-react';
Expand Down Expand Up @@ -42,7 +41,7 @@ import {
DropdownMenuItem,
DropdownMenuSeparator,
} from '@/components/ui/dropdown-menu';
import { Dialog, DialogContent } from '@/components/ui/dialog';
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog';
import { SubmitPost } from '@/components/SubmitPost/SubmitPost';

/* ------------------------------------------------------------------ */
Expand All @@ -53,7 +52,6 @@ const NAV_PAGES = [
{ path: '/dashboard/profile', page: 'profile' },
{ path: '/dashboard/notifications', page: 'notifications' },
{ path: '/dashboard/settings', page: 'settings' },
{ path: '/dashboard/manage-invites', page: 'manage-invites' },
{ path: '/dashboard/control-panel', page: 'control-panel' },
] as const;
Comment on lines 52 to 56
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change removes “/dashboard/manage-invites” from the dashboard nav pages, which is a user-facing behavior change but isn’t mentioned in the PR description. Please either document this in the PR (and any related migration notes) or confirm it’s intentional and ensure there’s still a supported way for admins to reach invite management if that feature remains.

Copilot uses AI. Check for mistakes.

Expand Down Expand Up @@ -321,15 +319,6 @@ export default function DashboardLayout({
<p className="text-[11px] text-muted-foreground">Manage your account</p>
</div>
</DropdownMenuItem>
<DropdownMenuItem onClick={() => router.push('/dashboard/manage-invites')} className="cursor-pointer rounded-lg gap-3 py-2.5 px-3">
<div className="flex items-center justify-center w-8 h-8 rounded-full bg-muted">
<Mail className="size-4 text-muted-foreground" />
</div>
<div>
<p className="text-[13px] font-semibold">Manage Invites</p>
<p className="text-[11px] text-muted-foreground">Invite friends to join</p>
</div>
</DropdownMenuItem>
{isAdmin && (
<DropdownMenuItem onClick={() => router.push('/dashboard/control-panel')} className="cursor-pointer rounded-lg gap-3 py-2.5 px-3">
<div className="flex items-center justify-center w-8 h-8 rounded-full bg-[#e8f5ee]">
Expand Down Expand Up @@ -514,6 +503,7 @@ export default function DashboardLayout({

<Dialog open={submitDialogOpen} onOpenChange={setSubmitDialogOpen}>
<DialogContent className="max-w-md p-0" showCloseButton={false}>
<DialogTitle className="sr-only">Create Quote</DialogTitle>
<SubmitPost setOpen={setSubmitDialogOpen} />
</DialogContent>
</Dialog>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Skeleton } from '@/components/ui/skeleton';
import type { Notification } from '@/types/notification';

export function NotificationsPageContent() {
const userId = useAppStore((state) => state.user.data.id);
const userId = useAppStore((state) => (state.user.data._id || state.user.data.id) as string | undefined);

const { loading, data, refetch, error } = useQuery(GET_NOTIFICATIONS, {
skip: !userId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ export default function SettingsPageClient() {
const email = userData?.email ?? ''
const name = userData?.name ?? ''
const userId = userData?.id ?? userData?._id ?? ''
const isAdmin = Boolean(userData?.admin)

const [localDarkMode, setLocalDarkMode] = useState(isDarkMode)
const [originalDarkMode, setOriginalDarkMode] = useState(isDarkMode)
Expand Down Expand Up @@ -100,20 +99,22 @@ export default function SettingsPageClient() {

try {
const result = await updateUser({ variables: { user: userInput } })
if (result.data?.updateUser) {
const avatarValue =
typeof userData?.avatar === 'string'
? userData.avatar
: (userData?.avatar as { url?: string } | undefined)?.url
const updated = result.data?.updateUser
if (updated) {
setUserData({
...userData,
avatar: avatarValue,
...otherValues,
...updated,
avatar: userData?.avatar as string | undefined,
themePreference: localDarkMode ? 'dark' : 'light',
})
setOriginalDarkMode(localDarkMode)
toast.success('Settings saved successfully')
form.reset({ ...otherValues, password: '' })
form.reset({
name: updated.name ?? otherValues.name,
username: updated.username ?? otherValues.username,
email: updated.email ?? otherValues.email,
password: '',
})
}
} catch (err) {
const message = err instanceof Error ? err.message : 'Failed to save settings'
Expand Down Expand Up @@ -289,16 +290,7 @@ export default function SettingsPageClient() {
<LogOut className="size-4" />
Sign Out
</Button>
{isAdmin && (
<Button
type="button"
variant="ghost"
onClick={() => router.push('/dashboard/invites')}
className="text-muted-foreground"
>
Manage Invites
</Button>
)}

<div className="ml-auto">
<Button type="submit" disabled={!isFormDirty || loading}>
{loading ? (
Expand Down
3 changes: 2 additions & 1 deletion quotevote-frontend/src/components/Navbars/MainNavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
SheetHeader,
SheetTitle,
} from '@/components/ui/sheet';
import { Dialog, DialogContent } from '@/components/ui/dialog';
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog';
import { DisplayAvatar } from '@/components/DisplayAvatar';
import { NotificationMenu } from '@/components/Notifications/NotificationMenu';
import ChatMenu from '@/components/Chat/ChatMenu';
Expand Down Expand Up @@ -337,6 +337,7 @@ export function MainNavBar({}: MainNavBarProps) {
{/* Create Quote Dialog */}
<Dialog open={submitDialogOpen} onOpenChange={setSubmitDialogOpen}>
<DialogContent className={isMediumScreen ? 'max-w-md' : 'max-w-full h-full'} showCloseButton={false}>
<DialogTitle className="sr-only">Create Quote</DialogTitle>
<SubmitPost setOpen={setSubmitDialogOpen} />
</DialogContent>
</Dialog>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function Notification({
if (setOpenPopUp) {
setOpenPopUp();
}
router.push('/Notifications');
router.push('/dashboard/notifications');
};

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useMutation, useApolloClient } from '@apollo/client/react';
import moment from 'moment';
import { X, UserPlus, ArrowUp, ArrowDown, MessageSquare, Quote } from 'lucide-react';
import { Button } from '@/components/ui/button';
import Avatar from '@/components/Avatar';
import { DisplayAvatar } from '@/components/DisplayAvatar';
import { DELETE_NOTIFICATION } from '@/graphql/mutations';
import { GET_NOTIFICATIONS } from '@/graphql/queries';
import { useAppStore } from '@/store';
Expand Down Expand Up @@ -164,12 +164,6 @@ export function NotificationLists({ notifications, pageView = false }: Notificat
>
<div className="space-y-2">
{notifications.map((notification) => {
const avatarUrl =
notification.userBy.avatar?.url ||
(typeof notification.userBy.avatar === 'string'
? notification.userBy.avatar
: undefined);

const actionText = getNotificationActionText(notification.notificationType);
const icon = getNotificationIcon(notification.notificationType);
const displayName = notification.userBy.name || notification.userBy.username;
Expand Down Expand Up @@ -200,10 +194,10 @@ export function NotificationLists({ notifications, pageView = false }: Notificat
>
<div className="flex items-start gap-3">
<div className="relative flex-shrink-0">
<Avatar
src={avatarUrl}
alt={displayName}
size="md"
<DisplayAvatar
avatar={notification.userBy.avatar as string | Record<string, unknown> | undefined}
username={notification.userBy.username}
size={40}
/>
{icon && (
<div className="absolute -bottom-1.5 -right-1.5">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function NotificationMenu({ fontSize = 'small' }: NotificationMenuProps)
const { isMobile } = useResponsive();
const [open, setOpen] = useState(false);
const [isHovered, setIsHovered] = useState(false);
const userId = useAppStore((state) => state.user.data.id);
const userId = useAppStore((state) => (state.user.data._id || state.user.data.id) as string | undefined);

const { loading, data, refetch, error } = useQuery(GET_NOTIFICATIONS, {
skip: !userId,
Expand Down Expand Up @@ -68,12 +68,14 @@ export function NotificationMenu({ fontSize = 'small' }: NotificationMenuProps)
spacing={0}
>
<div className="relative">
<Badge
variant="destructive"
className="absolute -top-1 -right-1 z-10 min-w-[20px] h-5 flex items-center justify-center px-1"
>
{notifications.length > 0 ? notifications.length : null}
</Badge>
{notifications.length > 0 && (
<Badge
variant="destructive"
className="absolute -top-1 -right-1 z-10 min-w-[20px] h-5 flex items-center justify-center px-1"
>
{notifications.length}
</Badge>
)}
<Button
variant="ghost"
size="sm"
Expand Down Expand Up @@ -107,12 +109,14 @@ export function NotificationMenu({ fontSize = 'small' }: NotificationMenuProps)
const mobileContent = (
<>
<div className="relative">
<Badge
variant="destructive"
className="absolute -top-1 -right-1 z-10 min-w-[20px] h-5 flex items-center justify-center px-1"
>
{notifications.length > 0 ? notifications.length : null}
</Badge>
{notifications.length > 0 && (
<Badge
variant="destructive"
className="absolute -top-1 -right-1 z-10 min-w-[20px] h-5 flex items-center justify-center px-1"
>
{notifications.length}
</Badge>
)}
<Button
variant="ghost"
size="sm"
Expand Down
3 changes: 2 additions & 1 deletion quotevote-frontend/src/components/Sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
Sheet,
SheetContent,
} from '@/components/ui/sheet';
import { Dialog, DialogContent } from '@/components/ui/dialog';
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog';
import { DisplayAvatar } from '@/components/DisplayAvatar';
import { NotificationMenu } from '@/components/Notifications/NotificationMenu';
import ChatMenu from '@/components/Chat/ChatMenu';
Expand Down Expand Up @@ -366,6 +366,7 @@ export function Sidebar({
{/* Create Quote Dialog */}
<Dialog open={openCreateQuote} onOpenChange={setOpenCreateQuote}>
<DialogContent className="max-w-md p-0" showCloseButton={false}>
<DialogTitle className="sr-only">Create Quote</DialogTitle>
<SubmitPost setOpen={setOpenCreateQuote} />
</DialogContent>
</Dialog>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { useState, useRef, useEffect } from 'react'
import { useRouter } from 'next/navigation'
import { toast } from 'sonner'
import { useForm, Controller } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { useMutation, useApolloClient } from '@apollo/client/react'
Expand Down Expand Up @@ -136,13 +137,14 @@ export function SubmitPostForm({ options = [], user, setOpen }: SubmitPostFormPr
},
})

const { _id } =
(submitResult.data as { addPost?: { _id: string; url: string } })?.addPost || {}
const addPostResult = (submitResult.data as { addPost?: { _id: string; url: string } })?.addPost
const { _id } = addPostResult || {}
if (_id) {
setSelectedPost(_id)
apolloClient.cache.evict({ fieldName: 'posts' })
apolloClient.cache.gc()
setOpen(false)
toast.success('Post created!', { description: 'Your quote has been published.' })
router.push('/dashboard/explore')
}
} catch (err) {
Expand All @@ -157,12 +159,13 @@ export function SubmitPostForm({ options = [], user, setOpen }: SubmitPostFormPr

const hideAlert = () => {
setShowAlert(false)
setError(null)
reset()
}

return (
<>
{showAlert && (
{showAlert && error && (
<SubmitPostAlert
hideAlert={hideAlert}
shareableLink=""
Expand Down
14 changes: 0 additions & 14 deletions quotevote-frontend/src/components/settings/SettingsContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ export default function SettingsContent({ setOpen }: SettingsContentProps) {
const email = userData?.email ?? ''
const name = userData?.name ?? ''
const userId = userData?.id ?? userData?._id ?? ''
const admin = userData?.admin ?? false

const [updateUser, { loading, error }] = useMutation<UpdateUserResponse>(UPDATE_USER)

Expand Down Expand Up @@ -267,19 +266,6 @@ export default function SettingsContent({ setOpen }: SettingsContentProps) {
Sign Out
</Button>

{admin && (
<Button
type="button"
variant="secondary"
onClick={() => {
router.push('/dashboard/control-panel')
if (setOpen) setOpen(false)
}}
disabled={loading}
>
Manage Invites
</Button>
)}

<Button type="submit" disabled={!form.formState.isDirty || loading}>
{loading ? (
Expand Down
Loading
Loading