diff --git a/ice-bang-admin/src/App.tsx b/ice-bang-admin/src/App.tsx index 4ab5b09..5a54c2d 100644 --- a/ice-bang-admin/src/App.tsx +++ b/ice-bang-admin/src/App.tsx @@ -45,7 +45,8 @@ import { } from "./pages/user"; import{ WorkflowList, - WorkflowShow + WorkflowShow, + WorkflowCreate } from "./pages/workflows-list"; import { ForgotPassword } from "./pages/forgotPassword"; import { Login } from "./pages/login"; @@ -55,6 +56,9 @@ import { WorkflowsHistoryList } from "./pages/workflows-history"; import { WorkflowsHistoryShow } from "./pages/workflows-history"; import { MyPage } from "./pages/my-page"; +// 전역 스타일 import +import "./styles/global.css"; + function App() { return ( @@ -94,6 +98,7 @@ function App() { { name: "workflows_list", list: "/workflows-list", + create: "/workflows-list/create", show: "/workflows-list/show/:id", meta: { @@ -177,6 +182,7 @@ function App() { } /> + } /> } /> diff --git a/ice-bang-admin/src/pages/workflows-list/create.tsx b/ice-bang-admin/src/pages/workflows-list/create.tsx new file mode 100644 index 0000000..f3e6063 --- /dev/null +++ b/ice-bang-admin/src/pages/workflows-list/create.tsx @@ -0,0 +1,1083 @@ +import { Create, useForm } from "@refinedev/antd"; +import { + Form, + Input, + Switch, + Button, + Card, + Space, + Select, + Divider, + Row, + Col, + Typography, + Alert, + Tooltip +} from "antd"; +import { + PlusOutlined, + DeleteOutlined, + InfoCircleOutlined, + SettingOutlined, + ClockCircleOutlined +} from "@ant-design/icons"; +import { useState } from "react"; +import type { IWorkflowBackendDto, IJob, ITask } from "../../types/workflow"; + +const { Title, Text } = Typography; +const { TextArea } = Input; + +export const WorkflowCreate = () => { + const { formProps, saveButtonProps, form } = useForm({ + redirect: "show", + }); + + // 스케줄 관리를 위한 상태 + + type Schedule = { + id: number; + enabled: boolean; + description: string; + cronExpression: string; + frequency?: "daily" | "weekly"; + time?: string; + dayOfWeek?: number; + minute?: number; + }; + +// 상태 관리 + const [schedules, setSchedules] = useState([]); + + // Job 관리를 위한 상태 + const [jobs, setJobs] = useState([ + { + id: "job1", + type: "http", + task: [ + { + id: "task1", + parameters: { + url: "", + method: "GET", + headers: {}, + body: "" + } + } + ] + } + ]); + + // Job 타입 옵션 + const jobTypeOptions = [ + { value: "http", label: "HTTP 요청" }, + { value: "email", label: "이메일 발송" }, + { value: "database", label: "데이터베이스 작업" }, + { value: "file", label: "파일 처리" }, + { value: "custom", label: "커스텀 작업" } + ]; + + // 스케줄 추가 + const addSchedule = () => { + const newSchedule = { + id: schedules.length, + type: 'manual', + enabled: false, + description: '수동 실행', + cronExpression: '' + }; + + const updatedSchedules = [...schedules, newSchedule]; + setSchedules(updatedSchedules); + + // Form의 schedules 필드 업데이트 + form.setFieldValue('schedules', updatedSchedules); + }; + + // 스케줄 삭제 + const removeSchedule = (index: number) => { + const updatedSchedules = schedules.filter((_, i) => i !== index); + setSchedules(updatedSchedules); + + // Form의 schedules 필드 업데이트 + form.setFieldValue('schedules', updatedSchedules); + }; + + // Job 추가 + const addJob = () => { + const newJobId = `job${jobs.length + 1}`; + const newJob: IJob = { + id: newJobId, + type: "http", + task: [ + { + id: "task1", + parameters: { + url: "", + method: "GET", + headers: {}, + body: "" + } + } + ] + }; + + const updatedJobs = [...jobs, newJob]; + setJobs(updatedJobs); + + // Form의 config 필드 업데이트 + form.setFieldValue(['config', 'job'], updatedJobs); + }; + + // Job 삭제 + const removeJob = (index: number) => { + if (jobs.length <= 1) return; // 최소 1개는 유지 + + const updatedJobs = jobs.filter((_, i) => i !== index); + setJobs(updatedJobs); + + // Form의 config 필드 업데이트 + form.setFieldValue(['config', 'job'], updatedJobs); + }; + + // Job 타입 변경 + const updateJobType = (index: number, type: string) => { + const updatedJobs = [...jobs]; + updatedJobs[index].type = type; + + // 타입에 따른 기본 Task 파라미터 설정 + const defaultParameters = getDefaultTaskParameters(type); + updatedJobs[index].task[0].parameters = defaultParameters; + + setJobs(updatedJobs); + form.setFieldValue(['config', 'job'], updatedJobs); + }; + + // Job 타입별 기본 Task 파라미터 + const getDefaultTaskParameters = (type: string) => { + switch (type) { + case "http": + return { + url: "", + method: "GET", + headers: {}, + body: "" + }; + case "email": + return { + to: "", + subject: "", + content: "", + template: "" + }; + case "database": + return { + query: "", + database: "", + operation: "SELECT" + }; + case "file": + return { + path: "", + operation: "READ", + encoding: "UTF-8" + }; + case "custom": + return { + script: "", + language: "javascript", + timeout: 30000 + }; + default: + return {}; + } + }; + + // Task 파라미터 업데이트 + const updateTaskParameters = (jobIndex: number, parameters: any) => { + const updatedJobs = [...jobs]; + updatedJobs[jobIndex].task[0].parameters = parameters; + + setJobs(updatedJobs); + form.setFieldValue(['config', 'job'], updatedJobs); + }; + + // 스케줄 설명과 크론 표현식 업데이트 + const updateScheduleDescription = (scheduleIndex: number) => { + // 약간의 지연을 두어 폼 값이 업데이트된 후 실행 + setTimeout(() => { + const executionCycle = form.getFieldValue(`executionCycle_${scheduleIndex}`); + const executionTime = form.getFieldValue(`executionTime_${scheduleIndex}`); + const executionDayOfWeek = form.getFieldValue(`executionDayOfWeek_${scheduleIndex}`); + const executionHour = form.getFieldValue(`executionHour_${scheduleIndex}`); + const executionMinute = form.getFieldValue(`executionMinute_${scheduleIndex}`); + + let description = ''; + let cronExpression = ''; + + if (executionCycle === 'daily' && executionTime) { + const hour = executionTime.padStart(2, '0'); + description = `매일 ${hour}:00에 실행`; + cronExpression = `0 ${executionTime} * * *`; + } else if (executionCycle === 'weekly' && executionDayOfWeek && executionHour && executionMinute) { + const dayNames = ['일요일', '월요일', '화요일', '수요일', '목요일', '금요일', '토요일']; + const dayName = dayNames[parseInt(executionDayOfWeek)]; + const hourStr = String(executionHour).padStart(2, '0'); + const minuteStr = String(executionMinute).padStart(2, '0'); + + description = `매주 ${dayName} ${hourStr}:${minuteStr}에 실행`; + cronExpression = `${executionMinute} ${executionHour} * * ${executionDayOfWeek}`; + } + + // 폼 값 업데이트 + if (description && cronExpression) { + const currentSchedules = form.getFieldValue('schedules') || []; + const updatedSchedules = [...currentSchedules]; + updatedSchedules[scheduleIndex] = { + ...updatedSchedules[scheduleIndex], + description: description, + cronExpression: cronExpression + }; + + // 상태와 폼 값 동시 업데이트 + setSchedules(updatedSchedules); + form.setFieldsValue({ + schedules: updatedSchedules + }); + } + }, 100); + }; + + // Job별 Task 파라미터 렌더링 + const renderTaskParameters = (job: IJob, jobIndex: number) => { + const task = job.task[0]; // Task는 고정이므로 첫 번째만 사용 + + switch (job.type) { + case "http": + return ( +
+ + + + updateTaskParameters(jobIndex, { + ...task.parameters, + url: e.target.value + })} + placeholder="https://api.example.com/endpoint" + /> + + + + +