103 lines
2.7 KiB
TypeScript
103 lines
2.7 KiB
TypeScript
|
|
/**
|
||
|
|
* Hook for Supabase Realtime Submissions
|
||
|
|
* Subscribes to submissions table changes and updates UI in realtime
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { useEffect, useCallback, useRef } from 'react';
|
||
|
|
import { RealtimeChannel } from '@supabase/supabase-js';
|
||
|
|
import { supabase } from './supabase';
|
||
|
|
|
||
|
|
export interface SubmissionRecord {
|
||
|
|
id: string;
|
||
|
|
submission_id: string;
|
||
|
|
status: 'pending' | 'processing' | 'completed' | 'failed';
|
||
|
|
input: any;
|
||
|
|
results: any[] | null;
|
||
|
|
error_message: string | null;
|
||
|
|
created_at: string;
|
||
|
|
updated_at: string;
|
||
|
|
submission_type: 'tms_permission' | 'raw_download';
|
||
|
|
}
|
||
|
|
|
||
|
|
interface UseRealtimeSubmissionsOptions {
|
||
|
|
onInsert?: (record: SubmissionRecord) => void;
|
||
|
|
onUpdate?: (record: SubmissionRecord) => void;
|
||
|
|
onDelete?: (record: SubmissionRecord) => void;
|
||
|
|
/** Optional: filter by submission_type */
|
||
|
|
submissionType?: 'tms_permission' | 'raw_download';
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Subscribe to realtime changes on submissions table
|
||
|
|
*/
|
||
|
|
export function useRealtimeSubmissions(options: UseRealtimeSubmissionsOptions = {}) {
|
||
|
|
const { onInsert, onUpdate, onDelete, submissionType } = options;
|
||
|
|
const channelRef = useRef<RealtimeChannel | null>(null);
|
||
|
|
|
||
|
|
const subscribe = useCallback(() => {
|
||
|
|
// Unsubscribe existing channel
|
||
|
|
if (channelRef.current) {
|
||
|
|
channelRef.current.unsubscribe();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Create filter if submissionType provided
|
||
|
|
const filter = submissionType ? `submission_type=eq.${submissionType}` : undefined;
|
||
|
|
|
||
|
|
const channel = supabase
|
||
|
|
.channel('submissions-changes')
|
||
|
|
.on(
|
||
|
|
'postgres_changes',
|
||
|
|
{
|
||
|
|
event: 'INSERT',
|
||
|
|
schema: 'public',
|
||
|
|
table: 'submissions',
|
||
|
|
filter,
|
||
|
|
},
|
||
|
|
(payload) => {
|
||
|
|
onInsert?.(payload.new as SubmissionRecord);
|
||
|
|
}
|
||
|
|
)
|
||
|
|
.on(
|
||
|
|
'postgres_changes',
|
||
|
|
{
|
||
|
|
event: 'UPDATE',
|
||
|
|
schema: 'public',
|
||
|
|
table: 'submissions',
|
||
|
|
filter,
|
||
|
|
},
|
||
|
|
(payload) => {
|
||
|
|
onUpdate?.(payload.new as SubmissionRecord);
|
||
|
|
}
|
||
|
|
)
|
||
|
|
.on(
|
||
|
|
'postgres_changes',
|
||
|
|
{
|
||
|
|
event: 'DELETE',
|
||
|
|
schema: 'public',
|
||
|
|
table: 'submissions',
|
||
|
|
filter,
|
||
|
|
},
|
||
|
|
(payload) => {
|
||
|
|
onDelete?.(payload.old as SubmissionRecord);
|
||
|
|
}
|
||
|
|
)
|
||
|
|
.subscribe();
|
||
|
|
|
||
|
|
channelRef.current = channel;
|
||
|
|
|
||
|
|
return () => {
|
||
|
|
channel.unsubscribe();
|
||
|
|
};
|
||
|
|
}, [onInsert, onUpdate, onDelete, submissionType]);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const cleanup = subscribe();
|
||
|
|
return cleanup;
|
||
|
|
}, [subscribe]);
|
||
|
|
|
||
|
|
return {
|
||
|
|
/** Manually resubscribe */
|
||
|
|
resubscribe: subscribe,
|
||
|
|
};
|
||
|
|
}
|