File size: 5,495 Bytes
a27839e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
"use client";

import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog";
import { ScrollArea } from "@/components/ui/scroll-area";
import { DiffResult } from "@/services/diffPatch";
import { Card, CardContent } from "@/components/ui/card";

interface DiffReviewModalProps {
  isOpen: boolean;
  onClose: () => void;
  diffResult: DiffResult | null;
  onApply: () => void;
}

const DiffReviewModal = ({ isOpen, onClose, diffResult, onApply }: DiffReviewModalProps) => {
  const [isApplying, setIsApplying] = useState(false);
  
  const handleApply = async () => {
    setIsApplying(true);
    try {
      await onApply();
      onClose();
    } finally {
      setIsApplying(false);
    }
  };

  if (!diffResult) return null;

  let oldLineNum = 1;
  let newLineNum = 1;

  return (
    <Dialog open={isOpen} onOpenChange={onClose}>
      <DialogContent className="max-w-4xl h-[80vh] flex flex-col">
        <DialogHeader>
          <DialogTitle className="flex items-center">
            <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="mr-2">
              <path d="M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
              <path d="M14 15H9"/>
              <path d="M16 12c.5523 0 1-.4477 1-1s-.4477-1-1-1-.999.4477-.999 1 .4467 1 .999 1Z"/>
              <path d="M16 18c1.6569 0 3-1.3431 3-3s-1.3431-3-3-3-3 1.3431-3 3 1.3431 3 3 3Z"/>
              <path d="m17 2 4 4"/>
              <path d="M21 2 12 11"/>
            </svg>
            Code Changes Review
          </DialogTitle>
        </DialogHeader>
        <div className="text-sm text-gray-500 mb-2 flex items-center">
          <span className="bg-green-100 text-green-800 px-2 py-1 rounded mr-2">+{diffResult.added} lines</span>
          <span className="bg-red-100 text-red-800 px-2 py-1 rounded">-{diffResult.removed} lines</span>
        </div>
        <Card className="flex-1 border border-gray-200 min-h-0">
          <CardContent className="p-0 h-full">
            <ScrollArea className="h-full font-mono text-sm bg-gray-50 rounded-md">
              <div className="p-4">
                {diffResult.changes.map((change, changeIndex) => {
                  const lines = change.value.endsWith('\n') ? change.value.slice(0, -1).split('\n') : change.value.split('\n');
                  
                  return lines.map((line, lineIndex) => {
                    let currentOldLine = ' ';
                    let currentNewLine = ' ';
                    
                    if (change.removed) {
                      currentOldLine = String(oldLineNum++);
                    } else if (change.added) {
                      currentNewLine = String(newLineNum++);
                    } else {
                      currentOldLine = String(oldLineNum++);
                      currentNewLine = String(newLineNum++);
                    }

                    return (
                      <div 
                        key={`${changeIndex}-${lineIndex}`} 
                        className={`flex items-start py-0.5 ${
                          change.added ? 'bg-green-100' : 
                          change.removed ? 'bg-red-100' : ''
                        }`}
                      >
                        <span className="w-10 text-right pr-2 text-gray-400 select-none">{currentOldLine}</span>
                        <span className="w-10 text-right pr-2 text-gray-400 select-none">{currentNewLine}</span>
                        <span className={`w-6 text-center font-bold ${
                          change.added ? 'text-green-700' : 
                          change.removed ? 'text-red-700' : 'text-gray-500'
                        }`}>
                          {change.added ? '+' : change.removed ? '-' : ' '}
                        </span>
                        <span className={`flex-1 whitespace-pre-wrap break-all ${
                          change.added ? 'text-green-900' : 
                          change.removed ? 'text-red-900' : 'text-gray-800'
                        }`}>
                          {line}
                        </span>
                      </div>
                    );
                  });
                })}
              </div>
            </ScrollArea>
          </CardContent>
        </Card>
        <DialogFooter className="flex space-x-2 pt-4">
          <Button variant="outline" onClick={onClose} className="border-gray-300">
            Cancel
          </Button>
          <Button 
            onClick={handleApply} 
            disabled={isApplying}
            className="bg-gradient-to-r from-blue-600 to-indigo-700 hover:from-blue-700 hover:to-indigo-800"
          >
            {isApplying ? (
              <>
                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="mr-2 animate-spin">
                  <path d="M21 12a9 9 0 1 1-6.219-8.56"/>
                </svg>
                Applying...
              </>
            ) : (
              "Apply Changes"
            )}
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
};

export default DiffReviewModal;