Replace native selects with shadcn Select across all pages
Build & Auto-Repair / build (push) Has been cancelled
Build & Auto-Repair / build (push) Has been cancelled
- lead-scoring-settings.tsx: Feature type dropdown - bug-report-modal.tsx: Severity dropdown - client-portal/support/page.tsx: Severity dropdown - time-tracking/page.tsx: Timer project + manual entry project dropdowns - proposals/new/page.tsx: Lead + customer dropdowns
This commit is contained in:
@@ -137,21 +137,29 @@ export default function NewProposalPage() {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium">Lead (optional)</label>
|
<label className="text-sm font-medium">Lead (optional)</label>
|
||||||
<select value={leadId} onChange={(e) => setLeadId(e.target.value)} className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm">
|
<Select value={leadId} onValueChange={setLeadId}>
|
||||||
<option value="">Select lead...</option>
|
<SelectTrigger className="h-9 w-full">
|
||||||
|
<SelectValue placeholder="Select lead..." />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
{leads.map((l: any) => (
|
{leads.map((l: any) => (
|
||||||
<option key={l.id} value={l.id}>{l.contact_name || l.company_name}</option>
|
<SelectItem key={l.id} value={l.id}>{l.contact_name || l.company_name}</SelectItem>
|
||||||
))}
|
))}
|
||||||
</select>
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium">Customer (optional)</label>
|
<label className="text-sm font-medium">Customer (optional)</label>
|
||||||
<select value={customerId} onChange={(e) => setCustomerId(e.target.value)} className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm">
|
<Select value={customerId} onValueChange={setCustomerId}>
|
||||||
<option value="">Select customer...</option>
|
<SelectTrigger className="h-9 w-full">
|
||||||
|
<SelectValue placeholder="Select customer..." />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
{customers.map((c: any) => (
|
{customers.map((c: any) => (
|
||||||
<option key={c.id} value={c.id}>{c.company_name || `${c.first_name} ${c.last_name}`}</option>
|
<SelectItem key={c.id} value={c.id}>{c.company_name || `${c.first_name} ${c.last_name}`}</SelectItem>
|
||||||
))}
|
))}
|
||||||
</select>
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium">VAT %</label>
|
<label className="text-sm font-medium">VAT %</label>
|
||||||
|
|||||||
@@ -6,6 +6,13 @@ import { Button } from "@/components/ui/button"
|
|||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/components/ui/input"
|
||||||
import { Card } from "@/components/ui/card"
|
import { Card } from "@/components/ui/card"
|
||||||
import { Badge } from "@/components/ui/badge"
|
import { Badge } from "@/components/ui/badge"
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select"
|
||||||
import { Play, Square, Plus, Trash2, Clock, Briefcase, User, TrendingUp, DollarSign } from "lucide-react"
|
import { Play, Square, Plus, Trash2, Clock, Briefcase, User, TrendingUp, DollarSign } from "lucide-react"
|
||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
|
|
||||||
@@ -29,7 +36,7 @@ export default function TimeTrackingPage() {
|
|||||||
const [showManual, setShowManual] = useState(false)
|
const [showManual, setShowManual] = useState(false)
|
||||||
|
|
||||||
// Manual entry form
|
// Manual entry form
|
||||||
const [manualProject, setManualProject] = useState("")
|
const [manualProject, setManualProject] = useState("none")
|
||||||
const [manualDesc, setManualDesc] = useState("")
|
const [manualDesc, setManualDesc] = useState("")
|
||||||
const [manualDate, setManualDate] = useState(new Date().toISOString().split("T")[0])
|
const [manualDate, setManualDate] = useState(new Date().toISOString().split("T")[0])
|
||||||
const [manualHours, setManualHours] = useState("")
|
const [manualHours, setManualHours] = useState("")
|
||||||
@@ -41,7 +48,7 @@ export default function TimeTrackingPage() {
|
|||||||
// Timer
|
// Timer
|
||||||
const [timerRunning, setTimerRunning] = useState(false)
|
const [timerRunning, setTimerRunning] = useState(false)
|
||||||
const [timerSeconds, setTimerSeconds] = useState(0)
|
const [timerSeconds, setTimerSeconds] = useState(0)
|
||||||
const [timerProject, setTimerProject] = useState("")
|
const [timerProject, setTimerProject] = useState("none")
|
||||||
const [timerDesc, setTimerDesc] = useState("")
|
const [timerDesc, setTimerDesc] = useState("")
|
||||||
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
||||||
const startTimeRef = useRef<Date | null>(null)
|
const startTimeRef = useRef<Date | null>(null)
|
||||||
@@ -92,7 +99,7 @@ export default function TimeTrackingPage() {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
projectId: timerProject || null,
|
projectId: timerProject === "none" ? null : timerProject,
|
||||||
description: timerDesc,
|
description: timerDesc,
|
||||||
date: new Date().toISOString().split("T")[0],
|
date: new Date().toISOString().split("T")[0],
|
||||||
startTime: startTimeRef.current?.toISOString(),
|
startTime: startTimeRef.current?.toISOString(),
|
||||||
@@ -103,7 +110,7 @@ export default function TimeTrackingPage() {
|
|||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
setTimerDesc("")
|
setTimerDesc("")
|
||||||
setTimerProject("")
|
setTimerProject("none")
|
||||||
setTimerSeconds(0)
|
setTimerSeconds(0)
|
||||||
fetchData()
|
fetchData()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -124,7 +131,7 @@ export default function TimeTrackingPage() {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
projectId: manualProject || null,
|
projectId: manualProject === "none" ? null : manualProject,
|
||||||
description: manualDesc,
|
description: manualDesc,
|
||||||
date: manualDate,
|
date: manualDate,
|
||||||
durationMinutes: totalMins,
|
durationMinutes: totalMins,
|
||||||
@@ -212,10 +219,15 @@ export default function TimeTrackingPage() {
|
|||||||
<div className="flex items-end gap-3">
|
<div className="flex items-end gap-3">
|
||||||
<div className="flex-1 space-y-2">
|
<div className="flex-1 space-y-2">
|
||||||
<Input placeholder="What are you working on?" value={timerDesc} onChange={(e) => setTimerDesc(e.target.value)} className="h-9" disabled={timerRunning} />
|
<Input placeholder="What are you working on?" value={timerDesc} onChange={(e) => setTimerDesc(e.target.value)} className="h-9" disabled={timerRunning} />
|
||||||
<select value={timerProject} onChange={(e) => setTimerProject(e.target.value)} className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm" disabled={timerRunning}>
|
<Select value={timerProject} onValueChange={setTimerProject} disabled={timerRunning}>
|
||||||
<option value="">No project</option>
|
<SelectTrigger className="h-9 w-full">
|
||||||
{projects.map((p: any) => <option key={p.id} value={p.id}>{p.name}</option>)}
|
<SelectValue placeholder="No project" />
|
||||||
</select>
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="none">No project</SelectItem>
|
||||||
|
{projects.map((p: any) => <SelectItem key={p.id} value={p.id}>{p.name}</SelectItem>)}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-3xl font-mono font-bold tabular-nums px-4 py-1">{formatTimer(timerSeconds)}</div>
|
<div className="text-3xl font-mono font-bold tabular-nums px-4 py-1">{formatTimer(timerSeconds)}</div>
|
||||||
{!timerRunning ? (
|
{!timerRunning ? (
|
||||||
@@ -238,10 +250,15 @@ export default function TimeTrackingPage() {
|
|||||||
<div className="space-y-3 p-3 rounded-lg border bg-muted/20">
|
<div className="space-y-3 p-3 rounded-lg border bg-muted/20">
|
||||||
<div className="grid gap-3 md:grid-cols-2">
|
<div className="grid gap-3 md:grid-cols-2">
|
||||||
<Input placeholder="Description *" value={manualDesc} onChange={(e) => setManualDesc(e.target.value)} className="h-8 text-sm" />
|
<Input placeholder="Description *" value={manualDesc} onChange={(e) => setManualDesc(e.target.value)} className="h-8 text-sm" />
|
||||||
<select value={manualProject} onChange={(e) => setManualProject(e.target.value)} className="flex h-8 w-full rounded-md border border-input bg-transparent px-3 text-sm shadow-sm">
|
<Select value={manualProject} onValueChange={setManualProject}>
|
||||||
<option value="">No project</option>
|
<SelectTrigger className="h-8 w-full">
|
||||||
{projects.map((p: any) => <option key={p.id} value={p.id}>{p.name}</option>)}
|
<SelectValue placeholder="No project" />
|
||||||
</select>
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="none">No project</SelectItem>
|
||||||
|
{projects.map((p: any) => <SelectItem key={p.id} value={p.id}>{p.name}</SelectItem>)}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-3 flex-wrap">
|
<div className="flex gap-3 flex-wrap">
|
||||||
<Input type="date" value={manualDate} onChange={(e) => setManualDate(e.target.value)} className="h-8 w-40 text-sm" />
|
<Input type="date" value={manualDate} onChange={(e) => setManualDate(e.target.value)} className="h-8 w-40 text-sm" />
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select"
|
||||||
import { LifeBuoy, Send } from "lucide-react"
|
import { LifeBuoy, Send } from "lucide-react"
|
||||||
|
|
||||||
interface Ticket {
|
interface Ticket {
|
||||||
@@ -107,16 +114,17 @@ export default function ClientSupport() {
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm text-[#8a8a95] mb-1">Severity</label>
|
<label className="block text-sm text-[#8a8a95] mb-1">Severity</label>
|
||||||
<select
|
<Select value={severity} onValueChange={setSeverity}>
|
||||||
value={severity}
|
<SelectTrigger className="w-full">
|
||||||
onChange={(e) => setSeverity(e.target.value)}
|
<SelectValue />
|
||||||
className="w-full bg-[#1a1a24] border border-[#2a2a35] rounded-lg px-3 py-2 text-sm text-white outline-none focus:border-[#1BB0CE]/50"
|
</SelectTrigger>
|
||||||
>
|
<SelectContent>
|
||||||
<option value="low">Low</option>
|
<SelectItem value="low">Low</SelectItem>
|
||||||
<option value="medium">Medium</option>
|
<SelectItem value="medium">Medium</SelectItem>
|
||||||
<option value="high">High</option>
|
<SelectItem value="high">High</SelectItem>
|
||||||
<option value="critical">Critical</option>
|
<SelectItem value="critical">Critical</SelectItem>
|
||||||
</select>
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{submitError && (
|
{submitError && (
|
||||||
|
|||||||
@@ -5,6 +5,13 @@ import { Button } from "@/components/ui/button"
|
|||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/components/ui/input"
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||||
import { Badge } from "@/components/ui/badge"
|
import { Badge } from "@/components/ui/badge"
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select"
|
||||||
import { Plus, Trash2, RefreshCw, Gauge } from "lucide-react"
|
import { Plus, Trash2, RefreshCw, Gauge } from "lucide-react"
|
||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
|
|
||||||
@@ -142,11 +149,16 @@ export function LeadScoringSettings() {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs font-medium">Type</label>
|
<label className="text-xs font-medium">Type</label>
|
||||||
<select value={newFeature.featureType} onChange={e => setNewFeature(p => ({ ...p, featureType: e.target.value }))} className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm">
|
<Select value={newFeature.featureType} onValueChange={v => setNewFeature(p => ({ ...p, featureType: v }))}>
|
||||||
<option value="keyword">Keyword Match</option>
|
<SelectTrigger className="h-9 w-full">
|
||||||
<option value="boolean">Boolean (has value)</option>
|
<SelectValue />
|
||||||
<option value="range">Range</option>
|
</SelectTrigger>
|
||||||
</select>
|
<SelectContent>
|
||||||
|
<SelectItem value="keyword">Keyword Match</SelectItem>
|
||||||
|
<SelectItem value="boolean">Boolean (has value)</SelectItem>
|
||||||
|
<SelectItem value="range">Range</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs font-medium">Weight</label>
|
<label className="text-xs font-medium">Weight</label>
|
||||||
|
|||||||
@@ -2,6 +2,13 @@
|
|||||||
|
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
import { motion, AnimatePresence } from "framer-motion"
|
import { motion, AnimatePresence } from "framer-motion"
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select"
|
||||||
import { X, Bug, Loader2, CheckCircle } from "lucide-react"
|
import { X, Bug, Loader2, CheckCircle } from "lucide-react"
|
||||||
|
|
||||||
interface BugReportModalProps {
|
interface BugReportModalProps {
|
||||||
@@ -151,16 +158,17 @@ export function BugReportModal({ open, onClose }: BugReportModalProps) {
|
|||||||
<label className="mb-1.5 block text-sm font-medium text-[#8A9078] dark:text-[#CCCCCC]">
|
<label className="mb-1.5 block text-sm font-medium text-[#8A9078] dark:text-[#CCCCCC]">
|
||||||
Severity
|
Severity
|
||||||
</label>
|
</label>
|
||||||
<select
|
<Select value={severity} onValueChange={setSeverity}>
|
||||||
value={severity}
|
<SelectTrigger className="w-full">
|
||||||
onChange={(e) => setSeverity(e.target.value)}
|
<SelectValue />
|
||||||
className="w-full rounded-lg border border-[#D4D8CC] dark:border-[#333333] bg-white dark:bg-[#1E1E1E] px-3 py-2 text-sm text-[#2D3020] dark:text-white focus:border-[#C84B4B] focus:outline-none focus:ring-1 focus:ring-[#C84B4B]"
|
</SelectTrigger>
|
||||||
>
|
<SelectContent>
|
||||||
<option value="low">Low — Minor cosmetic issue</option>
|
<SelectItem value="low">Low — Minor cosmetic issue</SelectItem>
|
||||||
<option value="medium">Medium — Affects functionality</option>
|
<SelectItem value="medium">Medium — Affects functionality</SelectItem>
|
||||||
<option value="high">High — Major feature broken</option>
|
<SelectItem value="high">High — Major feature broken</SelectItem>
|
||||||
<option value="critical">Critical — System is blocked</option>
|
<SelectItem value="critical">Critical — System is blocked</SelectItem>
|
||||||
</select>
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="rounded-lg bg-[#FAFAF6] dark:bg-[#1A1A1A] px-3 py-2">
|
<div className="rounded-lg bg-[#FAFAF6] dark:bg-[#1A1A1A] px-3 py-2">
|
||||||
|
|||||||
Reference in New Issue
Block a user