intermittent commit
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useMemo, useState, type FormEvent } from "react";
|
||||
import { Bell, CalendarCheck, Check, Clock, Plus, RefreshCw, Send, XCircle } from "lucide-react";
|
||||
import { Button, hasScope, type ApiSettings, type AuthInfo } from "@govoplan/core-webui";
|
||||
import { Button, EmailAddressInput, hasScope, type ApiSettings, type AuthInfo, type MailboxAddress } from "@govoplan/core-webui";
|
||||
import {
|
||||
closeSchedulingRequest,
|
||||
createSchedulingCalendarEvent,
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
evaluateSchedulingFreeBusy,
|
||||
listSchedulingNotifications,
|
||||
listSchedulingRequests,
|
||||
lookupSchedulingAddresses,
|
||||
openSchedulingRequest,
|
||||
schedulingSummary,
|
||||
type SchedulingNotification,
|
||||
@@ -51,6 +52,8 @@ export default function SchedulingPage({ settings, auth }: { settings: ApiSettin
|
||||
{ label: "Option 1", start_at: localValue(defaultStart), end_at: localValue(defaultEnd) }
|
||||
]);
|
||||
const [participants, setParticipants] = useState<ParticipantDraft[]>([{ display_name: "", email: "", required: true }]);
|
||||
const [addressLookupQuery, setAddressLookupQuery] = useState("");
|
||||
const [addressSuggestions, setAddressSuggestions] = useState<MailboxAddress[]>([]);
|
||||
|
||||
const canWrite = hasScope(auth, "scheduling:schedule:write");
|
||||
const selected = useMemo(() => requests.find((item) => item.id === selectedId) || requests[0] || null, [requests, selectedId]);
|
||||
@@ -73,6 +76,37 @@ export default function SchedulingPage({ settings, auth }: { settings: ApiSettin
|
||||
void loadDetails(selected.id);
|
||||
}, [selected?.id]);
|
||||
|
||||
useEffect(() => {
|
||||
const query = addressLookupQuery.trim();
|
||||
if (!canWrite || query.length < 2) {
|
||||
setAddressSuggestions([]);
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
const timer = window.setTimeout(() => {
|
||||
void lookupSchedulingAddresses(settings, query).
|
||||
then((response) => {
|
||||
if (cancelled) return;
|
||||
if (!response.available) {
|
||||
setAddressSuggestions([]);
|
||||
return;
|
||||
}
|
||||
setAddressSuggestions(
|
||||
response.candidates
|
||||
.map((candidate) => candidate.email ? { name: candidate.display_name || undefined, email: candidate.email } : null)
|
||||
.filter((address): address is MailboxAddress => Boolean(address?.email))
|
||||
);
|
||||
}).
|
||||
catch(() => {
|
||||
if (!cancelled) setAddressSuggestions([]);
|
||||
});
|
||||
}, 200);
|
||||
return () => {
|
||||
cancelled = true;
|
||||
window.clearTimeout(timer);
|
||||
};
|
||||
}, [addressLookupQuery, canWrite, settings.accessToken, settings.apiBaseUrl, settings.apiKey]);
|
||||
|
||||
async function loadRequests() {
|
||||
setLoading(true);
|
||||
setError("");
|
||||
@@ -258,7 +292,22 @@ export default function SchedulingPage({ settings, auth }: { settings: ApiSettin
|
||||
{participants.map((participant, index) => (
|
||||
<div className="scheduling-participant-draft" key={index}>
|
||||
<input placeholder="Name" value={participant.display_name} onChange={(event) => updateParticipant(index, "display_name", event.target.value)} />
|
||||
<input placeholder="Email" value={participant.email} onChange={(event) => updateParticipant(index, "email", event.target.value)} />
|
||||
<EmailAddressInput
|
||||
value={participant.email ? [{ name: participant.display_name || undefined, email: participant.email }] : []}
|
||||
suggestions={addressSuggestions}
|
||||
onSuggestionQueryChange={setAddressLookupQuery}
|
||||
allowMultiple={false}
|
||||
disabled={!canWrite}
|
||||
compact
|
||||
addLabel="Add participant"
|
||||
emptyText="Participant email"
|
||||
onChange={(addresses) => {
|
||||
const address = addresses[0];
|
||||
updateParticipantDraft(index, {
|
||||
display_name: address?.name ?? participant.display_name,
|
||||
email: address?.email ?? ""
|
||||
});
|
||||
}} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -333,6 +382,10 @@ export default function SchedulingPage({ settings, auth }: { settings: ApiSettin
|
||||
function updateParticipant(index: number, field: keyof ParticipantDraft, value: string) {
|
||||
setParticipants((items) => items.map((item, itemIndex) => (itemIndex === index ? { ...item, [field]: value } : item)));
|
||||
}
|
||||
|
||||
function updateParticipantDraft(index: number, patch: Partial<ParticipantDraft>) {
|
||||
setParticipants((items) => items.map((item, itemIndex) => (itemIndex === index ? { ...item, ...patch } : item)));
|
||||
}
|
||||
}
|
||||
|
||||
function isRequestEnvelope(value: unknown): value is { request: SchedulingRequest } {
|
||||
|
||||
Reference in New Issue
Block a user