File size: 6,632 Bytes
67264dd
 
 
 
1116aba
67264dd
 
1116aba
 
67264dd
 
 
 
 
 
1116aba
67264dd
 
 
 
 
 
 
 
 
 
1116aba
 
 
 
67264dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"use client";

import React, { useState } from "react";
import { X, Send, AlertCircle } from "lucide-react";
import axios from "axios";
import { motion, AnimatePresence } from "framer-motion";

const getToken = () => typeof window !== 'undefined' ? localStorage.getItem('access_token') : null;

export default function FeedbackModal({ isOpen, onClose }: { isOpen: boolean, onClose: () => void }) {
    const [issueType, setIssueType] = useState("Bug");
    const [message, setMessage] = useState("");
    const [status, setStatus] = useState<"idle" | "loading" | "success" | "error">("idle");
    const [errorMsg, setErrorMsg] = useState("");

    const handleSubmit = async (e: React.FormEvent) => {
        e.preventDefault();
        if (message.trim().length < 5) {
            setErrorMsg("Message must be at least 5 characters.");
            return;
        }
        
        setStatus("loading");
        setErrorMsg("");

        try {
            const baseUrl = process.env.NEXT_PUBLIC_BACKEND_URL || 'http://localhost:8000';
            await axios.post(`${baseUrl}/issues/`, { issue_type: issueType, message }, {
                headers: { Authorization: `Bearer ${getToken()}` }
            });
            setStatus("success");
            setTimeout(() => {
                setStatus("idle");
                setMessage("");
                onClose();
            }, 2000);
        } catch (err: any) {
            setStatus("error");
            setErrorMsg(err.response?.data?.detail || "Failed to submit feedback.");
        }
    };

    if (!isOpen) return null;

    return (
        <AnimatePresence>
            <motion.div 
                initial={{ opacity: 0 }} 
                animate={{ opacity: 1 }} 
                exit={{ opacity: 0 }}
                className="fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-sm p-4"
                onClick={onClose}
            >
                <motion.div 
                    initial={{ scale: 0.95, opacity: 0, y: 10 }}
                    animate={{ scale: 1, opacity: 1, y: 0 }}
                    exit={{ scale: 0.95, opacity: 0, y: 10 }}
                    onClick={e => e.stopPropagation()}
                    className="bg-[var(--theme-bg)] border border-theme-border shadow-2xl rounded-2xl w-full max-w-lg overflow-hidden flex flex-col"
                >
                    <div className="p-6 border-b border-theme-border flex justify-between items-center">
                        <h2 className="text-xl font-semibold tracking-wide text-[var(--theme-text)]">Report an Issue</h2>
                        <button onClick={onClose} className="text-theme-text/50 hover:text-[var(--theme-text)] transition-colors !cursor-none">
                            <X className="w-5 h-5 !cursor-none" />
                        </button>
                    </div>

                    <div className="p-6">
                        {status === "success" ? (
                            <div className="flex flex-col items-center justify-center py-8 text-green-400">
                                <AlertCircle className="w-12 h-12 mb-4" />
                                <p className="text-lg">Thanks for your feedback!</p>
                            </div>
                        ) : (
                            <form onSubmit={handleSubmit} className="flex flex-col gap-4">
                                {errorMsg && (
                                    <div className="bg-red-500/10 border border-red-500/20 text-red-400 p-3 rounded-lg text-sm">
                                        {errorMsg}
                                    </div>
                                )}
                                
                                <div className="flex flex-col gap-2">
                                    <label className="text-sm text-[var(--theme-text)]/70 uppercase tracking-widest font-mono">Issue Type</label>
                                    <select 
                                        value={issueType} 
                                        onChange={e => setIssueType(e.target.value)}
                                        className="bg-[var(--theme-bg)] border border-theme-border text-[var(--theme-text)] rounded-lg p-3 outline-none focus:border-[#d0c4bb]/50 transition-colors !cursor-none"
                                    >
                                        <option value="Bug">Bug</option>
                                        <option value="UI Issue">UI Issue</option>
                                        <option value="Suggestion">Suggestion</option>
                                        <option value="Other">Other</option>
                                    </select>
                                </div>

                                <div className="flex flex-col gap-2">
                                    <label className="text-sm text-[var(--theme-text)]/70 uppercase tracking-widest font-mono">Message</label>
                                    <textarea 
                                        value={message}
                                        onChange={e => setMessage(e.target.value)}
                                        placeholder="Describe the issue or suggestion..."
                                        rows={4}
                                        className="bg-[var(--theme-bg)] border border-theme-border text-[var(--theme-text)] rounded-lg p-3 outline-none focus:border-[#d0c4bb]/50 transition-colors resize-none !cursor-none"
                                    />
                                </div>

                                <button 
                                    disabled={status === "loading"}
                                    type="submit" 
                                    className="mt-2 bg-[#d0c4bb] text-[var(--theme-bg)] hover:bg-[var(--theme-text)] font-semibold py-3 rounded-lg flex items-center justify-center gap-2 transition-all disabled:opacity-50 !cursor-none"
                                >
                                    {status === "loading" ? "Submitting..." : (
                                        <>
                                            <Send className="w-4 h-4 !cursor-none" />
                                            Submit Feedback
                                        </>
                                    )}
                                </button>
                            </form>
                        )}
                    </div>
                </motion.div>
            </motion.div>
        </AnimatePresence>
    );
}