Skip to content
Open
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
42 changes: 20 additions & 22 deletions app/projects/[id]/editor/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -927,15 +927,26 @@ export default function EditorPage() {
</Button>
)}

{/* Suggestions link */}
{isSuggestionMode && (
<Link href={`/projects/${projectId}/suggestions`}>
<Button variant="ghost" size="sm" className="gap-2 text-amber-600 dark:text-amber-400">
<Lightbulb className="h-4 w-4" />
<span className="hidden sm:inline">My Suggestions</span>
</Button>
</Link>
)}
{/* Suggestions / PRs link — unified, mode-aware */}
<Link href={`/projects/${projectId}/pull-requests`}>
<Button
variant="ghost"
size="sm"
className={editorMode === "standard" ? "gap-2 text-amber-600 dark:text-amber-400" : "gap-2"}
>
{editorMode === "standard"
? <Lightbulb className="h-4 w-4" />
: <GitPullRequest className="h-4 w-4" />}
<span className="hidden sm:inline">
{editorMode === "standard" ? "My Suggestions" : "My Pull Requests"}
</span>
{editorMode === "developer" && openPRCount > 0 && (
<span className="rounded-full bg-primary-100 px-1.5 py-0.5 text-xs font-medium text-primary-700 dark:bg-primary-900/30 dark:text-primary-400">
{openPRCount}
</span>
)}
</Button>
</Link>

{/* Review Suggestions link (editors/admins only) */}
{canEdit && pendingSuggestionCount > 0 && (
Expand Down Expand Up @@ -1022,19 +1033,6 @@ export default function EditorPage() {
)}
</Button>

{/* PR Link */}
<Link href={`/projects/${projectId}/pull-requests`}>
<Button variant="ghost" size="sm" className="gap-2">
<GitPullRequest className="h-4 w-4" />
<span className="hidden sm:inline">PRs</span>
{openPRCount > 0 && (
<span className="rounded-full bg-primary-100 px-1.5 py-0.5 text-xs font-medium text-primary-700 dark:bg-primary-900/30 dark:text-primary-400">
{openPRCount}
</span>
)}
</Button>
</Link>

<Button
variant="ghost"
size="sm"
Expand Down
6 changes: 5 additions & 1 deletion app/projects/[id]/pull-requests/[prNumber]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Header } from "@/components/layout/header";
import { Button } from "@/components/ui/button";
import { PRDetail } from "@/components/pr/PRDetail";
import { projectApi, type Project } from "@/lib/api/projects";
import { useEditorModeStore } from "@/lib/stores/editorModeStore";

