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

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>
);
};