feat: Enhance provider components with detailed documentation and comments
- Added comprehensive comments and JSDoc-style documentation to NotificationProvider, ThemeProvider, UserProvider, and WebsiteThemeProvider for better clarity and maintainability. - Improved type definitions in index.ts for better code understanding and usage. - Introduced Docker support with Dockerfiles for various services including AI server, signaling server, and browser-use service. - Created a docker-compose.yml file to orchestrate multiple services including PostgreSQL, AI, scraper, and frontend. - Added a startup guide (startup.txt) for setting up the CRM environment on Ubuntu with Docker. - Included a .dockerignore file to exclude unnecessary files from Docker builds.
This commit is contained in:
@@ -1,8 +1,13 @@
|
||||
// ── AI Assistant: Job Selector Component ──
|
||||
// Dropdown that fetches and displays job categories from the API.
|
||||
// Allows the user to pick a job, then triggers a Facebook search.
|
||||
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import { Briefcase, ChevronDown, Loader2, Search } from "lucide-react"
|
||||
|
||||
/** A job category returned from the API */
|
||||
interface Job {
|
||||
job_title: string
|
||||
keywords: string[]
|
||||
@@ -11,17 +16,25 @@ interface Job {
|
||||
}
|
||||
|
||||
interface JobSelectorProps {
|
||||
/** Called when a job is selected or cleared (null) */
|
||||
onSelect: (job: Job | null) => void
|
||||
/** Called when the user clicks the "Search Facebook" button */
|
||||
onSearch?: (job: Job) => void
|
||||
/** Whether a search is currently in progress */
|
||||
searching?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* JobSelector — dropdown to pick a job category and initiate a Facebook search.
|
||||
* Fetches available jobs on mount and provides a "Search Facebook" CTA once a job is selected.
|
||||
*/
|
||||
export function JobSelector({ onSelect, onSearch, searching }: JobSelectorProps) {
|
||||
const [jobs, setJobs] = useState<Job[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [open, setOpen] = useState(false)
|
||||
const [selected, setSelected] = useState<Job | null>(null)
|
||||
|
||||
// ── Fetch available job categories on mount ──
|
||||
useEffect(() => {
|
||||
fetch("/api/ai/jobs")
|
||||
.then((r) => r.json())
|
||||
@@ -30,6 +43,7 @@ export function JobSelector({ onSelect, onSearch, searching }: JobSelectorProps)
|
||||
.finally(() => setLoading(false))
|
||||
}, [])
|
||||
|
||||
// ── Select a job, close dropdown, notify parent ──
|
||||
const handleSelect = (job: Job) => {
|
||||
setSelected(job)
|
||||
setOpen(false)
|
||||
@@ -38,6 +52,7 @@ export function JobSelector({ onSelect, onSearch, searching }: JobSelectorProps)
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
{/* ── Dropdown trigger button ── */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(!open)}
|
||||
@@ -50,8 +65,10 @@ export function JobSelector({ onSelect, onSearch, searching }: JobSelectorProps)
|
||||
{loading ? <Loader2 className="h-3.5 w-3.5 animate-spin text-primary" /> : <ChevronDown className={`h-3.5 w-3.5 text-muted-foreground/60 transition-transform duration-200 ${open ? "rotate-180" : ""}`} />}
|
||||
</button>
|
||||
|
||||
{/* ── Dropdown menu — visible when open ── */}
|
||||
{open && (
|
||||
<>
|
||||
{/* Backdrop to close on click outside */}
|
||||
<div className="fixed inset-0 z-10" onClick={() => setOpen(false)} />
|
||||
<div className="absolute top-full left-0 right-0 mt-1.5 z-20 bg-card border border-border rounded-xl shadow-xl max-h-60 overflow-y-auto">
|
||||
{jobs.map((job, i) => (
|
||||
@@ -71,6 +88,7 @@ export function JobSelector({ onSelect, onSearch, searching }: JobSelectorProps)
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{/* ── "Search Facebook" CTA — shown only when a job is selected ── */}
|
||||
{selected && onSearch && (
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user