@inf-minds/jobs-ui 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/components/ConnectionStatus.d.ts +33 -0
  2. package/dist/components/ConnectionStatus.js +19 -0
  3. package/dist/components/CreateJobForm.d.ts +34 -0
  4. package/dist/components/CreateJobForm.js +45 -0
  5. package/dist/components/Dashboard.d.ts +56 -0
  6. package/dist/components/Dashboard.js +30 -0
  7. package/dist/components/JobCard.d.ts +30 -0
  8. package/dist/components/JobCard.js +21 -0
  9. package/dist/components/JobEventLog.d.ts +37 -0
  10. package/dist/components/JobEventLog.js +41 -0
  11. package/dist/components/JobFilters.d.ts +36 -0
  12. package/dist/components/JobFilters.js +21 -0
  13. package/dist/components/JobList.d.ts +35 -0
  14. package/dist/components/JobList.js +25 -0
  15. package/dist/components/JobProgress.d.ts +44 -0
  16. package/dist/components/JobProgress.js +35 -0
  17. package/dist/components/JobStatusBadge.d.ts +42 -0
  18. package/dist/components/JobStatusBadge.js +32 -0
  19. package/dist/components/index.d.ts +9 -0
  20. package/dist/components/index.js +11 -0
  21. package/dist/hooks/index.d.ts +4 -0
  22. package/dist/hooks/index.js +6 -0
  23. package/dist/hooks/use-connection.d.ts +34 -0
  24. package/dist/hooks/use-connection.js +66 -0
  25. package/dist/hooks/use-job-list.d.ts +70 -0
  26. package/dist/hooks/use-job-list.js +64 -0
  27. package/dist/hooks/use-job-stream.d.ts +58 -0
  28. package/dist/hooks/use-job-stream.js +73 -0
  29. package/dist/hooks/use-job.d.ts +56 -0
  30. package/dist/hooks/use-job.js +64 -0
  31. package/dist/index.d.ts +4 -0
  32. package/dist/index.js +8 -0
  33. package/dist/store/index.d.ts +2 -0
  34. package/dist/store/index.js +3 -0
  35. package/dist/store/job-stream-store.d.ts +23 -0
  36. package/dist/store/job-stream-store.js +161 -0
  37. package/dist/store/types.d.ts +72 -0
  38. package/dist/store/types.js +3 -0
  39. package/dist/styles/components.css +223 -0
  40. package/dist/styles/index.d.ts +25 -0
  41. package/dist/styles/index.js +27 -0
  42. package/dist/styles/preset.d.ts +19 -0
  43. package/dist/styles/preset.js +52 -0
  44. package/dist/types/index.d.ts +1 -0
  45. package/dist/types/index.js +3 -0
  46. package/dist/types/renderers.d.ts +55 -0
  47. package/dist/types/renderers.js +3 -0
  48. package/package.json +54 -0
