File size: 7,380 Bytes
56c7b6d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
228
229
230
231
232
import React, { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import {
  // Search as SearchIcon,
  MapPin,
  Briefcase,
  AlertCircle,
  Zap,
} from "lucide-react";
import { Card, Button, Input, Select } from "@/components/common";
import { useJobStore } from "@/store/appStore";
import { jobsAPI } from "@/utils/api";
import type { SearchFilters, Job } from "@/types";

interface JobSearchProps {
  onJobsFound?: (jobs: Job[]) => void;
}

const JobSearch: React.FC<JobSearchProps> = ({ onJobsFound }) => {
  const [filters, setFilters] = React.useState<SearchFilters>({
    keyword: "",
    location: "",
    maxJobs: 10,
    maxDaysOld: 30,
  });
  const [errors, setErrors] = React.useState<Record<string, string>>({});

  // Added a local state to track which button triggered the search
  const [searchMode, setSearchMode] = useState<"standard" | "apify" | null>(
    null
  );

  // Validate inputs helper function
  const validateInputs = () => {
    const newErrors: Record<string, string> = {};
    if (!filters.keyword.trim()) newErrors.keyword = "Job title is required";
    if (!filters.location.trim()) newErrors.location = "Location is required";

    if (Object.keys(newErrors).length > 0) {
      setErrors(newErrors);
      return false;
    }
    setErrors({});
    return true;
  };

  const { setIsSearching, isSearching, setJobs } = useJobStore();

  // const handleSearch = async (e: React.FormEvent) => {
  //   e.preventDefault();
  //   const newErrors: Record<string, string> = {};

  //   if (!filters.keyword.trim()) {
  //     newErrors.keyword = "Job title is required";
  //   }
  //   if (!filters.location.trim()) {
  //     newErrors.location = "Location is required";
  //   }

  //   if (Object.keys(newErrors).length > 0) {
  //     setErrors(newErrors);
  //     return;
  //   }

  //   setErrors({});
  //   setIsSearching(true);

  //   try {
  //     const response = await jobsAPI.searchJobs(filters);
  //     setJobs(response.jobs);
  //     onJobsFound?.(response.jobs);
  //   } catch (error) {
  //     const message =
  //       error instanceof Error ? error.message : "Failed to search jobs";
  //     setErrors({ submit: message });
  //   } finally {
  //     setIsSearching(false);
  //   }
  // };

  // --- NEW APIFY SEARCH HANDLER ---
  const handleApifySearch = async () => {
    if (!validateInputs()) return;

    setIsSearching(true);
    setSearchMode("apify");

    try {
      const response = await jobsAPI.searchJobsApify(filters);
      setJobs(response.jobs);
      onJobsFound?.(response.jobs);
    } catch (error) {
      const message =
        error instanceof Error
          ? error.message
          : "Failed to search jobs via Apify";
      setErrors({ submit: message });
    } finally {
      setIsSearching(false);
      setSearchMode(null);
    }
  };

  return (
    <Card glow innerGlow className="max-w-2xl mx-auto">
      <div className="mb-6">
        <h2 className="text-2xl font-bold text-white mb-2">Find Jobs</h2>
        <p className="text-slate-400">
          Search for job opportunities on LinkedIn
        </p>
      </div>

      {/* <form onSubmit={handleSearch} className="space-y-5"> */}
      {/* Removed onSubmit from form since buttons now handle their own click events */}
      <form className="space-y-5">
        <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
          <Input
            label="Job Title / Keyword"
            placeholder="e.g., Senior React Developer"
            icon={<Briefcase className="w-4 h-4" />}
            value={filters.keyword}
            onChange={(e) =>
              setFilters({ ...filters, keyword: e.target.value })
            }
            error={errors.keyword}
            required
          />
          <Input
            label="Location"
            placeholder="e.g., San Francisco, CA"
            icon={<MapPin className="w-4 h-4" />}
            value={filters.location}
            onChange={(e) =>
              setFilters({ ...filters, location: e.target.value })
            }
            error={errors.location}
            required
          />
        </div>

        <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
          <Select
            label="Number of Jobs"
            value={filters.maxJobs.toString()}
            onChange={(e) =>
              setFilters({ ...filters, maxJobs: parseInt(e.target.value) })
            }
            options={[
              { value: "5", label: "5 Jobs" },
              { value: "10", label: "10 Jobs" },
              { value: "15", label: "15 Jobs" },
              { value: "20", label: "20 Jobs" },
              { value: "30", label: "30 Jobs" },
            ]}
          />
          <Select
            label="Posted Within"
            value={(filters.maxDaysOld || 30).toString()}
            onChange={(e) =>
              setFilters({ ...filters, maxDaysOld: parseInt(e.target.value) })
            }
            options={[
              { value: "1", label: "Last 24 Hours" },
              { value: "3", label: "Last 3 Days" },
              { value: "7", label: "Last Week" },
              { value: "14", label: "Last 2 Weeks" },
              { value: "30", label: "Last Month" },
            ]}
          />
        </div>

        <AnimatePresence mode="wait">
          {errors.submit && (
            <motion.div
              initial={{ opacity: 0, y: -4 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -4 }}
              className="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">{errors.submit}</p>
            </motion.div>
          )}
        </AnimatePresence>

        {/* <Button
          type="submit"
          loading={isSearching}
          fullWidth
          size="lg"
          icon={<SearchIcon className="w-5 h-5" />}
        >
          {isSearching ? "Searching Jobs..." : "Search Jobs"}
        </Button> */}

        {/* Buttons Section - Changed to side-by-side layout */}
        <div className="flex flex-col sm:flex-row gap-4 pt-2">
          {/* <Button
            type="button"
            onClick={handleSearch}
            loading={isSearching && searchMode === "standard"}
            disabled={isSearching && searchMode === "apify"}
            className="flex-1"
            size="lg"
            icon={<SearchIcon className="w-5 h-5" />}
          >
            {isSearching && searchMode === "standard"
              ? "Searching..."
              : "Standard Search"}
          </Button> */}

          <Button
            type="button"
            onClick={handleApifySearch}
            loading={isSearching && searchMode === "apify"}
            disabled={isSearching && searchMode === "standard"}
            className="flex-1 bg-indigo-600 hover:bg-indigo-700 border-indigo-500" // Custom colors to differentiate
            size="lg"
            icon={<Zap className="w-5 h-5" />} // Lightning bolt icon to show it's faster
          >
            {isSearching && searchMode === "apify"
              ? "Searching..."
              : "Fast Apify Search"}
          </Button>
        </div>
      </form>
    </Card>
  );
};

export default JobSearch;