export default function PullRequestDetailPage() {
const { data: session, status } = useSession();
Expand All @@ -19,6 +20,8 @@ export default function PullRequestDetailPage() {
const [project, setProject] = useState<Project | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const editorMode = useEditorModeStore((s) => s.editorMode);
const isSuggestionMode = editorMode === "standard";

useEffect(() => {
const fetchProject = async () => {
Expand Down Expand Up @@ -99,7 +102,7 @@ export default function PullRequestDetailPage() {
className="flex items-center gap-1 hover:text-slate-900 dark:hover:text-slate-200"
>
<ArrowLeft className="h-4 w-4" />
Pull Requests
{isSuggestionMode ? "My Suggestions" : "Pull Requests"}
</Link>
</div>

Expand All @@ -111,6 +114,7 @@ export default function PullRequestDetailPage() {
accessToken={session?.accessToken}
userRole={project.user_role}
currentUserId={session?.user?.id}
mode={editorMode}
/>
</div>
</div>
Expand Down
19 changes: 13 additions & 6 deletions app/projects/[id]/pull-requests/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import { useState } from "react";
import { useSession } from "next-auth/react";
import { useParams, useRouter } from "next/navigation";
import Link from "next/link";
import { ArrowLeft, Plus, GitPullRequest } from "lucide-react";
import { ArrowLeft, Plus, GitPullRequest, Lightbulb } from "lucide-react";
import { Header } from "@/components/layout/header";
import { Button } from "@/components/ui/button";
import { PRList } from "@/components/pr/PRList";
import { PRCreateModal } from "@/components/pr/PRCreateModal";
import { BranchProvider } from "@/lib/context/BranchContext";
import { useProject, derivePermissions } from "@/lib/hooks/useProject";
import { useProjectHomeHref } from "@/lib/hooks/useProjectHomeHref";
import { useEditorModeStore } from "@/lib/stores/editorModeStore";

export default function PullRequestsPage() {
const { data: session, status } = useSession();
Expand All @@ -23,6 +24,8 @@ export default function PullRequestsPage() {
const { canEdit: canCreatePR } = derivePermissions(project, session?.accessToken);
const projectHomeHref = useProjectHomeHref(projectId);
const [showCreateModal, setShowCreateModal] = useState(false);
const editorMode = useEditorModeStore((s) => s.editorMode);
const isSuggestionMode = editorMode === "standard";

if (isLoading || status === "loading") {
return (
Expand Down Expand Up @@ -86,27 +89,31 @@ export default function PullRequestsPage() {
{/* Header */}
<div className="mb-8 flex items-center justify-between">
<div className="flex items-center gap-3">
<GitPullRequest className="h-8 w-8 text-slate-600 dark:text-slate-400" />
{isSuggestionMode
? <Lightbulb className="h-8 w-8 text-amber-500 dark:text-amber-400" />
: <GitPullRequest className="h-8 w-8 text-slate-600 dark:text-slate-400" />}
<div>
<h1 className="text-2xl font-semibold text-slate-900 dark:text-white">
Pull Requests
{isSuggestionMode ? "My Suggestions" : "Pull Requests"}
</h1>
<p className="text-sm text-slate-500">
Review and manage proposed changes
{isSuggestionMode
? "Track the status of your proposed changes"
: "Review and manage proposed changes"}
</p>
</div>
</div>

{canCreatePR && (
<Button onClick={() => setShowCreateModal(true)} className="gap-2">
<Plus className="h-4 w-4" />
New Pull Request
{isSuggestionMode ? "New Suggestion" : "New Pull Request"}
</Button>
)}
</div>

{/* PR List */}
<PRList projectId={projectId} accessToken={session?.accessToken} />
<PRList projectId={projectId} accessToken={session?.accessToken} mode={editorMode} />

{/* Create Modal */}
{session?.accessToken && (
Expand Down
28 changes: 18 additions & 10 deletions components/pr/PRActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ import {
} from "lucide-react";
import Link from "next/link";
import { branchesApi } from "@/lib/api/revisions";
import type { EditorMode } from "@/lib/stores/editorModeStore";

interface PRActionsProps {
projectId: string;
pr: PullRequest;
accessToken: string;
userRole?: string;
mode?: EditorMode;
onUpdate: (pr: PullRequest) => void;
className?: string;
}
Expand All @@ -37,9 +39,11 @@ export function PRActions({
pr,
accessToken,
userRole,
mode = "developer",
onUpdate,
className,
}: PRActionsProps) {
const isSuggestionMode = mode === "standard";
const [isSubmitting, setIsSubmitting] = useState(false);
const [error, setError] = useState<string | null>(null);
const [showReviewForm, setShowReviewForm] = useState(false);
Expand Down Expand Up @@ -341,7 +345,7 @@ export function PRActions({
Review
</Button>

{/* Merge button */}
{/* Merge / Accept button */}
{canMerge && (
<Button
size="sm"
Expand All @@ -359,11 +363,11 @@ export function PRActions({
) : (
<GitMerge className="h-4 w-4" />
)}
Merge
{isSuggestionMode ? "Accept" : "Merge"}
</Button>
)}

{/* Close button */}
{/* Close / Reject button */}
<Button
size="sm"
variant="ghost"
Expand All @@ -372,7 +376,7 @@ export function PRActions({
className="gap-1 text-red-600 hover:bg-red-50 hover:text-red-600 dark:hover:bg-red-900/20"
>
<XCircle className="h-4 w-4" />
Close
{isSuggestionMode ? "Reject" : "Close"}
</Button>
</div>
)}
Expand All @@ -382,9 +386,11 @@ export function PRActions({
open={showMergeDialog}
onOpenChange={setShowMergeDialog}
onConfirm={handleMerge}
title="Merge Pull Request"
description={`Are you sure you want to merge "${pr.title}" into ${pr.target_branch}?`}
confirmLabel="Merge"
title={isSuggestionMode ? "Accept Suggestion" : "Merge Pull Request"}
description={isSuggestionMode
? `Are you sure you want to accept "${pr.title}" into ${pr.target_branch}?`
: `Are you sure you want to merge "${pr.title}" into ${pr.target_branch}?`}
confirmLabel={isSuggestionMode ? "Accept" : "Merge"}
variant="default"
>
<label className="flex items-center gap-2 py-2">
Expand All @@ -405,9 +411,11 @@ export function PRActions({
open={showCloseDialog}
onOpenChange={setShowCloseDialog}
onConfirm={handleClose}
title="Close Pull Request"
description={`Are you sure you want to close "${pr.title}"? This will not delete the branch and can be reopened later.`}
confirmLabel="Close PR"
title={isSuggestionMode ? "Reject Suggestion" : "Close Pull Request"}
description={isSuggestionMode
? `Are you sure you want to reject "${pr.title}"? This will not delete the branch and can be reopened later.`
: `Are you sure you want to close "${pr.title}"? This will not delete the branch and can be reopened later.`}
confirmLabel={isSuggestionMode ? "Reject" : "Close PR"}
variant="danger"
/>

Expand Down
4 changes: 4 additions & 0 deletions components/pr/PRDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { PRActions } from "./PRActions";
import { PRCommentThread } from "./PRCommentThread";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import type { EditorMode } from "@/lib/stores/editorModeStore";
import {
GitPullRequest,
GitMerge,
Expand All @@ -39,6 +40,7 @@ interface PRDetailProps {
accessToken?: string;
userRole?: string;
currentUserId?: string;
mode?: EditorMode;
className?: string;
}

Expand All @@ -48,6 +50,7 @@ export function PRDetail({
accessToken,
userRole,
currentUserId,
mode = "developer",
className,
}: PRDetailProps) {
const [pr, setPR] = useState<PullRequest | null>(null);
Expand Down Expand Up @@ -301,6 +304,7 @@ export function PRDetail({
pr={pr}
accessToken={accessToken}
userRole={userRole}
mode={mode}
onUpdate={setPR}
/>
)}
Expand Down
24 changes: 18 additions & 6 deletions components/pr/PRList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,24 @@ import {
import { PRListItem } from "./PRListItem";
import { cn } from "@/lib/utils";
import { GitPullRequest } from "lucide-react";
import type { EditorMode } from "@/lib/stores/editorModeStore";

interface PRListProps {
projectId: string;
accessToken?: string;
defaultStatus?: PRStatus | "all";
mode?: EditorMode;
className?: string;
}

export function PRList({
projectId,
accessToken,
defaultStatus = "open",
mode = "developer",
className,
}: PRListProps) {
const isSuggestionMode = mode === "standard";
const [prs, setPrs] = useState<PullRequest[]>([]);
const [total, setTotal] = useState(0);
const [isLoading, setIsLoading] = useState(true);
Expand Down Expand Up @@ -69,8 +73,8 @@ export function PRList({

const statusTabs: { value: PRStatus | "all"; label: string }[] = [
{ value: "open", label: "Open" },
{ value: "merged", label: "Merged" },
{ value: "closed", label: "Closed" },
{ value: "merged", label: isSuggestionMode ? "Accepted" : "Merged" },
{ value: "closed", label: isSuggestionMode ? "Rejected" : "Closed" },
{ value: "all", label: "All" },
];

Expand All @@ -96,7 +100,7 @@ export function PRList({
</div>

<span className="text-sm text-slate-500">
{total} pull request{total !== 1 ? "s" : ""}
{total} {isSuggestionMode ? (total !== 1 ? "suggestions" : "suggestion") : (total !== 1 ? "pull requests" : "pull request")}
</span>
</div>

Expand All @@ -113,10 +117,18 @@ export function PRList({
<div className="rounded-lg border border-slate-200 bg-slate-50 px-4 py-12 text-center dark:border-slate-700 dark:bg-slate-800/50">
<GitPullRequest className="mx-auto h-12 w-12 text-slate-300 dark:text-slate-600" />
<h3 className="mt-4 font-medium text-slate-900 dark:text-slate-100">
No pull requests
{isSuggestionMode ? "No suggestions" : "No pull requests"}
</h3>
<p className="mt-1 text-sm text-slate-500">
{statusFilter === "open"
{isSuggestionMode
? statusFilter === "open"
? "There are no open suggestions for this project."
: statusFilter === "merged"
? "No suggestions have been accepted yet."
: statusFilter === "closed"
? "No suggestions have been rejected."
: "No suggestions have been submitted for this project."
: statusFilter === "open"
? "There are no open pull requests for this project."
: statusFilter === "merged"
? "No pull requests have been merged yet."
Expand All @@ -130,7 +142,7 @@ export function PRList({
{/* PR List */}
<div className="space-y-3">
{prs.map((pr) => (
<PRListItem key={pr.id} pr={pr} projectId={projectId} />
<PRListItem key={pr.id} pr={pr} projectId={projectId} mode={mode} />
))}
</div>

Expand Down
11 changes: 7 additions & 4 deletions components/pr/PRListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@ import {
User,
} from "lucide-react";
import Link from "next/link";
import type { EditorMode } from "@/lib/stores/editorModeStore";

interface PRListItemProps {
pr: PullRequest;
projectId: string;
mode?: EditorMode;
className?: string;
}

export function PRListItem({ pr, projectId, className }: PRListItemProps) {
export function PRListItem({ pr, projectId, mode = "developer", className }: PRListItemProps) {
const isSuggestionMode = mode === "standard";
const formatDate = (timestamp: string) => {
const date = new Date(timestamp);
const now = new Date();
Expand Down Expand Up @@ -56,13 +59,13 @@ export function PRListItem({ pr, projectId, className }: PRListItemProps) {
case "merged":
return (
<span className="rounded-full bg-purple-100 px-2 py-0.5 text-xs font-medium text-purple-700 dark:bg-purple-900/30 dark:text-purple-400">
Merged
{isSuggestionMode ? "Accepted" : "Merged"}
</span>
);
case "closed":
return (
<span className="rounded-full bg-red-100 px-2 py-0.5 text-xs font-medium text-red-700 dark:bg-red-900/30 dark:text-red-400">
Closed
{isSuggestionMode ? "Rejected" : "Closed"}
</span>
);
default:
Expand Down Expand Up @@ -116,7 +119,7 @@ export function PRListItem({ pr, projectId, className }: PRListItemProps) {
<span className="flex items-center gap-1">
<Clock className="h-3 w-3" />
{pr.status === "merged" && pr.merged_at
? `merged ${formatDate(pr.merged_at)}`
? `${isSuggestionMode ? "accepted" : "merged"} ${formatDate(pr.merged_at)}`
: `opened ${formatDate(pr.created_at)}`}
</span>

Expand Down
Loading