feat: add schedulers feature with task management
- Introduced SchedulersPage for managing scheduled tasks. - Implemented CreateTaskDialog for creating new scheduled tasks. - Added SchedulersHeader for filtering and searching tasks. - Created SchedulersTable to display scheduled tasks with actions. - Implemented loading and error states with SchedulersLoadingStates. - Added API service for task management in schedulers. - Enhanced date formatting utility to handle timezone. - Updated AppSidebar and AppRoutes to include SchedulersPage.
This commit is contained in:
@@ -4,3 +4,4 @@ export * from './player'
|
||||
export * from './files'
|
||||
export * from './extractions'
|
||||
export * from './favorites'
|
||||
export * from './schedulers'
|
||||
|
||||
201
src/lib/api/services/schedulers.ts
Normal file
201
src/lib/api/services/schedulers.ts
Normal file
@@ -0,0 +1,201 @@
|
||||
import { apiClient } from '../client'
|
||||
import type { ApiResponse } from '../types'
|
||||
|
||||
// Task types
|
||||
export type TaskType = 'CREDIT_RECHARGE' | 'PLAY_SOUND' | 'PLAY_PLAYLIST'
|
||||
|
||||
export type TaskStatus = 'PENDING' | 'RUNNING' | 'COMPLETED' | 'FAILED' | 'CANCELLED'
|
||||
|
||||
export type RecurrenceType = 'NONE' | 'HOURLY' | 'DAILY' | 'WEEKLY' | 'MONTHLY' | 'YEARLY' | 'CRON'
|
||||
|
||||
// Task interfaces
|
||||
export interface ScheduledTask {
|
||||
id: number
|
||||
name: string
|
||||
task_type: TaskType
|
||||
status: TaskStatus
|
||||
user_id: number | null
|
||||
scheduled_at: string
|
||||
timezone: string
|
||||
parameters: Record<string, unknown>
|
||||
recurrence_type: RecurrenceType
|
||||
cron_expression: string | null
|
||||
recurrence_count: number | null
|
||||
expires_at: string | null
|
||||
executions_count: number
|
||||
last_executed_at: string | null
|
||||
next_execution_at: string | null
|
||||
error_message: string | null
|
||||
is_active: boolean
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface CreateScheduledTaskRequest {
|
||||
name: string
|
||||
task_type: TaskType
|
||||
scheduled_at: string
|
||||
timezone?: string
|
||||
parameters?: Record<string, unknown>
|
||||
recurrence_type?: RecurrenceType
|
||||
cron_expression?: string | null
|
||||
recurrence_count?: number | null
|
||||
expires_at?: string | null
|
||||
}
|
||||
|
||||
export interface UpdateScheduledTaskRequest {
|
||||
name?: string
|
||||
scheduled_at?: string
|
||||
timezone?: string
|
||||
parameters?: Record<string, unknown>
|
||||
is_active?: boolean
|
||||
expires_at?: string | null
|
||||
}
|
||||
|
||||
export interface GetUserTasksParams {
|
||||
status?: TaskStatus
|
||||
task_type?: TaskType
|
||||
limit?: number
|
||||
offset?: number
|
||||
}
|
||||
|
||||
export interface GetAllTasksParams {
|
||||
status?: TaskStatus
|
||||
task_type?: TaskType
|
||||
limit?: number
|
||||
offset?: number
|
||||
}
|
||||
|
||||
// API service
|
||||
export const schedulersService = {
|
||||
// User tasks
|
||||
async getUserTasks(params?: GetUserTasksParams): Promise<ScheduledTask[]> {
|
||||
const searchParams = new URLSearchParams()
|
||||
|
||||
if (params?.status) searchParams.append('status', params.status)
|
||||
if (params?.task_type) searchParams.append('task_type', params.task_type)
|
||||
if (params?.limit) searchParams.append('limit', params.limit.toString())
|
||||
if (params?.offset) searchParams.append('offset', params.offset.toString())
|
||||
|
||||
const queryString = searchParams.toString()
|
||||
const endpoint = `/api/v1/scheduler/tasks${queryString ? `?${queryString}` : ''}`
|
||||
|
||||
return await apiClient.get<ScheduledTask[]>(endpoint)
|
||||
},
|
||||
|
||||
async getTask(taskId: number): Promise<ScheduledTask> {
|
||||
return await apiClient.get<ScheduledTask>(`/api/v1/scheduler/tasks/${taskId}`)
|
||||
},
|
||||
|
||||
async createTask(data: CreateScheduledTaskRequest): Promise<ScheduledTask> {
|
||||
return await apiClient.post<ScheduledTask>('/api/v1/scheduler/tasks', data)
|
||||
},
|
||||
|
||||
async updateTask(taskId: number, data: UpdateScheduledTaskRequest): Promise<ScheduledTask> {
|
||||
return await apiClient.patch<ScheduledTask>(`/api/v1/scheduler/tasks/${taskId}`, data)
|
||||
},
|
||||
|
||||
async cancelTask(taskId: number): Promise<ApiResponse> {
|
||||
return await apiClient.delete<ApiResponse>(`/api/v1/scheduler/tasks/${taskId}`)
|
||||
},
|
||||
|
||||
// Admin endpoints
|
||||
async getAllTasks(params?: GetAllTasksParams): Promise<ScheduledTask[]> {
|
||||
const searchParams = new URLSearchParams()
|
||||
|
||||
if (params?.status) searchParams.append('status', params.status)
|
||||
if (params?.task_type) searchParams.append('task_type', params.task_type)
|
||||
if (params?.limit) searchParams.append('limit', params.limit.toString())
|
||||
if (params?.offset) searchParams.append('offset', params.offset.toString())
|
||||
|
||||
const queryString = searchParams.toString()
|
||||
const endpoint = `/api/v1/scheduler/admin/tasks${queryString ? `?${queryString}` : ''}`
|
||||
|
||||
return await apiClient.get<ScheduledTask[]>(endpoint)
|
||||
},
|
||||
|
||||
async getSystemTasks(params?: { status?: TaskStatus; task_type?: TaskType }): Promise<ScheduledTask[]> {
|
||||
const searchParams = new URLSearchParams()
|
||||
|
||||
if (params?.status) searchParams.append('status', params.status)
|
||||
if (params?.task_type) searchParams.append('task_type', params.task_type)
|
||||
|
||||
const queryString = searchParams.toString()
|
||||
const endpoint = `/api/v1/scheduler/admin/system-tasks${queryString ? `?${queryString}` : ''}`
|
||||
|
||||
return await apiClient.get<ScheduledTask[]>(endpoint)
|
||||
},
|
||||
|
||||
async createSystemTask(data: CreateScheduledTaskRequest): Promise<ScheduledTask> {
|
||||
return await apiClient.post<ScheduledTask>('/api/v1/scheduler/admin/system-tasks', data)
|
||||
},
|
||||
}
|
||||
|
||||
// Utility functions
|
||||
export function getTaskTypeLabel(taskType: TaskType): string {
|
||||
switch (taskType) {
|
||||
case 'CREDIT_RECHARGE':
|
||||
return 'Credit Recharge'
|
||||
case 'PLAY_SOUND':
|
||||
return 'Play Sound'
|
||||
case 'PLAY_PLAYLIST':
|
||||
return 'Play Playlist'
|
||||
default:
|
||||
return taskType
|
||||
}
|
||||
}
|
||||
|
||||
export function getTaskStatusLabel(status: TaskStatus): string {
|
||||
switch (status) {
|
||||
case 'PENDING':
|
||||
return 'Pending'
|
||||
case 'RUNNING':
|
||||
return 'Running'
|
||||
case 'COMPLETED':
|
||||
return 'Completed'
|
||||
case 'FAILED':
|
||||
return 'Failed'
|
||||
case 'CANCELLED':
|
||||
return 'Cancelled'
|
||||
default:
|
||||
return status
|
||||
}
|
||||
}
|
||||
|
||||
export function getRecurrenceTypeLabel(recurrenceType: RecurrenceType): string {
|
||||
switch (recurrenceType) {
|
||||
case 'NONE':
|
||||
return 'None'
|
||||
case 'HOURLY':
|
||||
return 'Hourly'
|
||||
case 'DAILY':
|
||||
return 'Daily'
|
||||
case 'WEEKLY':
|
||||
return 'Weekly'
|
||||
case 'MONTHLY':
|
||||
return 'Monthly'
|
||||
case 'YEARLY':
|
||||
return 'Yearly'
|
||||
case 'CRON':
|
||||
return 'Custom'
|
||||
default:
|
||||
return recurrenceType
|
||||
}
|
||||
}
|
||||
|
||||
export function getTaskStatusVariant(status: TaskStatus): 'default' | 'secondary' | 'destructive' | 'outline' {
|
||||
switch (status) {
|
||||
case 'PENDING':
|
||||
return 'outline'
|
||||
case 'RUNNING':
|
||||
return 'default'
|
||||
case 'COMPLETED':
|
||||
return 'secondary'
|
||||
case 'FAILED':
|
||||
return 'destructive'
|
||||
case 'CANCELLED':
|
||||
return 'outline'
|
||||
default:
|
||||
return 'default'
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user