You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
170 lines
7.3 KiB
170 lines
7.3 KiB
import { useEffect, useState } from 'react';
|
|
import ReactQuill from 'react-quill';
|
|
import 'react-quill/dist/quill.snow.css';
|
|
import CodeMirror from '@uiw/react-codemirror';
|
|
import { html } from '@codemirror/lang-html';
|
|
import { javascript } from '@codemirror/lang-javascript';
|
|
import { php } from '@codemirror/lang-php';
|
|
import 'codemirror/lib/codemirror.css';
|
|
import 'codemirror/theme/material.css';
|
|
import PrimaryButton from '@/Components/PrimaryButton';
|
|
import { Code, Download, Minus, MinusCircle, Plus } from 'react-feather';
|
|
import FormGroup from '@/Components/FormGroup';
|
|
import InputLabel from '@/Components/InputLabel';
|
|
import TextInput from '@/Components/TextInput';
|
|
import AddFrameworkModal from '../AppFramework/AddModal';
|
|
import axios from 'axios';
|
|
|
|
interface InstructionsProps {
|
|
instruction_id: number;
|
|
onDelete: (instruction_id: number) => void;
|
|
onUpdate: (instruction_id: number, steps: any[], frameworkID: string) => void;
|
|
}
|
|
|
|
interface Framework {
|
|
id: number;
|
|
framework_name: string;
|
|
version: string;
|
|
}
|
|
|
|
export default function Instructions({ instruction_id, onDelete, onUpdate }: InstructionsProps) {
|
|
|
|
const [frameworkID, setFrameworkID] = useState('');
|
|
|
|
const [frameworks, setFrameworks] = useState<Framework[]>([]);
|
|
const fetchFrameworks = async () => {
|
|
try {
|
|
const response = await axios.get<Framework[]>(route('frameworks.index'));
|
|
setFrameworks(response.data);
|
|
} catch (error) {
|
|
console.error('Failed to fetch frameworks', error);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchFrameworks();
|
|
}, []);
|
|
|
|
const [isAddFrameworkModalOpen, setIsAddFrameworkModalOpen] = useState(false);
|
|
const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
|
const selectedValue = e.target.value;
|
|
setFrameworkID(selectedValue);
|
|
if (selectedValue === "0") {
|
|
setIsAddFrameworkModalOpen(true);
|
|
}
|
|
}
|
|
|
|
const [steps, setSteps] = useState<Array<{ title: string, content: string, code: string }>>([]);
|
|
|
|
const handleAddStep = () => {
|
|
setSteps([...steps, { title: '', content: '', code: '' }]);
|
|
};
|
|
|
|
const handleRemoveStep = (step_id: number) => {
|
|
setSteps(prevSteps => prevSteps.filter((_, index) => index !== step_id));
|
|
};
|
|
|
|
const handleTitleChange = (step_id: number, title: string) => {
|
|
const newSteps = [...steps];
|
|
newSteps[step_id].title = title;
|
|
setSteps(newSteps);
|
|
onUpdate(instruction_id, newSteps, frameworkID);
|
|
};
|
|
|
|
const handleContentChange = (step_id: number, content: string) => {
|
|
const newSteps = [...steps];
|
|
newSteps[step_id].content = content;
|
|
setSteps(newSteps);
|
|
onUpdate(instruction_id, newSteps, frameworkID);
|
|
};
|
|
|
|
const handleCodeChange = (step_id: number, code: string) => {
|
|
const newSteps = [...steps];
|
|
newSteps[step_id].code = code;
|
|
setSteps(newSteps);
|
|
onUpdate(instruction_id, newSteps, frameworkID);
|
|
};
|
|
|
|
return (
|
|
<div className="h-full w-full bg-neutral-10 shadow-md my-4 p-6 rounded-lg flex flex-col items-start">
|
|
<div className='flex w-full justify-between items-center'>
|
|
<h1 className="font-bold text-lg text-secondary-main flex items-center my-2">Instructions Editor</h1>
|
|
<button className="btn btn-link text-error-main p-0 no-underline" onClick={() => onDelete(instruction_id)}>
|
|
<Minus size={20} />
|
|
Remove Instruction
|
|
</button>
|
|
</div>
|
|
|
|
<div className='w-full'>
|
|
<FormGroup>
|
|
<InputLabel htmlFor="frameworkID"><Code className='stroke-neutral-10' /></InputLabel>
|
|
<label htmlFor="frameworkID" className="ml-2 w-1/4">Application Framework:</label>
|
|
<select name="frameworkID" id="frameworkID" value={frameworkID} onChange={handleChange}
|
|
className='px-2 py-0 flex-grow w-full h-[30px] bg-primary-background border border-primary-hover focus:outline-none focus:bg-neutral-10 focus:border focus:border-primary-hover'>
|
|
<option value="" selected disabled></option>
|
|
{frameworks.map(framework => (
|
|
<option key={framework.id} value={framework.id}>
|
|
{framework.framework_name}
|
|
</option>
|
|
))}
|
|
<option value="0" className="text-secondary-main font-semibold">Add new framework</option>
|
|
</select>
|
|
</FormGroup>
|
|
</div>
|
|
|
|
<div className='w-full my-4'>
|
|
{steps.map((step, step_id) => (
|
|
<div key={step_id}>
|
|
<div className='flex justify-between items-center'>
|
|
<div className='flex w-full items-center'>
|
|
<h3 className='font-semibold text-lg w-1/7'>Step {step_id + 1}</h3>
|
|
<TextInput
|
|
id='stepTitle'
|
|
name='stepTitle'
|
|
value={step.title}
|
|
placeholder='Step Title'
|
|
autoComplete='off'
|
|
required
|
|
onChange={(e) => handleTitleChange(step_id, e.target.value)}
|
|
className='w-1/4 mx-4'
|
|
/>
|
|
</div>
|
|
<button type="button" className="btn btn-link text-error-main p-0 no-underline" onClick={() => handleRemoveStep(step_id)}>
|
|
<Minus size={20} />
|
|
Remove Step
|
|
</button>
|
|
</div>
|
|
|
|
<p>Textual Instruction:</p>
|
|
<ReactQuill
|
|
value={step.content}
|
|
onChange={(content) => handleContentChange(step_id, content)}
|
|
className="h-1/2"
|
|
modules={{
|
|
toolbar: [
|
|
['bold', 'italic', 'underline'],
|
|
[{'list': 'ordered'}, {'list': 'bullet'}],
|
|
['link', 'image', 'video']
|
|
]
|
|
}}
|
|
/>
|
|
<p className='mt-2'>Code Snippet:</p>
|
|
<CodeMirror
|
|
value={step.code}
|
|
extensions={[php(), javascript(), html()]}
|
|
onChange={(value) => handleCodeChange(step_id, value)}
|
|
/>
|
|
<div className="divider divider-neutral-20"></div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<PrimaryButton type="button" onClick={handleAddStep} className='w-full'>
|
|
<Plus className='stroke-neutral-10' />
|
|
Add Steps
|
|
</PrimaryButton>
|
|
|
|
<AddFrameworkModal show={isAddFrameworkModalOpen} onClose={() => setIsAddFrameworkModalOpen(false)} onFrameworkAdded={fetchFrameworks} />
|
|
</div>
|
|
);
|
|
};
|