Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
Implements a new admin “Control Panel” dashboard (with multiple moderation/management tabs), adds a user-facing “Manage Invites” page, and shifts the primary dashboard landing route from /dashboard/search to /dashboard/explore while updating navigation/tests accordingly.
Changes:
- Added admin tabs for user reports, post moderation, featured posts, user management, and statistics (plus related GraphQL/types/UI primitives).
- Added
/dashboard/manage-inviteswith invite status actions. - Replaced
/dashboard/searchwith/dashboard/exploreacross routing, middleware behavior, and tests; removed many/app/test/*pages.
Reviewed changes
Copilot reviewed 88 out of 88 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| quotevote-frontend/src/types/admin.ts | Adds TypeScript types used by new admin/manage-invites features. |
| quotevote-frontend/src/graphql/queries.ts | Adds GraphQL queries for invite requests and user reports. |
| quotevote-frontend/src/graphql/mutations.ts | Adds mutation for updating invite status. |
| quotevote-frontend/src/components/ui/table.tsx | Introduces a table UI primitive used by admin/manage screens. |
| quotevote-frontend/src/components/ui/switch.tsx | Introduces a switch UI primitive for admin user toggles. |
| quotevote-frontend/src/components/ui/button.tsx | Updates button variants/sizes and Radix Slot usage. |
| quotevote-frontend/src/components/ui/alert-dialog.tsx | Adds alert dialog UI primitive used for destructive admin actions. |
| quotevote-frontend/src/components/StripePaymentDialog/index.ts | Exports the new StripePaymentDialog component. |
| quotevote-frontend/src/components/StripePaymentDialog/StripePaymentDialog.tsx | Adds Stripe “Buy Button” dialog and script loader. |
| quotevote-frontend/src/components/Post/PostSkeleton.tsx | Updates post skeleton markup/styling. |
| quotevote-frontend/src/components/Comment/CommentList.tsx | Updates comment list UI incl. empty/loading states. |
| quotevote-frontend/src/components/Comment/CommentInput.tsx | Updates comment input UI and keyboard submit behavior. |
| quotevote-frontend/src/components/Comment/Comment.tsx | Updates comment card layout and action styling. |
| quotevote-frontend/src/components/Admin/index.ts | Exports newly added admin dashboard tabs. |
| quotevote-frontend/src/components/Admin/UserReportsTab.tsx | Adds admin UI for viewing reports and disabling users. |
| quotevote-frontend/src/components/Admin/UserManagementTab.tsx | Adds admin UI to toggle contributor badge. |
| quotevote-frontend/src/components/Admin/StatisticsTab.tsx | Adds admin stats UI derived from invite/user data. |
| quotevote-frontend/src/components/Admin/PostModerationTab.tsx | Adds admin UI for approving/rejecting posts. |
| quotevote-frontend/src/components/Admin/FeaturedPostsTab.tsx | Adds admin UI to assign/remove featured slots. |
| quotevote-frontend/src/components/Admin/BotListTab.tsx | Adds toast-based success/error feedback for bot actions. |
| quotevote-frontend/src/app/test/user-posts/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/submit-post/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/subheader/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/signup/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/sidebar/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/reset/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/request-invite-dialog/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/request-access/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/quotes/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/profile/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/popover-menu/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/navigation-flow/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/login/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/icons/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/highlight-text/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/error-boundary/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/content-list/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/common/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/comments/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/carousel/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/buttons/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/buddy-list/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/auth-flow/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/admin/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test/activity/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/test-postchat/page.tsx | Removes test-only page. |
| quotevote-frontend/src/app/error/PageContent.tsx | Updates “back” navigation target to /dashboard/explore. |
| quotevote-frontend/src/app/dashboard/settings/page.tsx | Adds Settings page wrapper + metadata (client page split). |
| quotevote-frontend/src/app/dashboard/search/page.tsx | Removes old Search page route (replaced by Explore). |
| quotevote-frontend/src/app/dashboard/search/loading.tsx | Removes old Search loading UI (replaced by Explore). |
| quotevote-frontend/src/app/dashboard/post/[group]/[title]/[postId]/page.tsx | Updates post detail layout + tabs styling and sidebar behavior. |
| quotevote-frontend/src/app/dashboard/manage-invites/page.tsx | Adds Manage Invites route wrapper + metadata. |
| quotevote-frontend/src/app/dashboard/manage-invites/ManageInvitesClient.tsx | Adds invite management UI and actions. |
| quotevote-frontend/src/app/dashboard/layout.tsx | Updates nav to Explore, adds Manage Invites, uses removeToken on logout. |
| quotevote-frontend/src/app/dashboard/explore/page.tsx | Adds new Explore page route + Suspense skeleton. |
| quotevote-frontend/src/app/dashboard/explore/loading.tsx | Adds Explore route loading UI. |
| quotevote-frontend/src/app/dashboard/control-panel/page.tsx | Converts Control Panel page to client wrapper. |
| quotevote-frontend/src/app/dashboard/control-panel/ControlPanelClient.tsx | Adds admin-only tabbed control panel client implementation. |
| quotevote-frontend/src/app/components/LandingPage/LandingPageContent.tsx | Redirects signed-in users to /dashboard/explore. |
| quotevote-frontend/src/app/auths/signup/PageContent.tsx | Redirects after signup to /dashboard/explore. |
| quotevote-frontend/src/app/auths/login/PageContent.tsx | Redirects after login to /dashboard/explore. |
| quotevote-frontend/src/app/auths/investor-thanks/page.tsx | Updates metadata/content copy and layout sizing. |
| quotevote-frontend/src/tests/foundation/middleware.test.ts | Updates middleware tests for Explore route. |
| quotevote-frontend/src/tests/components/StripePaymentDialog/StripePaymentDialog.test.tsx | Adds tests for StripePaymentDialog. |
| quotevote-frontend/src/tests/components/Post/PostSkeleton.test.tsx | Updates tests to match new skeleton markup. |
| quotevote-frontend/src/tests/components/Admin/UserReportsTab.test.tsx | Adds tests for UserReportsTab. |
| quotevote-frontend/src/tests/components/Admin/PostModerationTab.test.tsx | Adds tests for PostModerationTab. |
| quotevote-frontend/src/tests/components/Admin/BotListTab.test.tsx | Adds tests for BotListTab. |
| quotevote-frontend/src/tests/app/page.test.tsx | Updates landing page redirect expectations to Explore route. |
| quotevote-frontend/src/tests/app/dashboard/settings/page.test.tsx | Adds tests covering settings page behavior. |
| quotevote-frontend/src/tests/app/dashboard/search/page.test.tsx | Refactors test to validate Explore page instead of Search. |
| quotevote-frontend/src/tests/app/dashboard/manage-invites/page.test.tsx | Adds tests for Manage Invites page client. |
| quotevote-frontend/src/tests/app/dashboard/explore/page.test.tsx | Adds tests for Explore page wrapper. |
| quotevote-frontend/src/tests/app/dashboard/control-panel/page.test.tsx | Updates control panel tests for new client implementation + admin gating. |
| quotevote-frontend/src/tests/app/auths/login.test.tsx | Updates login redirect expectation to Explore route. |
| quotevote-frontend/public/sitemap.xml | Adds a static sitemap. |
| quotevote-frontend/public/robots.txt | Adds robots directives and sitemap link. |
| quotevote-frontend/package.json | Adjusts dev scripts (webpack default + turbopack alt). |
| quotevote-frontend/next.config.ts | Updates CSP connect/font sources (and still defines CSP). |
| quotevote-frontend/middleware.ts | Adds admin-only middleware gating for control panel + Explore redirect updates. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const parts = token.split('.'); | ||
| if (parts.length !== 3) return null; | ||
| const payload = parts[1]; | ||
| const decoded = atob(payload.replace(/-/g, '+').replace(/_/g, '/')); |
There was a problem hiding this comment.
decodeJwtPayload base64url-decodes the JWT payload without adding required padding. For many JWTs this will throw and silently return null. Add proper base64url padding before atob (or use a small base64url decode helper) so admin routing works reliably.
| const decoded = atob(payload.replace(/-/g, '+').replace(/_/g, '/')); | |
| // Convert from base64url to base64 and add required padding | |
| let base64 = payload.replace(/-/g, '+').replace(/_/g, '/'); | |
| const paddingNeeded = base64.length % 4; | |
| if (paddingNeeded > 0) { | |
| base64 = base64 + '='.repeat(4 - paddingNeeded); | |
| } | |
| const decoded = atob(base64); |
| // Admin-only route protection for /dashboard/control-panel | ||
| if (pathname.startsWith('/dashboard/control-panel')) { | ||
| const payload = decodeJwtPayload(token); | ||
| if (!payload || payload.admin !== true) { | ||
| return NextResponse.redirect(new URL('/dashboard/explore', request.url)); | ||
| } |
There was a problem hiding this comment.
Route protection for /dashboard/control-panel relies on an unverified JWT payload (decodeJwtPayload explicitly does not verify signatures). A user can forge a token with { admin: true } and bypass this middleware check. Prefer verifying the JWT signature (e.g. with jose in the edge runtime) or enforce admin access exclusively via server-side authorization and redirect based on a trusted backend check.
| <AlertDialogAction | ||
| onClick={() => | ||
| handleDisable( | ||
| report.reportedUser?._id, | ||
| report.reportedUser?.username | ||
| ) | ||
| } |
There was a problem hiding this comment.
handleDisable expects (userId: string, username: string), but the click handler passes report.reportedUser?._id / report.reportedUser?.username, which are typed as string | undefined due to optional chaining. With strict enabled this should be a type error (and at runtime could toast “@undefined”). Since reportedUser is non-optional in UserReport, use non-optional access (report.reportedUser._id, report.reportedUser.username) or guard explicitly before rendering the action.
| const handleSave = async (id: string) => { | ||
| const slot = selection[id] | ||
| try { | ||
| await updateSlot({ | ||
| variables: { postId: id, featuredSlot: slot ? Number(slot) : null }, | ||
| }) | ||
| toast.success(slot ? `Post assigned to slot ${slot}` : 'Post removed from featured') |
There was a problem hiding this comment.
SelectItem uses value="none", but handleSave treats any truthy slot as a number (Number(slot)), so choosing “None” will send featuredSlot: NaN to the GraphQL mutation (expected Int). Treat the “none” selection as null (e.g., normalize slot === 'none' || slot === '' to null) and ensure the Select’s unselected value matches that normalization.
| const STRIPE_BUY_BUTTON_ID = 'buy_btn_1RY6bhP3PjIJfZEbu5CpTDjo' | ||
| const STRIPE_PUBLISHABLE_KEY = | ||
| 'pk_live_51RXriSP3PjIJfZEb1tqnEGBOGFZBHREUxqWHeO22GASJ5It6MKfpakOE3oDtL7II20j5idUR6NuXrBlaKXvszY6q00nn8KxROy' |
There was a problem hiding this comment.
This component hardcodes a live Stripe publishable key and buy button ID in the client bundle. Move these to environment variables (e.g. NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY / NEXT_PUBLIC_STRIPE_BUY_BUTTON_ID) so they can be rotated and so non-production deployments don’t accidentally point at live Stripe configuration.
| const existing = document.querySelector('script[src*="buy-button.js"]') | ||
| if (existing) { | ||
| stripeLoaded = true | ||
| listeners.forEach((cb) => cb()) | ||
| return |
There was a problem hiding this comment.
ensureStripeScript marks Stripe as loaded if a matching <script> tag exists, even if it hasn’t finished loading yet. That can cause StripeBuyButton to render before the custom element is registered. Consider tracking a separate “script tag present” vs “loaded” state, or checking customElements.get('stripe-buy-button'), or attaching an onload handler to the existing script instead of immediately setting stripeLoaded = true.
| "default-src 'self'", | ||
| "script-src 'self' 'unsafe-eval' 'unsafe-inline'", | ||
| "style-src 'self' 'unsafe-inline'", | ||
| "img-src 'self' data: https:", | ||
| "font-src 'self' data:", | ||
| "connect-src 'self' http://localhost:4000 ws://localhost:4000", | ||
| "font-src 'self' data: https://fonts.gstatic.com", | ||
| "connect-src 'self' http://localhost:4000 ws://localhost:4000 https://fonts.googleapis.com https://fonts.gstatic.com", | ||
| "frame-ancestors 'none'", | ||
| ].join("; "), |
There was a problem hiding this comment.
CSP currently restricts script-src to 'self' (plus unsafe-*). The Stripe Buy Button script is loaded from https://js.stripe.com/..., which will be blocked in production. Update CSP to allow https://js.stripe.com in script-src and add appropriate frame-src/connect-src entries for Stripe checkout endpoints used by the buy button.
implemented admin dashboards and fixed settings functionality.