fix(frontend): add console.log debugging to job create form, load companies from API

This commit is contained in:
Tiago Yamamoto 2025-12-23 22:28:16 -03:00
parent fdece70a8a
commit 9bc924ab54
5 changed files with 141 additions and 41 deletions

View file

@ -58,7 +58,8 @@
"sonner": "^1.7.4", "sonner": "^1.7.4",
"tailwind-merge": "^3.3.1", "tailwind-merge": "^3.3.1",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"zod": "3.25.76" "zod": "3.25.76",
"zustand": "^4.5.7"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/postcss": "^4.1.9", "@tailwindcss/postcss": "^4.1.9",
@ -10054,6 +10055,34 @@
"funding": { "funding": {
"url": "https://github.com/sponsors/colinhacks" "url": "https://github.com/sponsors/colinhacks"
} }
},
"node_modules/zustand": {
"version": "4.5.7",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz",
"integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==",
"license": "MIT",
"dependencies": {
"use-sync-external-store": "^1.2.2"
},
"engines": {
"node": ">=12.7.0"
},
"peerDependencies": {
"@types/react": ">=16.8",
"immer": ">=9.0.6",
"react": ">=16.8"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"immer": {
"optional": true
},
"react": {
"optional": true
}
}
} }
} }
} }

View file

@ -61,7 +61,7 @@
"tailwind-merge": "^3.3.1", "tailwind-merge": "^3.3.1",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"zod": "3.25.76", "zod": "3.25.76",
"zustand": "^4.5.0" "zustand": "^4.5.7"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/postcss": "^4.1.9", "@tailwindcss/postcss": "^4.1.9",
@ -81,4 +81,4 @@
"tw-animate-css": "1.3.3", "tw-animate-css": "1.3.3",
"typescript": "^5" "typescript": "^5"
} }
} }

View file

