Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/actions/chapterAction.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ export const createChapter = (chapterData) => async (dispatch) => {
return response.data;
} catch (error) {
const errorResponseData = error.response?.data;

const errorMessage = errorResponseData?.message || error.message;

dispatch({
type: CREATE_CHAPTER_FAIL,
payload: errorMessage,
Expand All @@ -41,6 +41,7 @@ export const createChapter = (chapterData) => async (dispatch) => {
};



export const getChapters = (subjectName, standard) => async (dispatch) => {
try {
dispatch({ type: GET_CHAPTERS_REQUEST });
Expand Down
7 changes: 4 additions & 3 deletions src/actions/topicAction.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,20 @@ export const createTopic = (topicData) => async (dispatch) => {
payload: data,
});

return data;
return data;
} catch (error) {
const errorMessage = error.response?.data?.message || error.message;
const errorMessage = error.response?.data?.message || error.message || "An error occurred";

dispatch({
type: CREATE_TOPIC_FAIL,
payload: errorMessage,
});

return Promise.reject(errorMessage);
return Promise.reject({ message: errorMessage, status: error.response?.status });
}
};


export const getTopics = (subjectName, standard, chapterId) => async (dispatch) => {
try {
dispatch({ type: GET_TOPICS_REQUEST });
Expand Down
298 changes: 170 additions & 128 deletions src/pages/CreateChapter.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,144 +7,186 @@ import { createChapter } from "../actions/chapterAction";
import { getSubjects } from "../actions/subjectAction";

const CreateChapter = () => {
const dispatch = useDispatch();
const { isLoading } = useSelector((state) => state.chapter);
const { subjectList } = useSelector((state) => state.getSubject);
const dispatch = useDispatch();
const { isLoading } = useSelector((state) => state.chapter);
const { subjectList } = useSelector((state) => state.getSubject);

const [standard, setStandard] = useState("");
const [subject, setSubject] = useState("");
const [chapters, setChapters] = useState([{ name: "" }]);
const [isSubmitting, setIsSubmitting] = useState(false);
const [standard, setStandard] = useState("");
const [subject, setSubject] = useState("");
const [chapters, setChapters] = useState([{ name: "", chapterNumber: "" }]);
const [isSubmitting, setIsSubmitting] = useState(false);

useEffect(() => {
if (standard) {
dispatch(getSubjects(standard));
}
}, [standard, dispatch]);

const handleFormSubmit = async (event) => {
event.preventDefault();

if (isSubmitting) return;

setIsSubmitting(true);

const formattedData = {
subject: {
name: subject,
chapters,
standard: standard,
},
};

try {
const result = await dispatch(createChapter(formattedData));

if (result.success) {
toast.success('Chapter added successfully!');
setChapters([{ name: '' }]);
} else {
const errorMessage = result?.message || 'Failed to add chapter.';
toast.error(errorMessage);
}
} catch (error) {
toast.error(error);
} finally {
setIsSubmitting(false);
}
useEffect(() => {
if (standard) {
dispatch(getSubjects(standard.trim()));
}
}, [standard, dispatch]);

const handleFormSubmit = async (event) => {
event.preventDefault();

if (isSubmitting) return;

// Validate chapters array
if (!chapters.length) {
toast.error("Please add at least one chapter.");
return;
}

const seenChapters = new Set();
for (const chapter of chapters) {
const uniqueKey = `${chapter.name.trim().toLowerCase()}-${chapter.chapterNumber}`;
if (seenChapters.has(uniqueKey)) {
toast.error("Chapter names and chapter numbers must be unique for the same subject and standard.");
return;
}
seenChapters.add(uniqueKey);
}

// Validate standard and subject
if (!standard || !standard.trim()) {
toast.error("Please select a standard.");
return;
}

if (!subject || !subject.trim()) {
toast.error("Please select a subject.");
return;
}

setIsSubmitting(true);

const formattedData = {
subject: {
name: subject.trim(),
standard: standard.trim(),
chapters: chapters.map((chapter) => ({
name: chapter.name.trim(),
chapterNumber: parseInt(String(chapter.chapterNumber).trim(), 10),
topics: chapter.topics || [],
})),
},
};



try {
const result = await dispatch(createChapter(formattedData));
if (result.success) {
toast.success("Chapter added successfully!");
setChapters([{ name: "", chapterNumber: "", topics: [] }]);
} else {
toast.error(result.message || "Failed to add chapter.");
}
} catch (error) {
console.error("Error adding chapter:", error);
toast.error(error.message || "An error occurred while adding the chapter.");
} finally {
setIsSubmitting(false);
}
};



const handleChapterChange = (index, field, value) => {
const updatedChapters = [...chapters];
updatedChapters[index][field] = field === "chapterNumber" ? Number(value) : value.trim();
setChapters(updatedChapters);
};

const handleAddChapter = () => {
const lastChapter = chapters[chapters.length - 1];
if (lastChapter.name.trim() && lastChapter.chapterNumber !== "") {
setChapters([...chapters, { name: "", chapterNumber: "" }]);
} else {
toast.error("Please complete the current chapter details before adding a new one.");
}
};


const handleChapterChange = (index, value) => {
const updatedChapters = [...chapters];
updatedChapters[index].name = value;
setChapters(updatedChapters);
};
return (
<main className="p-4">
<h1 className="text-center m-10 text-white-600">Create Chapter</h1>
<form className="max-w-md mx-auto" onSubmit={handleFormSubmit}>
<div className="relative z-0 w-full mb-5 group flex flex-col-reverse">
<Select
showSearch
style={{ width: 200 }}
placeholder="Select Standard"
value={standard}
onChange={(value) => setStandard(value)}
options={standards}
/>
<label className="text-white-500 text-sm dark:text-white-400">Standard</label>
</div>

const handleAddChapter = () => {
const lastChapter = chapters[chapters.length - 1];
if (lastChapter.name.trim()) {
setChapters([...chapters, { name: "" }]);
} else {
toast.error("Please enter a chapter name before adding a new one.");
}
};
<div className="relative z-0 w-full mb-5 group flex flex-col-reverse">
<Select
showSearch
style={{ width: 200 }}
placeholder="Select Subject"
value={subject}
onChange={(value) => setSubject(value)}
options={subjectList?.map((name) => ({ value: name, label: name }))}
/>
<label className="text-white-500 text-sm dark:text-white-400">Subject</label>
</div>

return (
<main className="p-4">
<h1 className="text-center m-10 text-white-600">Create Chapter</h1>
<form className="max-w-md mx-auto" onSubmit={handleFormSubmit}>
<div className="relative z-0 w-full mb-5 group flex flex-col-reverse">
<Select
showSearch
style={{ width: 200 }}
placeholder="Select Standard"
value={standard}
onChange={(value) => setStandard(value)}
options={standards}
/>
<label className="text-white-500 text-sm dark:text-white-400">
Standard
</label>
</div>
<div className="relative z-0 w-full mb-5 group">
{chapters.map((chapter, index) => (
<div key={`chapter-${index}`} className="relative z-0 w-full mb-5 group">
<div className="flex gap-4">
<input
type="number"
name={`chapterNumber-${index}`}
id={`chapterNumber-${index}`}
className="block py-2.5 px-2 w-20 text-sm bg-transparent border border-gray-300 rounded-lg"
placeholder="Number"
value={chapter.chapterNumber || 0}
onChange={(e) =>
handleChapterChange(index, "chapterNumber", e.target.value)
}
min="0"
required
/>

<div className="relative z-0 w-full mb-5 group flex flex-col-reverse">
<Select
showSearch
style={{ width: 200 }}
placeholder="Select Subject"
value={subject}
onChange={(value) => setSubject(value)}
options={subjectList?.map((name) => ({ value: name, label: name }))}
/>
<label className="text-white-500 text-sm dark:text-white-400">
Subject
</label>
</div>
<input
type="text"
name={`chapterName-${index}`}
id={`chapterName-${index}`}
className="block py-2.5 px-2 w-full text-sm bg-transparent border border-gray-300 rounded-lg"
placeholder="Chapter Name"
value={chapter.name}
onChange={(e) =>
handleChapterChange(index, "name", e.target.value)
}
required
/>
</div>
</div>
))}

<div className="relative z-0 w-full mb-5 group">
{chapters.map((chapter, index) => (
<div
key={`chapter-${index}`}
className="relative z-0 w-full mb-5 group flex flex-col-reverse"
>
<input
type="text"
name={`chapter-${index}`}
id={`chapter-${index}`}
className="block py-2.5 px-0 w-full text-sm text-white-900 bg-transparent border-0 border-b-2 border-white-300 appearance-none dark:text-white dark:border-white-600 dark:focus:border-white-500 focus:outline-none focus:ring-0 focus:border-white-600 peer"
placeholder=" "
value={chapter.name}
onChange={(e) => handleChapterChange(index, e.target.value)}
/>
<label
htmlFor={`chapter-${index}`}
className="peer-focus:font-medium absolute text-sm text-white-500 dark:text-white-400 duration-300 transform -translate-y-6 scale-75 top-3 -z-10 origin-[0] peer-focus:start-0 rtl:peer-focus:translate-x-1/4 peer-focus:text-white-600 peer-focus:dark:text-blue-500 peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0 peer-focus:scale-75 peer-focus:-translate-y-6"
>
Chapter Name
</label>
</div>
))}
</div>
</div>

<div
className="border text-white-600 mb-10 rounded-xl h-10 text-sm flex items-center justify-center cursor-pointer"
onClick={handleAddChapter}
>
Add more chapters
</div>
<div
className="border text-white-600 mb-10 rounded-xl h-10 text-sm flex items-center justify-center cursor-pointer"
onClick={handleAddChapter}
>
Add more chapters
</div>

<button
type="submit"
disabled={isSubmitting || isLoading}
className={`text-white bg-blue-${isSubmitting || isLoading ? '400' : '700'} hover:bg-blue-${isSubmitting || isLoading ? '500' : '800'} focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800`}
>
{isSubmitting || isLoading ? "Submitting..." : "Submit"}
</button>
</form>
</main>
);
<button
type="submit"
disabled={isSubmitting || isLoading}
className={`text-white bg-blue-${
isSubmitting || isLoading ? "400" : "700"
} hover:bg-blue-${
isSubmitting || isLoading ? "500" : "800"
} focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800`}
>
{isSubmitting || isLoading ? "Submitting..." : "Submit"}
</button>
</form>
</main>
);
};

export default CreateChapter;
Loading