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.
285 lines
13 KiB
285 lines
13 KiB
import FormGroup from "@/Components/FormGroup";
|
|
import InputError from "@/Components/InputError";
|
|
import InputLabel from "@/Components/InputLabel";
|
|
import TextInput from "@/Components/TextInput";
|
|
import Authenticated from "@/Layouts/AuthenticatedLayout";
|
|
import { PageProps } from "@/types";
|
|
import { Head, Link, useForm } from "@inertiajs/react";
|
|
import { Book, Box, CheckCircle, FileText, Paperclip, Plus, PlusCircle, XCircle, XOctagon } from "react-feather";
|
|
import 'codemirror/lib/codemirror.css';
|
|
import 'codemirror/mode/javascript/javascript';
|
|
import PrimaryButton from "@/Components/PrimaryButton";
|
|
import Instructions from "./InstructionsForm";
|
|
import Modal from "@/Components/Modal";
|
|
import ModalButton from "@/Components/ModalButton";
|
|
import { FormEventHandler, useEffect, useState } from "react";
|
|
import AddCategoryModal from "../SubjectCategory/AddModal";
|
|
import axios from "axios";
|
|
import SuccessButton from "@/Components/SuccessButton";
|
|
import ErrorButton from "@/Components/ErrorButton";
|
|
import dayjs from "dayjs";
|
|
|
|
interface Category {
|
|
id: number;
|
|
subject_title: string;
|
|
description?: string;
|
|
}
|
|
|
|
export default function Create({ auth }: PageProps) {
|
|
const thisUser = auth.user;
|
|
|
|
const { data, setData, processing, errors } = useForm({
|
|
pageTitle: '',
|
|
category: '',
|
|
introduction:'',
|
|
});
|
|
|
|
const [isAddCategoryModalOpen, setIsAddCategoryModalOpen] = useState(false);
|
|
const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
|
const selectedValue = e.target.value;
|
|
setData('category', selectedValue);
|
|
if (selectedValue === "0") {
|
|
setIsAddCategoryModalOpen(true);
|
|
}
|
|
}
|
|
|
|
const [status, setStatus] = useState('');
|
|
const [feedback, setFeedback] = useState('');
|
|
const [isSuccessModalOpen, setIsSuccessModalOpen] = useState(false);
|
|
const [isErrorModalOpen, setIsErrorModalOpen] = useState(false);
|
|
const [categories, setCategories] = useState<Category[]>([]);
|
|
|
|
const fetchCategories = async () => {
|
|
try {
|
|
const response = await axios.get<Category[]>(route('categories.index'));
|
|
setCategories(response.data);
|
|
} catch (e) {
|
|
setStatus('Function Failure');
|
|
setFeedback('Failed to fetch categories');
|
|
setIsErrorModalOpen(true);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (thisUser.role_id !== 1) {
|
|
setStatus('Warning');
|
|
setFeedback('You are unauthorized to access this page');
|
|
setIsErrorModalOpen(true);
|
|
} else {
|
|
fetchCategories();
|
|
}
|
|
}, [thisUser.role_id]);
|
|
|
|
const handleCloseSuccessModal = () => {
|
|
setIsSuccessModalOpen(false);
|
|
window.location.href = '/dashboard';
|
|
}
|
|
|
|
const handleCloseErrorModal = () => {
|
|
setIsErrorModalOpen(false);
|
|
window.location.href = '/dashboard';
|
|
};
|
|
|
|
const [instructions, setInstructions] = useState<{ id: number; steps: any[]; frameworkID: string; }[]>([]);
|
|
const [nextId, setNextId] = useState<number>(0);
|
|
const handleAddInstruction = () => {
|
|
setInstructions([...instructions, { id: nextId, steps: [], frameworkID: '' }]);
|
|
setNextId(nextId + 1);
|
|
};
|
|
const handleRemoveInstruction = (id: number) => {
|
|
setInstructions(prevInstructions =>
|
|
prevInstructions.filter(instruction => instruction.id !== id)
|
|
);
|
|
};
|
|
const handleUpdateInstruction = (id: number, steps: any[], frameworkID: string) => {
|
|
setInstructions(prevInstructions =>
|
|
prevInstructions.map(instruction =>
|
|
instruction.id === id ? { ...instruction, steps, frameworkID } : instruction
|
|
)
|
|
);
|
|
};
|
|
|
|
const submit: FormEventHandler = async (e) => {
|
|
e.preventDefault();
|
|
|
|
if (data.category==="0" || data.category==="") {
|
|
alert('Please choose a valid category.')
|
|
return
|
|
}
|
|
|
|
try {
|
|
const response = await axios.post(route('page.add'), data);
|
|
const pageID = response.data.page.id;
|
|
|
|
await Promise.all(instructions.map(async (instruction) => {
|
|
if (instruction.frameworkID === "0" || instruction.frameworkID === "") {
|
|
alert('Please choose a valid framework for each instruction.');
|
|
return;
|
|
}
|
|
|
|
const timestamp = dayjs().format('YYYYMMDDHHmmss');
|
|
const jsonString = JSON.stringify({ steps: instruction.steps });
|
|
const blob = new Blob([jsonString], { type: 'application/json' });
|
|
const file = new File([blob], `${timestamp}.json`, { type: 'application/json' });
|
|
|
|
const formData = new FormData();
|
|
formData.append('file', file);
|
|
formData.append('frameworkID', instruction.frameworkID);
|
|
formData.append('pageID', pageID.toString());
|
|
|
|
await axios.post(route('instruction.add'), formData, {
|
|
headers: {
|
|
'Content-Type': 'multipart/form-data',
|
|
},
|
|
});
|
|
}));
|
|
|
|
setStatus('Success');
|
|
setFeedback(response.data.message);
|
|
setIsSuccessModalOpen(true);
|
|
} catch (error) {
|
|
setStatus('Error')
|
|
if (axios.isAxiosError(error)) {
|
|
setFeedback(error.response?.data.message || 'An error occurred');
|
|
} else {
|
|
setFeedback('An unexpected error occurred');
|
|
}
|
|
setIsErrorModalOpen(true);
|
|
}
|
|
}
|
|
|
|
const renderIfAdmin = () => {
|
|
if (thisUser.role_id === 1) {
|
|
return (
|
|
<div>
|
|
<div className="breadcrumbs text-sm">
|
|
<ul>
|
|
<li><Link href="/dashboard">Home</Link></li>
|
|
<li><a>Repository Pages</a></li>
|
|
<li>Add New Repository Page</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<h1 className="font-bold text-xl text-primary-main flex items-center my-2"><FileText className="stroke-primary-main mr-2" />Create Repository Page</h1>
|
|
<div className="divider"></div>
|
|
<form id="CreateRepositoryPageForm" onSubmit={submit}>
|
|
<div>
|
|
<FormGroup>
|
|
<InputLabel htmlFor="pageTitle"><Box className='stroke-neutral-10' /></InputLabel>
|
|
<label htmlFor="pageTitle" className="mx-2 font-semibold">Page Title:</label>
|
|
|
|
</FormGroup>
|
|
<TextInput
|
|
id='pageTitle'
|
|
name='pageTitle'
|
|
value={data.pageTitle}
|
|
autoComplete='off'
|
|
required
|
|
onChange={(e) => setData('pageTitle', e.target.value)}
|
|
/>
|
|
<InputError message={errors.pageTitle} className="mt-2" />
|
|
</div>
|
|
|
|
<div>
|
|
<FormGroup>
|
|
<InputLabel htmlFor="category"><Book className='stroke-neutral-10' /></InputLabel>
|
|
<label htmlFor="category" className="mx-2 font-semibold">Subject Category:</label>
|
|
</FormGroup>
|
|
<select name="category" id="category" value={data.category} 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>
|
|
{categories.map(category => (
|
|
<option key={category.id} value={category.id}>
|
|
{category.subject_title}
|
|
</option>
|
|
))}
|
|
<option value="0" className="text-secondary-main font-semibold">Add new framework</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<FormGroup>
|
|
<InputLabel htmlFor="description"><Paperclip className='stroke-neutral-10' /></InputLabel>
|
|
<label htmlFor="description" className="mx-2 font-semibold">Introduction:</label>
|
|
</FormGroup>
|
|
<textarea className='px-2 flex-grow w-full bg-primary-background border border-primary-hover focus:outline-none focus:bg-neutral-10 focus:border-2 focus:border-primary-hover '
|
|
onChange={(e) => setData('introduction', e.target.value)} />
|
|
</div>
|
|
{instructions.map((instruction) => (
|
|
<Instructions
|
|
key={instruction.id}
|
|
instruction_id={instruction.id}
|
|
onDelete={handleRemoveInstruction}
|
|
onUpdate={handleUpdateInstruction}
|
|
/>
|
|
))}
|
|
<PrimaryButton type='button' disabled={processing} onClick={handleAddInstruction} className="w-full">
|
|
<Plus className='stroke-neutral-10' />
|
|
Add Instruction
|
|
</PrimaryButton>
|
|
|
|
<div className="divider"></div>
|
|
<div className="flex">
|
|
<ErrorButton className="w-1/2" onClick={() => window.location.href = '/dashboard'}>
|
|
<XCircle className='stroke-neutral-10' />
|
|
Cancel
|
|
</ErrorButton>
|
|
<SuccessButton type="submit" className="w-1/2">
|
|
<PlusCircle className='stroke-neutral-10' />
|
|
Save Repository Page
|
|
</SuccessButton>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Authenticated user={thisUser}>
|
|
<Head title="Repository Page" />
|
|
|
|
<div id="CreateRepositoryPage" className="drawer lg:drawer-open">
|
|
<input id="my-drawer-2" type="checkbox" className="drawer-toggle" />
|
|
<div className="drawer-content flex flex-row justify-between p-6 bg-neutral-20">
|
|
<div className="h-full w-full bg-neutral-10 shadow-md p-6 rounded-lg">
|
|
{renderIfAdmin()}
|
|
</div>
|
|
</div>
|
|
<div className="drawer-side">
|
|
<label htmlFor="my-drawer-2" aria-label="close sidebar" className="drawer-overlay"></label>
|
|
|
|
<ul className="menu p-4 w-56 h-full bg-primary-background text-base-content">
|
|
{/* Sidebar content here */}
|
|
<li><a>Sidebar Item 1</a></li>
|
|
<li><a>Sidebar Item 2</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<AddCategoryModal show={isAddCategoryModalOpen} onClose={() => setIsAddCategoryModalOpen(false)} onCategoryAdded={fetchCategories} />
|
|
|
|
<Modal show={isSuccessModalOpen} onClose={() => setIsSuccessModalOpen(false)} maxWidth="lg" styling='success'>
|
|
<div className="p-4 flex flex-col items-center">
|
|
<CheckCircle className='stroke-success-main' size={80} />
|
|
<h2 className="text-xl font-bold mt-2">{ status }</h2>
|
|
<p className="mt-4">{ feedback }</p>
|
|
<ModalButton onClick={handleCloseSuccessModal} className="bg-success-main text-white hover:bg-success-hover active:bg-success-pressed">
|
|
Close
|
|
</ModalButton>
|
|
</div>
|
|
</Modal>
|
|
|
|
<Modal show={isErrorModalOpen} onClose={() => setIsErrorModalOpen(false)} maxWidth="lg" styling='error'>
|
|
<div className="p-4 flex flex-col items-center">
|
|
<XOctagon className='stroke-error-main' size={80} />
|
|
<h2 className="text-xl font-bold mt-2">{ status }</h2>
|
|
<p className="mt-4">{ feedback }</p>
|
|
<ModalButton onClick={handleCloseErrorModal} className="bg-error-main text-white hover:bg-error-hover active:bg-error-pressed">
|
|
Close
|
|
</ModalButton>
|
|
</div>
|
|
</Modal>
|
|
</Authenticated>
|
|
);
|
|
}
|