feat(p8): /v1/calendar REST + kernel-client methods + @insignia/iios-meeting-web

Task 8.6: CalendarController exposes schedule/request(+fromCallback/fromClaim)/
list/get/consent/transcript/summarize/action-items/providers(+sync) (session-auth).
kernel-client RestClient gains scheduleMeeting/requestMeeting/listMeetings/
getMeeting/setConsent/generateTranscript/summarizeMeeting/listActionItems/
connect+syncCalendarProvider + Meeting/MeetingParticipant/MeetingActionItem types.
New @insignia/iios-meeting-web (MeetingProvider + useMeetings/useMeeting/useSchedule/
useConsent/useSummarize/useActionItems). Boundary: meeting-web -> contracts +
kernel-client (fails on meeting-web->service).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-01 18:09:18 +05:30
parent 83369f2055
commit ca9498ce83
12 changed files with 366 additions and 1 deletions
+2
View File
@@ -0,0 +1,2 @@
export { MeetingProvider, useMeetings, useMeeting, useSchedule, useConsent, useSummarize, useActionItems } from './react';
export type { Meeting, MeetingParticipant, MeetingActionItem } from '@insignia/iios-kernel-client';
+97
View File
@@ -0,0 +1,97 @@
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { RestClient, type Meeting, type MeetingActionItem } from '@insignia/iios-kernel-client';
const MeetingContext = createContext<RestClient | null>(null);
export function MeetingProvider({
serviceUrl,
token,
children,
}: {
serviceUrl: string;
token: string;
children: React.ReactNode;
}): React.ReactElement {
const client = useMemo(() => new RestClient({ serviceUrl, token }), [serviceUrl, token]);
return <MeetingContext.Provider value={client}>{children}</MeetingContext.Provider>;
}
function useClient(): RestClient {
const c = useContext(MeetingContext);
if (!c) throw new Error('meeting hooks must be used within <MeetingProvider>');
return c;
}
export function useMeetings(opts?: { pollMs?: number }): { meetings: Meeting[]; refresh: () => Promise<void> } {
const client = useClient();
const [meetings, setMeetings] = useState<Meeting[]>([]);
const refresh = useCallback(async () => {
try {
setMeetings(await client.listMeetings());
} catch {
/* transient */
}
}, [client]);
useEffect(() => {
void refresh();
if (!opts?.pollMs) return;
const t = setInterval(() => void refresh(), opts.pollMs);
return () => clearInterval(t);
}, [refresh, opts?.pollMs]);
return { meetings, refresh };
}
export function useMeeting(id: string | null): { meeting: Meeting | null; refresh: () => Promise<void> } {
const client = useClient();
const [meeting, setMeeting] = useState<Meeting | null>(null);
const refresh = useCallback(async () => {
if (!id) return setMeeting(null);
try {
setMeeting(await client.getMeeting(id));
} catch {
/* transient */
}
}, [client, id]);
useEffect(() => {
void refresh();
}, [refresh]);
return { meeting, refresh };
}
export function useSchedule(): (input: Parameters<RestClient['scheduleMeeting']>[0]) => Promise<Meeting> {
const client = useClient();
return (input) => client.scheduleMeeting(input);
}
export function useConsent(): {
setConsent: (meetingId: string, actorRefId: string, status: string) => Promise<unknown>;
generateTranscript: (meetingId: string) => Promise<unknown>;
} {
const client = useClient();
return {
setConsent: (meetingId, actorRefId, status) => client.setConsent(meetingId, actorRefId, status),
generateTranscript: (meetingId) => client.generateTranscript(meetingId),
};
}
export function useSummarize(): (meetingId: string) => Promise<Meeting> {
const client = useClient();
return (meetingId) => client.summarizeMeeting(meetingId);
}
export function useActionItems(meetingId: string | null): { actionItems: MeetingActionItem[]; refresh: () => Promise<void> } {
const client = useClient();
const [actionItems, setActionItems] = useState<MeetingActionItem[]>([]);
const refresh = useCallback(async () => {
if (!meetingId) return setActionItems([]);
try {
setActionItems(await client.listActionItems(meetingId));
} catch {
/* transient */
}
}, [client, meetingId]);
useEffect(() => {
void refresh();
}, [refresh]);
return { actionItems, refresh };
}