feat: add Auto Claim toggle with hourly time picker to AI column claim section
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -58,6 +58,14 @@ import {
|
|||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover";
|
} from "@/components/ui/popover";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Switch } from "@/components/ui/switch";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
import { useAppDispatch, useAppSelector } from "@/redux/hooks";
|
import { useAppDispatch, useAppSelector } from "@/redux/hooks";
|
||||||
import {
|
import {
|
||||||
clearTaskStatus,
|
clearTaskStatus,
|
||||||
@@ -167,6 +175,9 @@ export default function AppointmentsPage() {
|
|||||||
const [isClaimingColumn, setIsClaimingColumn] = useState(false);
|
const [isClaimingColumn, setIsClaimingColumn] = useState(false);
|
||||||
const [selectedClaimAiColumns, setSelectedClaimAiColumns] = useState<Set<number>>(new Set());
|
const [selectedClaimAiColumns, setSelectedClaimAiColumns] = useState<Set<number>>(new Set());
|
||||||
const [isClaimingAiColumn, setIsClaimingAiColumn] = useState(false);
|
const [isClaimingAiColumn, setIsClaimingAiColumn] = useState(false);
|
||||||
|
const [autoClaimEnabled, setAutoClaimEnabled] = useState(false);
|
||||||
|
const [autoClaimTime, setAutoClaimTime] = useState("19:00");
|
||||||
|
const autoClaimFiredHourRef = useRef<number | null>(null);
|
||||||
const [aiClaimModalOpen, setAiClaimModalOpen] = useState(false);
|
const [aiClaimModalOpen, setAiClaimModalOpen] = useState(false);
|
||||||
const [aiClaimQueue, setAiClaimQueue] = useState<ScheduledAppointment[]>([]);
|
const [aiClaimQueue, setAiClaimQueue] = useState<ScheduledAppointment[]>([]);
|
||||||
const [aiClaimCurrentIndex, setAiClaimCurrentIndex] = useState(0);
|
const [aiClaimCurrentIndex, setAiClaimCurrentIndex] = useState(0);
|
||||||
@@ -462,6 +473,26 @@ export default function AppointmentsPage() {
|
|||||||
}
|
}
|
||||||
}, [needsAiClaimResume, isLoadingAppointments]);
|
}, [needsAiClaimResume, isLoadingAppointments]);
|
||||||
|
|
||||||
|
// Auto-claim: fire handleClaimForColumnWithAi at the configured hour each day
|
||||||
|
useEffect(() => {
|
||||||
|
if (!autoClaimEnabled || !autoClaimTime) return;
|
||||||
|
const [targetHour] = autoClaimTime.split(":").map(Number);
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
const now = new Date();
|
||||||
|
const currentHour = now.getHours();
|
||||||
|
const currentMinute = now.getMinutes();
|
||||||
|
if (currentHour === targetHour && currentMinute === 0) {
|
||||||
|
if (autoClaimFiredHourRef.current !== currentHour) {
|
||||||
|
autoClaimFiredHourRef.current = currentHour;
|
||||||
|
handleClaimForColumnWithAi();
|
||||||
|
}
|
||||||
|
} else if (autoClaimFiredHourRef.current === currentHour && currentMinute !== 0) {
|
||||||
|
autoClaimFiredHourRef.current = null;
|
||||||
|
}
|
||||||
|
}, 30_000);
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [autoClaimEnabled, autoClaimTime, selectedClaimAiColumns]);
|
||||||
|
|
||||||
// Create/upsert appointment mutation
|
// Create/upsert appointment mutation
|
||||||
const createAppointmentMutation = useMutation({
|
const createAppointmentMutation = useMutation({
|
||||||
mutationFn: async (appointment: InsertAppointment) => {
|
mutationFn: async (appointment: InsertAppointment) => {
|
||||||
@@ -1784,7 +1815,7 @@ export default function AppointmentsPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Claim for Column with AI section */}
|
{/* Claim for Column with AI section */}
|
||||||
<div className="flex items-center gap-2 border rounded-md px-3 py-2 bg-white shadow-sm">
|
<div className="flex flex-wrap items-center gap-2 border rounded-md px-3 py-2 bg-white shadow-sm">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => handleClaimForColumnWithAi()}
|
onClick={() => handleClaimForColumnWithAi()}
|
||||||
disabled={isLoading || isClaimingAiColumn || selectedClaimAiColumns.size === 0}
|
disabled={isLoading || isClaimingAiColumn || selectedClaimAiColumns.size === 0}
|
||||||
@@ -1818,6 +1849,34 @@ export default function AppointmentsPage() {
|
|||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
))}
|
))}
|
||||||
|
<div className="flex items-center gap-2 border-l pl-3 ml-1">
|
||||||
|
<Switch
|
||||||
|
id="auto-claim-toggle"
|
||||||
|
checked={autoClaimEnabled}
|
||||||
|
onCheckedChange={setAutoClaimEnabled}
|
||||||
|
/>
|
||||||
|
<Label htmlFor="auto-claim-toggle" className="text-sm font-medium cursor-pointer whitespace-nowrap">
|
||||||
|
Auto Claim
|
||||||
|
</Label>
|
||||||
|
{autoClaimEnabled && (
|
||||||
|
<Select value={autoClaimTime} onValueChange={setAutoClaimTime}>
|
||||||
|
<SelectTrigger className="h-8 w-28 text-xs">
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{Array.from({ length: 24 }, (_, h) => {
|
||||||
|
const value = `${String(h).padStart(2, "0")}:00`;
|
||||||
|
const label = h === 0 ? "12 AM" : h < 12 ? `${h} AM` : h === 12 ? "12 PM" : `${h - 12} PM`;
|
||||||
|
return (
|
||||||
|
<SelectItem key={value} value={value}>
|
||||||
|
{label}
|
||||||
|
</SelectItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Text Reminder for Column section */}
|
{/* Text Reminder for Column section */}
|
||||||
|
|||||||
Reference in New Issue
Block a user