@@ -0,0 +1,6 @@
1
+ // ABOUTME: Hooks module exports
2
+ // ABOUTME: Re-exports all React hooks for job management
3
+ export { useJobStream } from './use-job-stream.js';
4
+ export { useJob } from './use-job.js';
5
+ export { useJobList } from './use-job-list.js';
6
+ export { useConnection } from './use-connection.js';
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Options for useConnection hook.
3
+ */
4
+ export interface UseConnectionOptions {
5
+ /** WebSocket URL to connect to */
6
+ url: string;
7
+ /** Interval in milliseconds between reconnection attempts */
8
+ reconnectInterval?: number;
9
+ /** Callback when a message is received */
10
+ onMessage?: (data: unknown) => void;
11
+ }
12
+ /**
13
+ * Result of useConnection hook.
14
+ */
15
+ export interface UseConnectionResult {
16
+ /** Whether the WebSocket is connected */
17
+ connected: boolean;
18
+ /** Error message if connection failed */
19
+ error: string | null;
20
+ /** Send data through the WebSocket */
21
+ send: (data: unknown) => void;
22
+ }
23
+ /**
24
+ * Hook for managing WebSocket connections with auto-reconnect.
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * const { connected, error, send } = useConnection({
29
+ * url: 'ws://localhost:8080',
30
+ * onMessage: (data) => console.log('Received:', data),
31
+ * });
32
+ * ```
33
+ */
34
+ export declare function useConnection({ url, reconnectInterval, onMessage, }: UseConnectionOptions): UseConnectionResult;
@@ -0,0 +1,66 @@
1
+ // ABOUTME: WebSocket connection hook with auto-reconnect
2
+ // ABOUTME: Manages connection state, message handling, and reconnection logic
3
+ import { useState, useEffect, useCallback, useRef } from 'react';
4
+ /**
5
+ * Hook for managing WebSocket connections with auto-reconnect.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * const { connected, error, send } = useConnection({
10
+ * url: 'ws://localhost:8080',
11
+ * onMessage: (data) => console.log('Received:', data),
12
+ * });
13
+ * ```
14
+ */
15
+ export function useConnection({ url, reconnectInterval = 3000, onMessage, }) {
16
+ const [connected, setConnected] = useState(false);
17
+ const [error, setError] = useState(null);
18
+ const wsRef = useRef(null);
19
+ const reconnectRef = useRef(null);
20
+ const connect = useCallback(() => {
21
+ // Don't create multiple connections
22
+ if (wsRef.current?.readyState === WebSocket.OPEN)
23
+ return;
24
+ const ws = new WebSocket(url);
25
+ ws.onopen = () => {
26
+ setConnected(true);
27
+ setError(null);
28
+ };
29
+ ws.onmessage = (event) => {
30
+ try {
31
+ const data = JSON.parse(event.data);
32
+ onMessage?.(data);
33
+ }
34
+ catch (err) {
35
+ console.error('[WS] Parse error:', err);
36
+ }
37
+ };
38
+ ws.onclose = () => {
39
+ setConnected(false);
40
+ wsRef.current = null;
41
+ // Schedule reconnect
42
+ reconnectRef.current = setTimeout(connect, reconnectInterval);
43
+ };
44
+ ws.onerror = () => {
45
+ setError('Connection failed');
46
+ };
47
+ wsRef.current = ws;
48
+ }, [url, reconnectInterval, onMessage]);
49
+ useEffect(() => {
50
+ connect();
51
+ return () => {
52
+ // Clear any pending reconnect
53
+ if (reconnectRef.current) {
54
+ clearTimeout(reconnectRef.current);
55
+ }
56
+ // Close the WebSocket
57
+ wsRef.current?.close();
58
+ };
59
+ }, [connect]);
60
+ const send = useCallback((data) => {
61
+ if (wsRef.current?.readyState === WebSocket.OPEN) {
62
+ wsRef.current.send(JSON.stringify(data));
63
+ }
64
+ }, []);
65
+ return { connected, error, send };
66
+ }
@@ -0,0 +1,70 @@
1
+ import type { JobData } from './use-job.js';
2
+ /**
3
+ * Options for listing jobs.
4
+ */
5
+ export interface ListJobsOptions {
6
+ /** Account ID (usually required) */
7
+ accountId?: string;
8
+ /** Filter by job type */
9
+ type?: string;
10
+ /** Filter by status */
11
+ status?: string;
12
+ /** Page size */
13
+ limit?: number;
14
+ /** Offset for pagination */
15
+ offset?: number;
16
+ }
17
+ /**
18
+ * Result of listing jobs.
19
+ */
20
+ export interface ListJobsResult<T extends JobData = JobData> {
21
+ /** Jobs matching the query */
22
+ jobs: T[];
23
+ /** Total count for pagination */
24
+ total?: number;
25
+ }
26
+ /**
27
+ * Function to fetch jobs.
28
+ */
29
+ export type FetchJobsFn<T extends JobData = JobData> = (options: ListJobsOptions) => Promise<ListJobsResult<T>>;
30
+ /**
31
+ * Result of useJobList hook.
32
+ */
33
+ export interface UseJobListResult<T extends JobData = JobData> {
34
+ /** List of jobs */
35
+ jobs: T[];
36
+ /** Total count */
37
+ total: number;
38
+ /** Loading state */
39
+ isLoading: boolean;
40
+ /** Error if fetch failed */
41
+ error?: Error;
42
+ /** Refetch the list */
43
+ refetch: () => void;
44
+ }
45
+ /**
46
+ * React hook for fetching a list of jobs.
47
+ *
48
+ * @example
49
+ * ```tsx
50
+ * function JobList({ accountId }: { accountId: string }) {
51
+ * const { jobs, total, isLoading, refetch } = useJobList(
52
+ * (opts) => api.listJobs(opts),
53
+ * { accountId, limit: 10 }
54
+ * );
55
+ *
56
+ * if (isLoading) return <Spinner />;
57
+ *
58
+ * return (
59
+ * <div>
60
+ * <p>Showing {jobs.length} of {total} jobs</p>
61
+ * {jobs.map(job => (
62
+ * <JobRow key={job.id} job={job} />
63
+ * ))}
64
+ * <button onClick={refetch}>Refresh</button>
65
+ * </div>
66
+ * );
67
+ * }
68
+ * ```
69
+ */
70
+ export declare function useJobList<T extends JobData = JobData>(fetchJobs: FetchJobsFn<T>, options: ListJobsOptions): UseJobListResult<T>;
@@ -0,0 +1,64 @@
1
+ // ABOUTME: useJobList React hook for fetching a list of jobs
2
+ // ABOUTME: Provides pagination support and refetch capability
3
+ import { useState, useEffect, useCallback } from 'react';
4
+ /**
5
+ * React hook for fetching a list of jobs.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * function JobList({ accountId }: { accountId: string }) {
10
+ * const { jobs, total, isLoading, refetch } = useJobList(
11
+ * (opts) => api.listJobs(opts),
12
+ * { accountId, limit: 10 }
13
+ * );
14
+ *
15
+ * if (isLoading) return <Spinner />;
16
+ *
17
+ * return (
18
+ * <div>
19
+ * <p>Showing {jobs.length} of {total} jobs</p>
20
+ * {jobs.map(job => (
21
+ * <JobRow key={job.id} job={job} />
22
+ * ))}
23
+ * <button onClick={refetch}>Refresh</button>
24
+ * </div>
25
+ * );
26
+ * }
27
+ * ```
28
+ */
29
+ export function useJobList(fetchJobs, options) {
30
+ const [jobs, setJobs] = useState([]);
31
+ const [total, setTotal] = useState(0);
32
+ const [isLoading, setIsLoading] = useState(true);
33
+ const [error, setError] = useState();
34
+ // Stringify options for dependency comparison
35
+ const optionsKey = JSON.stringify(options);
36
+ const fetch = useCallback(async () => {
37
+ setIsLoading(true);
38
+ setError(undefined);
39
+ try {
40
+ const result = await fetchJobs(options);
41
+ setJobs(result.jobs);
42
+ setTotal(result.total ?? result.jobs.length);
43
+ }
44
+ catch (err) {
45
+ setError(err instanceof Error ? err : new Error(String(err)));
46
+ }
47
+ finally {
48
+ setIsLoading(false);
49
+ }
50
+ }, [fetchJobs, optionsKey]);
51
+ useEffect(() => {
52
+ fetch();
53
+ }, [fetch]);
54
+ const refetch = useCallback(() => {
55
+ fetch();
56
+ }, [fetch]);
57
+ return {
58
+ jobs,
59
+ total,
60
+ isLoading,
61
+ error,
62
+ refetch,
63
+ };
64
+ }
@@ -0,0 +1,58 @@
1
+ import type { StreamClient } from '@inf-minds/relay-client';
2
+ import type { JobStreamStatus, ExtractedProgress } from '../store/types.js';
3
+ /**
4
+ * Options for useJobStream hook.
5
+ */
6
+ export interface UseJobStreamOptions {
7
+ /** Starting offset (default: '-1') */
8
+ offset?: string;
9
+ /** Auto-start streaming (default: true) */
10
+ autoStart?: boolean;
11
+ /** Extract progress from custom events */
12
+ extractProgress?: (event: unknown) => ExtractedProgress | null;
13
+ }
14
+ /**
15
+ * Result of useJobStream hook.
16
+ */
17
+ export interface UseJobStreamResult<TEvent> {
18
+ /** Accumulated events */
19
+ events: TEvent[];
20
+ /** Current status */
21
+ status: JobStreamStatus;
22
+ /** Progress (0.0 - 1.0) */
23
+ progress: number;
24
+ /** Progress message */
25
+ progressMessage?: string;
26
+ /** True when at stream tail */
27
+ upToDate: boolean;
28
+ /** True when stream closed */
29
+ closed: boolean;
30
+ /** Error if any */
31
+ error?: Error;
32
+ /** Manually start streaming */
33
+ start: () => void;
34
+ /** Stop streaming */
35
+ stop: () => void;
36
+ }
37
+ /**
38
+ * React hook for streaming job events.
39
+ *
40
+ * @example
41
+ * ```tsx
42
+ * function JobViewer({ jobId }: { jobId: string }) {
43
+ * const { events, progress, status, closed } = useJobStream<MyEvent>(
44
+ * client,
45
+ * jobId
46
+ * );
47
+ *
48
+ * return (
49
+ * <div>
50
+ * <ProgressBar value={progress} />
51
+ * {events.map(e => <EventItem key={e.id} event={e} />)}
52
+ * {closed && <div>Job complete!</div>}
53
+ * </div>
54
+ * );
55
+ * }
56
+ * ```
57
+ */
58
+ export declare function useJobStream<TEvent = unknown>(client: StreamClient, streamId: string, options?: UseJobStreamOptions): UseJobStreamResult<TEvent>;
@@ -0,0 +1,73 @@
1
+ // ABOUTME: useJobStream React hook for streaming job events
2
+ // ABOUTME: Provides events, progress, and status with automatic cleanup
3
+ import { useState, useEffect, useCallback, useRef } from 'react';
4
+ import { createJobStreamStore } from '../store/job-stream-store.js';
5
+ /**
6
+ * React hook for streaming job events.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * function JobViewer({ jobId }: { jobId: string }) {
11
+ * const { events, progress, status, closed } = useJobStream<MyEvent>(
12
+ * client,
13
+ * jobId
14
+ * );
15
+ *
16
+ * return (
17
+ * <div>
18
+ * <ProgressBar value={progress} />
19
+ * {events.map(e => <EventItem key={e.id} event={e} />)}
20
+ * {closed && <div>Job complete!</div>}
21
+ * </div>
22
+ * );
23
+ * }
24
+ * ```
25
+ */
26
+ export function useJobStream(client, streamId, options = {}) {
27
+ const { offset, autoStart = true, extractProgress } = options;
28
+ // Create store ref to maintain instance across renders
29
+ const storeRef = useRef(null);
30
+ // Initialize store
31
+ if (!storeRef.current) {
32
+ storeRef.current = createJobStreamStore({
33
+ client,
34
+ extractProgress,
35
+ });
36
+ }
37
+ const store = storeRef.current;
38
+ // State from store
39
+ const [state, setState] = useState(store.getState());
40
+ // Subscribe to store changes
41
+ useEffect(() => {
42
+ const unsubscribe = store.subscribe(setState);
43
+ return unsubscribe;
44
+ }, [store]);
45
+ // Auto-start effect
46
+ useEffect(() => {
47
+ if (autoStart && streamId) {
48
+ store.actions.start(streamId, offset);
49
+ }
50
+ return () => {
51
+ store.actions.stop();
52
+ };
53
+ }, [store, streamId, offset, autoStart]);
54
+ // Manual start
55
+ const start = useCallback(() => {
56
+ store.actions.start(streamId, offset);
57
+ }, [store, streamId, offset]);
58
+ // Stop
59
+ const stop = useCallback(() => {
60
+ store.actions.stop();
61
+ }, [store]);
62
+ return {
63
+ events: state.events,
64
+ status: state.status,
65
+ progress: state.progress,
66
+ progressMessage: state.progressMessage,
67
+ upToDate: state.upToDate,
68
+ closed: state.closed,
69
+ error: state.error,
70
+ start,
71
+ stop,
72
+ };
73
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Generic job type (minimal interface for UI purposes).
3
+ */
4
+ export interface JobData {
5
+ id: string;
6
+ type: string;
7
+ status: string;
8
+ progress?: number | null;
9
+ error?: string | null;
10
+ createdAt: Date;
11
+ updatedAt: Date;
12
+ [key: string]: unknown;
13
+ }
14
+ /**
15
+ * Function to fetch a job by ID.
16
+ */
17
+ export type FetchJobFn<T extends JobData = JobData> = (jobId: string) => Promise<T | null>;
18
+ /**
19
+ * Result of useJob hook.
20
+ */
21
+ export interface UseJobResult<T extends JobData = JobData> {
22
+ /** The job data */
23
+ job: T | null;
24
+ /** Loading state */
25
+ isLoading: boolean;
26
+ /** Error if fetch failed */
27
+ error?: Error;
28
+ /** Refetch the job */
29
+ refetch: () => void;
30
+ }
31
+ /**
32
+ * React hook for fetching a single job.
33
+ *
34
+ * @example
35
+ * ```tsx
36
+ * function JobDetails({ jobId }: { jobId: string }) {
37
+ * const { job, isLoading, error, refetch } = useJob(
38
+ * (id) => api.getJob(id),
39
+ * jobId
40
+ * );
41
+ *
42
+ * if (isLoading) return <Spinner />;
43
+ * if (error) return <Error message={error.message} />;
44
+ * if (!job) return <NotFound />;
45
+ *
46
+ * return (
47
+ * <div>
48
+ * <h1>{job.type}</h1>
49
+ * <p>Status: {job.status}</p>
50
+ * <button onClick={refetch}>Refresh</button>
51
+ * </div>
52
+ * );
53
+ * }
54
+ * ```
55
+ */
56
+ export declare function useJob<T extends JobData = JobData>(fetchJob: FetchJobFn<T>, jobId: string): UseJobResult<T>;
@@ -0,0 +1,64 @@
1
+ // ABOUTME: useJob React hook for fetching a single job
2
+ // ABOUTME: Provides loading states and refetch capability
3
+ import { useState, useEffect, useCallback } from 'react';
4
+ /**
5
+ * React hook for fetching a single job.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * function JobDetails({ jobId }: { jobId: string }) {
10
+ * const { job, isLoading, error, refetch } = useJob(
11
+ * (id) => api.getJob(id),
12
+ * jobId
13
+ * );
14
+ *
15
+ * if (isLoading) return <Spinner />;
16
+ * if (error) return <Error message={error.message} />;
17
+ * if (!job) return <NotFound />;
18
+ *
19
+ * return (
20
+ * <div>
21
+ * <h1>{job.type}</h1>
22
+ * <p>Status: {job.status}</p>
23
+ * <button onClick={refetch}>Refresh</button>
24
+ * </div>
25
+ * );
26
+ * }
27
+ * ```
28
+ */
29
+ export function useJob(fetchJob, jobId) {
30
+ const [job, setJob] = useState(null);
31
+ const [isLoading, setIsLoading] = useState(true);
32
+ const [error, setError] = useState();
33
+ const fetch = useCallback(async () => {
34
+ if (!jobId) {
35
+ setJob(null);
36
+ setIsLoading(false);
37
+ return;
38
+ }
39
+ setIsLoading(true);
40
+ setError(undefined);
41
+ try {
42
+ const result = await fetchJob(jobId);
43
+ setJob(result);
44
+ }
45
+ catch (err) {
46
+ setError(err instanceof Error ? err : new Error(String(err)));
47
+ }
48
+ finally {
49
+ setIsLoading(false);
50
+ }
51
+ }, [fetchJob, jobId]);
52
+ useEffect(() => {
53
+ fetch();
54
+ }, [fetch]);
55
+ const refetch = useCallback(() => {
56
+ fetch();
57
+ }, [fetch]);
58
+ return {
59
+ job,
60
+ isLoading,
61
+ error,
62
+ refetch,
63
+ };
64
+ }
@@ -0,0 +1,4 @@
1
+ export { createJobStreamStore, type JobStreamStore, type JobStreamState, type JobStreamStatus, type JobStreamActions, type JobStreamListener, type CreateJobStreamStoreOptions, type ExtractedProgress, type Unsubscribe, } from './store/index.js';
2
+ export { useJobStream, type UseJobStreamOptions, type UseJobStreamResult, useJob, type UseJobResult, type FetchJobFn, type JobData, useJobList, type UseJobListResult, type FetchJobsFn, type ListJobsOptions, type ListJobsResult, useConnection, type UseConnectionOptions, type UseConnectionResult, } from './hooks/index.js';
3
+ export { JobProgress, type JobProgressProps, JobEventLog, type JobEventLogProps, JobStatusBadge, type JobStatusBadgeProps, type JobStatus, JobCard, type JobCardProps, JobList, type JobListProps, JobFilters, type JobFiltersProps, ConnectionStatus, type ConnectionStatusProps, CreateJobForm, type CreateJobFormProps, Dashboard, type DashboardProps, type DashboardSectionProps, } from './components/index.js';
4
+ export { type JobData as RendererJobData, type JobTypeRenderer, type JobRendererRegistry, } from './types/index.js';
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ // ABOUTME: jobs-ui package entry point
2
+ // ABOUTME: Exports headless store, React hooks, and optional components
3
+ // Store exports (headless - works without React)
4
+ export { createJobStreamStore, } from './store/index.js';
5
+ // React hooks
6
+ export { useJobStream, useJob, useJobList, useConnection, } from './hooks/index.js';
7
+ // React components (optional, tree-shakeable)
8
+ export { JobProgress, JobEventLog, JobStatusBadge, JobCard, JobList, JobFilters, ConnectionStatus, CreateJobForm, Dashboard, } from './components/index.js';
@@ -0,0 +1,2 @@
1
+ export { createJobStreamStore } from './job-stream-store.js';
2
+ export type { JobStreamStore, JobStreamState, JobStreamStatus, JobStreamActions, JobStreamListener, CreateJobStreamStoreOptions, ExtractedProgress, Unsubscribe, } from './types.js';
@@ -0,0 +1,3 @@
1
+ // ABOUTME: Store module exports
2
+ // ABOUTME: Re-exports JobStreamStore and related types
3
+ export { createJobStreamStore } from './job-stream-store.js';
@@ -0,0 +1,23 @@
1
+ import type { JobStreamStore, CreateJobStreamStoreOptions } from './types.js';
2
+ /**
3
+ * Creates a headless store for job stream state.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * const store = createJobStreamStore({ client });
8
+ *
9
+ * // Subscribe to state changes
10
+ * const unsubscribe = store.subscribe((state) => {
11
+ * console.log('Events:', state.events.length);
12
+ * console.log('Progress:', state.progress);
13
+ * });
14
+ *
15
+ * // Start streaming
16
+ * store.actions.start('job-123');
17
+ *
18
+ * // Later: stop and cleanup
19
+ * store.actions.stop();
20
+ * unsubscribe();
21
+ * ```
22
+ */
23
+ export declare function createJobStreamStore<TEvent = unknown>(options: CreateJobStreamStoreOptions): JobStreamStore<TEvent>;