File size: 9,644 Bytes
ea9ca44
 
4b3a33f
 
 
 
 
ea9ca44
 
 
4b3a33f
ea9ca44
 
4b3a33f
 
ea9ca44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4b3a33f
ea9ca44
 
 
 
 
 
 
 
 
 
 
4b3a33f
ea9ca44
 
 
 
59f9574
 
 
 
 
b66d14f
 
59f9574
b66d14f
59f9574
b66d14f
 
59f9574
 
 
 
 
 
 
 
ea9ca44
 
 
4b3a33f
ea9ca44
4b3a33f
ea9ca44
 
4b3a33f
ea9ca44
 
 
 
 
 
 
 
59f9574
ea9ca44
 
 
 
 
 
4b3a33f
 
 
 
 
 
ea9ca44
 
 
 
4b3a33f
 
ea9ca44
 
4b3a33f
59f9574
 
ea9ca44
 
 
 
59f9574
 
 
ea9ca44
4b3a33f
 
 
ea9ca44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4b3a33f
ea9ca44
 
 
 
 
 
4b3a33f
 
 
ea9ca44
 
 
 
 
 
 
 
4b3a33f
ea9ca44
 
 
 
 
 
4b3a33f
 
 
ea9ca44
 
 
 
 
 
 
 
4b3a33f
ea9ca44
 
 
 
 
 
 
 
 
4b3a33f
ea9ca44
 
 
 
 
 
 
 
 
 
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
import React, { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { supabase } from '../supabaseClient';
import { SearchIcon } from './Icons';
import JobDetail from './JobDetail';
import ApplyModel from './ApplyModel';
import JobCard from './JobCard';
import VerificationModal from './VerificationModal'; // βœ… Import the new modal

export default function JobListings({ searchQuery, setSearchQuery, isSearching, filteredJobListings }) {

    const [selectedJob, setSelectedJob] = useState(null);
    const [appliedJobIds, setAppliedJobIds] = useState(new Set());
    const [applying, setApplying] = useState(null);

    // State for the Apply Modal
    const [jobToApply, setJobToApply] = useState(null);

    // βœ… State for Verification Modal
    const [showVerificationModal, setShowVerificationModal] = useState(false);

    // 1. Check Existing Applications on Load
    useEffect(() => {
        const fetchApplications = async () => {
            const { data: { user } } = await supabase.auth.getUser();
            if (user) {
                const { data } = await supabase
                    .from('applications')
                    .select('job_id')
                    .eq('user_id', user.id);

                if (data) {
                    setAppliedJobIds(new Set(data.map(app => app.job_id)));
                }
            }
        };
        fetchApplications();
    }, []);

    // 2. Open Apply Modal
    const initiateApply = (jobId) => {
        const job = filteredJobListings.find(j => j.id === jobId);
        if (job) {
            setJobToApply(job);
        }
    };

    // βœ… Helper: Send welcome message to applicant
    const sendApplicationConfirmationMessage = async (userId, jobTitle) => {
        try {
            const message = `Hello, Thank you for applying for the **${jobTitle}** position. We have received your application and our team is currently reviewing your profile. If your qualifications match our requirements, we will contact you shortly regarding the next steps in the selection process. We appreciate your interest in this opportunity.`;

            // Insert message using applicant's ID for both sender and receiver 
            // (RLS prevents applicants from inserting messages on behalf of an Admin)
            const { error } = await supabase.from('messages').insert([{
                sender_id: userId,
                receiver_id: userId,
                content: message,
                is_read: false
            }]);

            if (error) console.error('Error sending confirmation message:', error);
        } catch (err) {
            console.error('Failed to send confirmation message:', err);
        }
    };

    // 3. Submit Application (With Verification Gatekeeper)
    const handleFinalSubmit = async (formData) => {
        if (!jobToApply) return;

        setApplying(jobToApply.id);

        try {
            const { data: { user } } = await supabase.auth.getUser();

            if (!user) {
                alert("Please log in to apply.");
                return;
            }

            // --- πŸ”’ GATEKEEPER CHECK: Verify Phone Status ---
            const { data: profile, error: profileError } = await supabase
                .from('profiles')
                .select('is_phone_verified, experience_years')
                .eq('id', user.id)
                .single();

            if (profileError) throw profileError;

            // If NOT verified, stop the application and show modal
            /** if (!profile.is_phone_verified) {
                 setApplying(null);        // Stop loading spinner
                 setJobToApply(null);      // Close application form
                 setShowVerificationModal(true); // Open Verification Modal
                 return;                   // πŸ›‘ Stop execution here
             } **/

            // --- βœ… IF VERIFIED: Proceed with Application ---
            const { error } = await supabase
                .from('applications')
                .insert([{
                    job_id: jobToApply.id,
                    user_id: user.id,
                    status: 'Pending',
                    resume_url: formData.resume_url,
                    cover_letter: formData.cover_letter,
                    experience: profile.experience // Include experience from profile
                }]);

            if (error) throw error;

            // βœ… Send confirmation message to applicant
            await sendApplicationConfirmationMessage(user.id, jobToApply.title);

            setAppliedJobIds(prev => new Set(prev).add(jobToApply.id));
            alert("Application submitted successfully!");

            setJobToApply(null);

        } catch (error) {
            console.error("Error applying:", error.message);
            alert(`Could not apply: ${error.message}`);
        } finally {
            // Only stop spinner if we didn't switch to verification modal
            if (!showVerificationModal) setApplying(null);
        }
    };

    // 4. Withdraw Application
    const handleWithdraw = async (jobId) => {
        if (!confirm("Are you sure you want to withdraw this application?")) {
            return;
        }

        try {
            const { data: { user } } = await supabase.auth.getUser();
            if (!user) return;

            const { error } = await supabase
                .from('applications')
                .delete()
                .eq('job_id', jobId)
                .eq('user_id', user.id);

            if (error) throw error;

            setAppliedJobIds(prev => {
                const newSet = new Set(prev);
                newSet.delete(jobId);
                return newSet;
            });

        } catch (error) {
            console.error("Error withdrawing:", error.message);
            alert("Failed to withdraw application.");
        }
    };

    return (
        <>
            {/* Search Bar */}
            <div style={{ backgroundColor: 'rgba(251, 191, 36, 0.1)', border: '1px solid rgba(251, 191, 36, 0.3)', borderRadius: '1rem', padding: '1.5rem', marginBottom: '2rem' }}>
                <h2 style={{ fontSize: '1.5rem', fontWeight: 'bold' }}>Open Positions</h2>
                <p style={{ color: '#d1d5db', marginBottom: '1rem' }}>Browse through our current job openings</p>
                <div style={{ position: 'relative' }}>
                    <motion.div animate={{ scale: isSearching ? 1.1 : 1, rotate: isSearching ? 5 : 0 }} transition={{ type: 'spring', stiffness: 400, damping: 15 }} style={{ position: 'absolute', left: '0.75rem', top: '0', bottom: '0', display: 'grid', placeItems: 'center' }}><SearchIcon /></motion.div>
                    <input type="text" value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} placeholder="Search by job title..." style={{ width: '100%', padding: '0.75rem 1rem 0.75rem 2.5rem', borderRadius: '0.5rem', border: '1px solid rgba(251, 191, 36, 0.3)', backgroundColor: 'rgba(255,255,255,0.1)', color: 'white' }} />
                </div>
            </div>

            {/* Job Grid */}
            <motion.main layout style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', gap: '2rem' }}>
                <AnimatePresence>
                    {filteredJobListings.length > 0 ? (
                        filteredJobListings.map((job) => (
                            <motion.div key={job.id} layout initial={{ opacity: 0, scale: 0.8 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.8 }} transition={{ duration: 0.2 }}>
                                <JobCard
                                    {...job}
                                    onViewDetails={() => setSelectedJob(job)}
                                    onApply={() => initiateApply(job.id)}
                                    onWithdraw={() => handleWithdraw(job.id)}
                                    isApplied={appliedJobIds.has(job.id)}
                                    isApplying={applying === job.id}
                                />
                            </motion.div>
                        ))
                    ) : (
                        <motion.p initial={{ opacity: 0 }} animate={{ opacity: 1 }} style={{ color: '#d1d5db' }}>No jobs found.</motion.p>
                    )}
                </AnimatePresence>
            </motion.main>

            {/* Job Detail Modal */}
            {selectedJob && (
                <JobDetail
                    job={selectedJob}
                    onClose={() => setSelectedJob(null)}
                    onApply={() => initiateApply(selectedJob.id)}
                    isApplied={appliedJobIds.has(selectedJob.id)}
                    isApplying={applying === selectedJob.id}
                />
            )}

            {/* Apply Form Modal */}
            {jobToApply && (
                <ApplyModel
                    job={jobToApply}
                    isSubmitting={applying === jobToApply.id}
                    onClose={() => setJobToApply(null)}
                    onSubmit={handleFinalSubmit}
                />
            )}

            {/* βœ… OTP Verification Modal */}
            {showVerificationModal && (
                <VerificationModal
                    onClose={() => setShowVerificationModal(false)}
                    onVerified={() => {
                        setShowVerificationModal(false);
                        alert("Phone verified successfully! Please click Apply again.");
                    }}
                />
            )}
        </>
    );
};