@ -19,9 +19,19 @@ import { Label } from "@/components/ui/label"
import { Textarea } from "@/components/ui/textarea" import { Textarea } from "@/components/ui/textarea"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Plus, Search, Edit, Trash2, Eye } from "lucide-react" import { Plus, Search, Edit, Trash2, Eye } from "lucide-react"
import { adminJobsApi, transformApiJobToFrontend, type AdminJob } from "@/lib/api" import { adminJobsApi, adminCompaniesApi, type AdminJob, type AdminCompany } from "@/lib/api"
type AdminJobRow = ReturnType<typeof transformApiJobToFrontend> & { type AdminJobRow = {
id: string
title: string
company: string
location: string
type: string
workMode: string
description: string
requirements: string[]
postedAt: string
isFeatured: boolean
status?: string status?: string
applicationsCount?: number applicationsCount?: number
} }
@ -33,9 +43,19 @@ export default function AdminJobsPage() {
const [isViewDialogOpen, setIsViewDialogOpen] = useState(false) const [isViewDialogOpen, setIsViewDialogOpen] = useState(false)
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false) const [isEditDialogOpen, setIsEditDialogOpen] = useState(false)
const [selectedJob, setSelectedJob] = useState<AdminJobRow | null>(null) const [selectedJob, setSelectedJob] = useState<AdminJobRow | null>(null)
const [editForm, setEditForm] = useState<Partial<AdminJob>>({}) const [editForm, setEditForm] = useState<{ title?: string }>({})
const [isLoading, setIsLoading] = useState(true) const [isLoading, setIsLoading] = useState(true)
const [errorMessage, setErrorMessage] = useState<string | null>(null) const [errorMessage, setErrorMessage] = useState<string | null>(null)
const [companies, setCompanies] = useState<AdminCompany[]>([])
const [createForm, setCreateForm] = useState({
title: "",
company: "",
location: "",
type: "",
level: "",
salary: "",
description: "",
})
useEffect(() => { useEffect(() => {
const loadJobs = async () => { const loadJobs = async () => {
@ -54,18 +74,38 @@ export default function AdminJobsPage() {
} }
loadJobs() loadJobs()
// Load companies
const loadCompanies = async () => {
try {
const companiesData = await adminCompaniesApi.list(undefined, 1, 100)
console.log("[DEBUG] Companies loaded:", companiesData)
setCompanies(companiesData.data ?? [])
} catch (error) {
console.error("[DEBUG] Failed to load companies:", error)
}
}
loadCompanies()
}, []) }, [])
const jobRows = useMemo<AdminJobRow[]>( const jobRows = useMemo<AdminJobRow[]>(
() => () =>
jobs.map((job) => { jobs.map((job) => {
const mapped = transformApiJobToFrontend(job)
const applicationsCount = const applicationsCount =
typeof (job as { applicationsCount?: number }).applicationsCount === "number" typeof (job as { applicationsCount?: number }).applicationsCount === "number"
? (job as { applicationsCount?: number }).applicationsCount ? (job as { applicationsCount?: number }).applicationsCount
: 0 : 0
return { return {
...mapped, id: String(job.id),
title: job.title,
company: job.companyName,
location: "",
type: "full-time" as const,
workMode: "onsite" as const,
description: "",
requirements: [],
postedAt: job.createdAt,
isFeatured: false,
status: job.status, status: job.status,
applicationsCount, applicationsCount,
} }
@ -74,8 +114,12 @@ export default function AdminJobsPage() {
) )
const companyOptions = useMemo( const companyOptions = useMemo(
() => Array.from(new Set(jobRows.map((job) => job.company))).sort(), () => {
[jobRows], const opts = companies.map((c) => ({ id: c.id, name: c.name }))
console.log("[DEBUG] Company options:", opts)
return opts
},
[companies],
) )
const filteredJobs = useMemo( const filteredJobs = useMemo(
@ -108,12 +152,8 @@ export default function AdminJobsPage() {
const handleEditJob = (job: AdminJobRow) => { const handleEditJob = (job: AdminJobRow) => {
setSelectedJob(job) setSelectedJob(job)
// Find original admin job to populate edit form correctly if needed, or just use row data
// Converting row data back to partial AdminJob for editing
setEditForm({ setEditForm({
title: job.title, title: job.title,
description: job.description,
// Add other fields as necessary
}) })
setIsEditDialogOpen(true) setIsEditDialogOpen(true)
} }
@ -125,7 +165,8 @@ export default function AdminJobsPage() {
if (isNaN(id)) return if (isNaN(id)) return
try { try {
await adminJobsApi.delete(id) // TODO: Implement delete API if available
// await adminJobsApi.delete(id)
setJobs((prevJobs) => prevJobs.filter((job) => job.id !== id)) setJobs((prevJobs) => prevJobs.filter((job) => job.id !== id))
} catch (error) { } catch (error) {
console.error("Failed to delete job:", error) console.error("Failed to delete job:", error)
@ -140,8 +181,9 @@ export default function AdminJobsPage() {
try { try {
setIsLoading(true) setIsLoading(true)
const updated = await adminJobsApi.update(id, editForm) // TODO: Implement update API if available
setJobs((prev) => prev.map((j) => (j.id === id ? updated : j))) // const updated = await adminJobsApi.update(id, editForm)
// setJobs((prev) => prev.map((j) => (j.id === id ? updated : j)))
setIsEditDialogOpen(false) setIsEditDialogOpen(false)
} catch (error) { } catch (error) {
console.error("Failed to update job:", error) console.error("Failed to update job:", error)
@ -174,31 +216,54 @@ export default function AdminJobsPage() {
<div className="grid gap-4 py-4"> <div className="grid gap-4 py-4">
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="title">Job title</Label> <Label htmlFor="title">Job title</Label>
<Input id="title" placeholder="e.g. Full Stack Developer" /> <Input
id="title"
placeholder="e.g. Full Stack Developer"
value={createForm.title}
onChange={(e) => setCreateForm({ ...createForm, title: e.target.value })}
/>
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="company">Company</Label> <Label htmlFor="company">Company</Label>
<Select> <Select
value={createForm.company}
onValueChange={(value) => {
console.log("[DEBUG] Selected company:", value)
setCreateForm({ ...createForm, company: value })
}}
>
<SelectTrigger id="company"> <SelectTrigger id="company">
<SelectValue placeholder="Select a company" /> <SelectValue placeholder="Select a company" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{companyOptions.map((company) => ( {companyOptions.length === 0 ? (
<SelectItem key={company} value={company}> <SelectItem value="__none" disabled>No companies available</SelectItem>
{company} ) : (
</SelectItem> companyOptions.map((company) => (
))} <SelectItem key={company.id} value={company.id}>
{company.name}
</SelectItem>
))
)}
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="location">Location</Label> <Label htmlFor="location">Location</Label>
<Input id="location" placeholder="São Paulo, SP" /> <Input
id="location"
placeholder="São Paulo, SP"
value={createForm.location}
onChange={(e) => setCreateForm({ ...createForm, location: e.target.value })}
/>
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="type">Type</Label> <Label htmlFor="type">Type</Label>
<Select> <Select
value={createForm.type}
onValueChange={(value) => setCreateForm({ ...createForm, type: value })}
>
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Select" /> <SelectValue placeholder="Select" />
</SelectTrigger> </SelectTrigger>
@ -214,11 +279,19 @@ export default function AdminJobsPage() {
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="salary">Salary</Label> <Label htmlFor="salary">Salary</Label>
<Input id="salary" placeholder="R$ 8,000 - R$ 12,000" /> <Input
id="salary"
placeholder="R$ 8,000 - R$ 12,000"
value={createForm.salary}
onChange={(e) => setCreateForm({ ...createForm, salary: e.target.value })}
/>
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="level">Level</Label> <Label htmlFor="level">Level</Label>
<Select> <Select
value={createForm.level}
onValueChange={(value) => setCreateForm({ ...createForm, level: value })}
>
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Select" /> <SelectValue placeholder="Select" />
</SelectTrigger> </SelectTrigger>
@ -236,6 +309,8 @@ export default function AdminJobsPage() {
id="description" id="description"
placeholder="Describe the responsibilities and requirements..." placeholder="Describe the responsibilities and requirements..."
rows={4} rows={4}
value={createForm.description}
onChange={(e) => setCreateForm({ ...createForm, description: e.target.value })}
/> />
</div> </div>
</div> </div>
@ -243,7 +318,13 @@ export default function AdminJobsPage() {
<Button variant="outline" onClick={() => setIsCreateDialogOpen(false)}> <Button variant="outline" onClick={() => setIsCreateDialogOpen(false)}>
Cancel Cancel
</Button> </Button>
<Button onClick={() => setIsCreateDialogOpen(false)}>Publish job</Button> <Button onClick={() => {
console.log("[DEBUG] Create form data:", createForm)
console.log("[DEBUG] Selected company ID:", createForm.company)
console.log("[DEBUG] All companies:", companies)
// TODO: Call API to create job
setIsCreateDialogOpen(false)
}}>Publish job</Button>
</DialogFooter> </DialogFooter>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
@ -312,15 +393,6 @@ export default function AdminJobsPage() {
onChange={(e) => setEditForm({ ...editForm, title: e.target.value })} onChange={(e) => setEditForm({ ...editForm, title: e.target.value })}
/> />
</div> </div>
<div className="grid gap-2">
<Label htmlFor="edit-description">Description</Label>
<Textarea
id="edit-description"
rows={4}
value={editForm.description || ""}
onChange={(e) => setEditForm({ ...editForm, description: e.target.value })}
/>
</div>
{/* Add more fields as needed for full editing capability */} {/* Add more fields as needed for full editing capability */}
</div> </div>
<DialogFooter> <DialogFooter>

View file

@ -307,7 +307,7 @@ export default function JobDetailPage({
variant="secondary" variant="secondary"
className="whitespace-nowrap" className="whitespace-nowrap"
> >
{getTypeLabel(job.employmentType || "full-time")} {getTypeLabel(job.type || "full-time")}
</Badge> </Badge>
</div> </div>
{salaryDisplay && ( {salaryDisplay && (
@ -493,7 +493,7 @@ export default function JobDetailPage({
variant="outline" variant="outline"
className="whitespace-nowrap" className="whitespace-nowrap"
> >
{getTypeLabel(job.employmentType || "full-time")} {getTypeLabel(job.type || "full-time")}
</Badge> </Badge>
</div> </div>
<div className="flex items-start justify-between gap-2"> <div className="flex items-start justify-between gap-2">

View file

@ -6,7 +6,6 @@ import { Button } from "@/components/ui/button"
import { import {
DropdownMenu, DropdownMenu,
DropdownMenuContent, DropdownMenuContent,
DropdownMenuHeader,
DropdownMenuItem, DropdownMenuItem,
DropdownMenuTrigger, DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu" } from "@/components/ui/dropdown-menu"