|
1 | | -import { useState } from "react"; |
| 1 | +import { useState, useCallback } from "react"; |
2 | 2 | import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; |
3 | | -import { Plus, Pencil, Trash2, ExternalLink, X, Loader2, Link2 } from "lucide-react"; |
| 3 | +import { Plus, Pencil, Trash2, ExternalLink, X, Loader2, Link2, Braces } from "lucide-react"; |
4 | 4 | import api from "../../../lib/axios"; |
5 | 5 | import toast from "react-hot-toast"; |
6 | 6 |
|
@@ -30,6 +30,37 @@ export default function AdminExternalJobsPage() { |
30 | 30 | const [showForm, setShowForm] = useState(false); |
31 | 31 | const [editingId, setEditingId] = useState<number | null>(null); |
32 | 32 | const [form, setForm] = useState(EMPTY_FORM); |
| 33 | + const [jsonInput, setJsonInput] = useState(""); |
| 34 | + const [jsonError, setJsonError] = useState(""); |
| 35 | + |
| 36 | + const parseJsonInput = useCallback((raw: string) => { |
| 37 | + setJsonInput(raw); |
| 38 | + setJsonError(""); |
| 39 | + if (!raw.trim()) return; |
| 40 | + try { |
| 41 | + const obj = JSON.parse(raw); |
| 42 | + if (typeof obj !== "object" || obj === null) { |
| 43 | + setJsonError("Expected a JSON object"); |
| 44 | + return; |
| 45 | + } |
| 46 | + const str = (key: string) => (typeof obj[key] === "string" ? obj[key] : ""); |
| 47 | + const tags = Array.isArray(obj.tags) |
| 48 | + ? obj.tags.join(", ") |
| 49 | + : typeof obj.tags === "string" ? obj.tags : ""; |
| 50 | + setForm({ |
| 51 | + company: str("company"), |
| 52 | + role: str("role") || str("title") || str("position"), |
| 53 | + description: str("description") || str("desc") || str("about"), |
| 54 | + salary: str("salary") || str("stipend") || str("compensation") || str("ctc"), |
| 55 | + location: str("location") || str("city"), |
| 56 | + applyLink: str("applyLink") || str("apply_link") || str("url") || str("link"), |
| 57 | + tags, |
| 58 | + }); |
| 59 | + toast.success("Fields populated from JSON"); |
| 60 | + } catch { |
| 61 | + setJsonError("Invalid JSON — check syntax"); |
| 62 | + } |
| 63 | + }, []); |
33 | 64 |
|
34 | 65 | const { data, isLoading } = useQuery({ |
35 | 66 | queryKey: ["admin-external-jobs", page, search], |
@@ -87,6 +118,8 @@ export default function AdminExternalJobsPage() { |
87 | 118 | setShowForm(false); |
88 | 119 | setEditingId(null); |
89 | 120 | setForm(EMPTY_FORM); |
| 121 | + setJsonInput(""); |
| 122 | + setJsonError(""); |
90 | 123 | }; |
91 | 124 |
|
92 | 125 | const openEdit = (job: AdminJob) => { |
@@ -131,11 +164,33 @@ export default function AdminExternalJobsPage() { |
131 | 164 | {/* Form Modal */} |
132 | 165 | {showForm && ( |
133 | 166 | <div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4" onClick={closeForm}> |
134 | | - <div className="bg-white dark:bg-gray-900 rounded-xl border border-gray-200 dark:border-gray-700 p-6 w-full max-w-lg space-y-4" onClick={(e) => e.stopPropagation()}> |
| 167 | + <div className="bg-white dark:bg-gray-900 rounded-xl border border-gray-200 dark:border-gray-700 p-6 w-full max-w-lg space-y-4 max-h-[90vh] overflow-y-auto" onClick={(e) => e.stopPropagation()}> |
135 | 168 | <div className="flex items-center justify-between"> |
136 | 169 | <h2 className="text-lg font-bold dark:text-white">{editingId ? "Edit Job" : "Add External Job"}</h2> |
137 | 170 | <button onClick={closeForm} className="text-gray-400 hover:text-gray-600"><X className="w-5 h-5" /></button> |
138 | 171 | </div> |
| 172 | + {/* JSON quick-fill */} |
| 173 | + {!editingId && ( |
| 174 | + <div> |
| 175 | + <label className="flex items-center gap-1.5 text-xs font-medium text-gray-500 dark:text-gray-400 mb-1"> |
| 176 | + <Braces className="w-3.5 h-3.5" /> Paste JSON to auto-fill |
| 177 | + </label> |
| 178 | + <textarea |
| 179 | + value={jsonInput} |
| 180 | + onChange={(e) => parseJsonInput(e.target.value)} |
| 181 | + placeholder={'{\n "company": "Google",\n "role": "SDE Intern",\n "description": "...",\n "salary": "50k/month",\n "location": "Bangalore",\n "applyLink": "https://...",\n "tags": ["React", "Remote"]\n}'} |
| 182 | + className={`w-full px-3 py-2 text-xs font-mono border rounded-lg dark:bg-gray-800 dark:text-white focus:outline-none focus:ring-2 ${ |
| 183 | + jsonError |
| 184 | + ? "border-red-400 dark:border-red-500 focus:ring-red-300/30" |
| 185 | + : "border-gray-300 dark:border-gray-600 focus:ring-black/20 dark:focus:ring-white/20" |
| 186 | + }`} |
| 187 | + rows={4} |
| 188 | + /> |
| 189 | + {jsonError && <p className="text-xs text-red-500 mt-1">{jsonError}</p>} |
| 190 | + <div className="border-b border-gray-200 dark:border-gray-700 mt-3" /> |
| 191 | + </div> |
| 192 | + )} |
| 193 | + |
139 | 194 | <Input label="Company" value={form.company} onChange={(v) => setForm({ ...form, company: v })} placeholder="e.g. Google" /> |
140 | 195 | <Input label="Role" value={form.role} onChange={(v) => setForm({ ...form, role: v })} placeholder="e.g. Software Engineer Intern" /> |
141 | 196 | <div> |
|
0 commit comments