File size: 4,972 Bytes
88da18c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import React from 'react';
import { motion } from 'framer-motion';
import { Upload, FileText, AlertCircle } from 'lucide-react';
import { Card, Button } from '@/components/common';
import { useJobStore } from '@/store/appStore';
import { jobsAPI } from '@/utils/api';
import { readFileAsArrayBuffer } from '@/utils/helpers';

interface CVUploadProps {
  onSuccess?: (filename: string) => void;
  onError?: (error: string) => void;
}

const CVUpload: React.FC<CVUploadProps> = ({ onSuccess, onError }) => {
  const [isDragging, setIsDragging] = React.useState(false);
  const [error, setError] = React.useState<string | null>(null);
  const { cvTemplate, setCVTemplate } = useJobStore();
  const fileInputRef = React.useRef<HTMLInputElement>(null);

  const handleDragOver = (e: React.DragEvent) => {
    e.preventDefault();
    setIsDragging(true);
  };

  const handleDragLeave = () => {
    setIsDragging(false);
  };

  const handleFileSelect = async (files: FileList | null) => {
    if (!files || files.length === 0) return;

    const file = files[0];
    setError(null);

    // Validate file type
    if (!file.name.endsWith('.docx')) {
      setError('Only .docx files are supported');
      onError?.('Only .docx files are supported');
      return;
    }

    // Validate file size (max 10MB)
    if (file.size > 10 * 1024 * 1024) {
      setError('File size must be less than 10MB');
      onError?.('File size must be less than 10MB');
      return;
    }

    try {
      const arrayBuffer = await readFileAsArrayBuffer(file);
      const response = await jobsAPI.uploadCV(file);

      if (response.success) {
        setCVTemplate({
          filename: file.name,
          uploadedAt: new Date().toISOString(),
          size: file.size,
          content: arrayBuffer,
        });
        onSuccess?.(file.name);
      }
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Failed to upload CV';
      setError(message);
      onError?.(message);
    }
  };

  const handleDrop = (e: React.DragEvent) => {
    e.preventDefault();
    setIsDragging(false);
    handleFileSelect(e.dataTransfer.files);
  };

  return (
    <div className="w-full">
      {cvTemplate ? (
        <Card glow innerGlow>
          <div className="flex items-start gap-4">
            <div className="p-3 bg-emerald-500/20 rounded-lg">
              <FileText className="w-6 h-6 text-emerald-400" />
            </div>
            <div className="flex-1">
              <h3 className="font-semibold text-white mb-1">
                {cvTemplate.filename}
              </h3>
              <p className="text-sm text-slate-400 mb-3">
                Uploaded on{' '}
                {new Date(cvTemplate.uploadedAt).toLocaleDateString()}
              </p>
              <Button
                size="sm"
                variant="outline"
                onClick={() => {
                  setCVTemplate(null);
                  setError(null);
                }}
              >
                Change CV
              </Button>
            </div>
          </div>
        </Card>
      ) : (
        <motion.div
          onDragOver={handleDragOver}
          onDragLeave={handleDragLeave}
          onDrop={handleDrop}
          animate={isDragging ? { scale: 1.02 } : { scale: 1 }}
          className={`border-2 border-dashed rounded-xl p-8 text-center transition-all cursor-pointer ${
            isDragging
              ? 'border-emerald-500 bg-emerald-500/10'
              : 'border-slate-600 bg-slate-800/30 hover:border-emerald-400'
          }`}
          onClick={() => fileInputRef.current?.click()}
        >
          <motion.div
            animate={isDragging ? { scale: 1.1, rotate: 12 } : { scale: 1, rotate: 0 }}
            className="flex justify-center mb-4"
          >
            <div className="p-3 bg-emerald-500/20 rounded-lg">
              <Upload className="w-8 h-8 text-emerald-400" />
            </div>
          </motion.div>

          <h3 className="text-lg font-semibold text-white mb-2">
            Drop your CV or click to browse
          </h3>
          <p className="text-slate-400 text-sm mb-4">
            Supported format: .docx (Word Document)
          </p>
          <p className="text-xs text-slate-500">Max file size: 10MB</p>

          <input
            ref={fileInputRef}
            type="file"
            accept=".docx"
            onChange={(e) => handleFileSelect(e.target.files)}
            className="hidden"
          />
        </motion.div>
      )}

      {error && (
        <motion.div
          initial={{ opacity: 0, y: -4 }}
          animate={{ opacity: 1, y: 0 }}
          className="mt-4 p-4 bg-red-500/10 border border-red-500/30 rounded-lg flex gap-3"
        >
          <AlertCircle className="w-5 h-5 text-red-400 flex-shrink-0" />
          <p className="text-sm text-red-200">{error}</p>
        </motion.div>
      )}
    </div>
  );
};

export default CVUpload;