diff --git a/renderer/src/components/EditorPage/AudioEditor/AudioEditor.js b/renderer/src/components/EditorPage/AudioEditor/AudioEditor.js
index 793151c69..23d059a5c 100644
--- a/renderer/src/components/EditorPage/AudioEditor/AudioEditor.js
+++ b/renderer/src/components/EditorPage/AudioEditor/AudioEditor.js
@@ -66,7 +66,8 @@ const AudioEditor = ({ editor }) => {
const _books = [];
Object.entries(_data.type.flavorType.currentScope).forEach(
async ([key]) => {
- if (key === bookId.toUpperCase()) {
+ // Checking whether the selected book and chapter is in the scope or not
+ if (key === bookId.toUpperCase() && _data.type.flavorType.currentScope[key].includes(chapter)) {
_books.push(bookId.toUpperCase());
const fs = window.require('fs');
const path = require('path');
diff --git a/renderer/src/components/EditorPage/Navigation/reference/SelectBook.js b/renderer/src/components/EditorPage/Navigation/reference/SelectBook.js
index e0cde5603..40261083e 100644
--- a/renderer/src/components/EditorPage/Navigation/reference/SelectBook.js
+++ b/renderer/src/components/EditorPage/Navigation/reference/SelectBook.js
@@ -1,3 +1,4 @@
+/* eslint-disable no-nested-ternary */
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Disclosure, Transition } from '@headlessui/react';
@@ -14,6 +15,8 @@ export default function SelectBook({
setSelectedBooks,
scope,
existingScope = [],
+ disableScope = {},
+ call = '',
}) {
const [openNT, setOpenNT] = useState(true);
const [openOT, setOpenOT] = useState(true);
@@ -99,11 +102,13 @@ export default function SelectBook({
role="presentation"
key={book.name}
aria-label={`ot-${book.name}`}
- onClick={(e) => (
- multiSelectBook
+ onClick={(e) => (call === 'audio-project' ? (Object.prototype.hasOwnProperty.call(disableScope, (book.key).toUpperCase())
+ ? (multiSelectBook
? selectMultipleBooks(e, book.key, book.name)
- : bookSelect(e, book.key, book.name))}
- className={`${styles.bookSelect} ${selectedBooks.includes((book.key).toUpperCase()) ? styles.active : ''}`}
+ : bookSelect(e, book.key, book.name)) : '') : (multiSelectBook
+ ? selectMultipleBooks(e, book.key, book.name)
+ : bookSelect(e, book.key, book.name)))}
+ className={`${call === 'audio-project' && !Object.prototype.hasOwnProperty.call(disableScope, (book.key).toUpperCase()) ? styles.disabled : (selectedBooks.includes((book.key).toUpperCase()) ? (styles.bookSelect, styles.active) : styles.bookSelect)}`}
>
{book.name}
@@ -138,10 +143,13 @@ export default function SelectBook({
key={book.name}
role="presentation"
aria-label={`nt-${book.name}`}
- onClick={(e) => (multiSelectBook
- ? selectMultipleBooks(e, book.key, book.name)
- : bookSelect(e, book.key, book.name))}
- className={`${styles.bookSelect} ${selectedBooks.includes((book.key).toUpperCase()) ? styles.active : ''}`}
+ onClick={(e) => (call === 'audio-project' ? (Object.prototype.hasOwnProperty.call(disableScope, (book.key).toUpperCase())
+ ? (multiSelectBook
+ ? selectMultipleBooks(e, book.key, book.name)
+ : bookSelect(e, book.key, book.name)) : '') : (multiSelectBook
+ ? selectMultipleBooks(e, book.key, book.name)
+ : bookSelect(e, book.key, book.name)))}
+ className={`${call === 'audio-project' && !Object.prototype.hasOwnProperty.call(disableScope, (book.key).toUpperCase()) ? styles.disabled : (selectedBooks.includes((book.key).toUpperCase()) ? (styles.bookSelect, styles.active) : styles.bookSelect)}`}
>
{book.name}
diff --git a/renderer/src/components/EditorPage/Navigation/reference/SelectReference.module.css b/renderer/src/components/EditorPage/Navigation/reference/SelectReference.module.css
index 58c675f42..e1c09f06f 100644
--- a/renderer/src/components/EditorPage/Navigation/reference/SelectReference.module.css
+++ b/renderer/src/components/EditorPage/Navigation/reference/SelectReference.module.css
@@ -8,3 +8,7 @@
.active {
@apply py-1 px-2 bg-primary text-white cursor-pointer rounded sm:w-10/12 lg:w-5/12;
}
+
+.disabled {
+ @apply py-1 px-2 text-slate-400 rounded;
+}
\ No newline at end of file
diff --git a/renderer/src/components/EditorPage/Navigation/reference/SelectVerse.js b/renderer/src/components/EditorPage/Navigation/reference/SelectVerse.js
index 40fa0165a..ba564b1ce 100644
--- a/renderer/src/components/EditorPage/Navigation/reference/SelectVerse.js
+++ b/renderer/src/components/EditorPage/Navigation/reference/SelectVerse.js
@@ -1,3 +1,4 @@
+/* eslint-disable no-nested-ternary */
import { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { Disclosure, Transition } from '@headlessui/react';
@@ -23,6 +24,8 @@ export default function SelectVerse({
setVerseSelectActive,
setChapterNumber,
setVerseNumber,
+ scopedChapters,
+ call = '',
}) {
const [controlVerseSelect, setControlVerseSelect] = useState([]);
const [openChapter, setOpenChapter] = useState(true);
@@ -115,8 +118,8 @@ export default function SelectVerse({
key={chapter.key}
role="presentation"
id={`chapter-${chapter.name}`}
- onClick={(e) => { onChapterSelect(e, chapter.key); }}
- className={styles.select}
+ onClick={(e) => { call === 'audio-project' ? (scopedChapters.includes(chapter.name) ? onChapterSelect(e, chapter.key) : '') : onChapterSelect(e, chapter.key); }}
+ className={call === 'audio-project' ? (scopedChapters.includes(chapter.name) ? styles.select : styles.disabled) : styles.select}
>
{chapter.name}
diff --git a/renderer/src/components/ProjectManagement/Common/Button/BookButton.jsx b/renderer/src/components/ProjectManagement/Common/Button/BookButton.jsx
new file mode 100644
index 000000000..d06d90d33
--- /dev/null
+++ b/renderer/src/components/ProjectManagement/Common/Button/BookButton.jsx
@@ -0,0 +1,25 @@
+import React from 'react';
+
+function BookButton({
+ onClick,
+ children,
+ disabled = false,
+ className = '',
+ ...props
+}) {
+ const buttonClasses = `py-1 px-2 hover:bg-primary hover:text-white hover:font-bold cursor-pointer rounded text-left ${className} `;
+
+ return (
+
+ );
+}
+
+export default BookButton;
diff --git a/renderer/src/components/ProjectManagement/Common/Button/Button.jsx b/renderer/src/components/ProjectManagement/Common/Button/Button.jsx
new file mode 100644
index 000000000..9a32cbaf8
--- /dev/null
+++ b/renderer/src/components/ProjectManagement/Common/Button/Button.jsx
@@ -0,0 +1,25 @@
+import React from 'react';
+
+function Button({
+ children,
+ type = 'button',
+ onClick,
+ disabled = false,
+ ...props
+}) {
+ return (
+
+ );
+}
+
+export default Button;
diff --git a/renderer/src/components/ProjectManagement/ProjectManagement.js b/renderer/src/components/ProjectManagement/ProjectManagement.js
new file mode 100644
index 000000000..54d8cc5a1
--- /dev/null
+++ b/renderer/src/components/ProjectManagement/ProjectManagement.js
@@ -0,0 +1,181 @@
+/* eslint-disable no-useless-escape */
+import React, {
+ useRef, Fragment,
+ useEffect,
+ useCallback,
+ useState,
+} from 'react';
+import PropTypes from 'prop-types';
+import { Dialog, Transition } from '@headlessui/react';
+import { useTranslation } from 'react-i18next';
+import { SnackBar } from '@/components/SnackBar';
+// import { LoadingSpinner } from '@/components/LoadingSpinner';
+import CloseIcon from '@/illustrations/close-button-black.svg';
+import * as logger from '../../logger';
+import ScopeManagement from './scope-management/ScopeManagement';
+import { readProjectScope } from './utils/readProjectScope';
+import { LoadingSpinner } from '../LoadingSpinner';
+import { updateBurritoScope } from './utils/updateBurritoScope';
+
+export default function ProjectMangement(props) {
+ const {
+ open,
+ closePopUp,
+ project,
+ } = props;
+ const { t } = useTranslation();
+ const cancelButtonRef = useRef(null);
+ const [snackBar, setOpenSnackBar] = useState(false);
+ const [snackText, setSnackText] = useState('');
+ const [notify, setNotify] = useState();
+ const [currentScope, setCurrentScope] = useState({});
+ const [metadata, setMetadata] = useState('');
+ const [loading, setLoading] = useState(false);
+ const [backendScope, setBackendScope] = useState({});
+
+ const close = () => {
+ logger.debug('ProjectMangement.js', 'Closing the Dialog Box');
+ setOpenSnackBar(true);
+ closePopUp(false);
+ setMetadata({});
+ };
+
+ // load Metadata of the project
+ const getProjectMetadata = useCallback(async () => {
+ try {
+ setLoading(true);
+ const projectFullName = `${project?.name}_${project?.id?.[0]}`;
+ const projectMeta = await readProjectScope(projectFullName);
+ setMetadata(projectMeta.metadata);
+ setBackendScope(projectMeta.scope);
+ } catch (err) {
+ logger.error('ProjectMangement.js', `Read Meta : ${err}`);
+ } finally {
+ setLoading(false);
+ }
+ }, [project]);
+ function compareNumbers(a, b) {
+ return a - b;
+ }
+ const handleProject = () => {
+ logger.debug('ProjectMangement.js', 'Inside updateBurrito');
+ let mergedScope = currentScope;
+ // Merge both existing and new scope, if any scope difference exists
+ if (Object.keys(backendScope).length > 0) {
+ Object.entries(backendScope).forEach((book) => {
+ // Checking whether the book in scope is available in currentscope
+ if (currentScope[book[0]]) {
+ // merging the chapters of existing and selected books
+ const scopeSet = backendScope[book[0]];
+ const currentSet = currentScope[book[0]];
+ const arr = [...scopeSet, ...currentSet];
+ const mergedArr = [...new Set(arr)];
+ mergedScope = { ...mergedScope, [book[0]]: mergedArr.sort(compareNumbers) };
+ } else {
+ mergedScope = { ...mergedScope, [book[0]]: Object.values(backendScope[book[0]]) };
+ }
+ });
+ }
+ metadata.type.flavorType.currentScope = mergedScope;
+ const projectFullName = `${project?.name}_${project?.id?.[0]}`;
+ updateBurritoScope(projectFullName, metadata).then(() => {
+ setNotify('success');
+ setSnackText('Scope updated successfully!');
+ close();
+ });
+ };
+
+ useEffect(() => {
+ getProjectMetadata();
+ }, [getProjectMetadata]);
+
+ return (
+ <>
+
{children}
+ ); +} + +export default ScopeHead; diff --git a/renderer/src/components/ProjectManagement/scope-management/ScopeManagement.jsx b/renderer/src/components/ProjectManagement/scope-management/ScopeManagement.jsx new file mode 100644 index 000000000..87961a39a --- /dev/null +++ b/renderer/src/components/ProjectManagement/scope-management/ScopeManagement.jsx @@ -0,0 +1,292 @@ +import React, { useEffect, useState } from 'react'; +import { useBibleReference } from 'bible-reference-rcl'; +import ScopeHead from './ScopeHead'; +import TitleBar from './TitleBar'; +import BookButton from '../Common/Button/BookButton'; +import BulkSelectionGroup from './BulkSelectionGroup'; +import Button from '../Common/Button/Button'; +import BookItem from './BookItem'; +import * as logger from '../../../logger'; + +const initialBook = 'gen'; +const initialChapter = '1'; +const initialVerse = '1'; + +const ToggleBookOptions = [ + { key: 'all', name: 'All' }, + { key: 'old', name: 'Old' }, + { key: 'new', name: 'New' }, + { key: 'none', name: 'Deselect' }, +]; + +const ToggleChapterOptions = [ + { key: 'all', name: 'All' }, + { key: 'none', name: 'Deselect' }, +]; + +function ScopeManagement({ + metadata, currentScope, setCurrentScope, backendScope, +}) { + const [bookFilter, setBookFilter] = useState(''); + const [chapterFilter, setChapterFilter] = useState(''); + const [selectedChaptersSet, setSelectedChaptersSet] = useState(new Set([])); + + const { + state: { + // chapter, + // verse, + bookList, + chapterList, + // verseList, + bookName, + bookId, + }, actions: { + onChangeBook, + // onChangeChapter, + // onChangeVerse, + // applyBooksFilter, + }, + } = useBibleReference({ + initialBook, + initialChapter, + initialVerse, + }); + + const handleChangeBookToggle = (event) => { + setBookFilter(event.target.value); + const bookObj = {}; + if (event.target.value === 'all') { + bookList.forEach((book) => { + bookObj[book.key.toUpperCase()] = []; + }); + } else if (event.target.value === 'old') { + bookList?.slice(0, 39)?.forEach((book) => { + bookObj[book.key.toUpperCase()] = []; + }); + } else if (event.target.value === 'new') { + bookList?.slice(39)?.forEach((book) => { + bookObj[book.key.toUpperCase()] = []; + }); + } + const bookCode = Object.keys(bookObj)[0]; + onChangeBook(bookCode, bookCode); + setCurrentScope(bookObj); + }; + + const handleChangeChapterToggle = (event) => { + setChapterFilter(event.target.value); + let stringArray = []; + if (event.target.value === 'all') { + const numberArray = Array(chapterList.length).fill().map((_, idx) => 1 + idx); + stringArray = numberArray.map(String); + } + setCurrentScope((prev) => { + // check and change the selectedChapters + setSelectedChaptersSet(new Set(stringArray)); + return ({ ...prev, [bookId.toUpperCase()]: stringArray }); + }); + }; + + const handleSelectBook = (e, book) => { + if (bookFilter) { + setBookFilter(''); + } + const bookCode = book.key.toUpperCase(); + setCurrentScope((prev) => { + // check and change the selectedChapters + setSelectedChaptersSet(new Set(prev[bookCode]) || new Set([])); + return ({ ...prev, [bookCode]: prev[bookCode] || [] }); + }); + onChangeBook(book.key, book.key); + }; + + const handleChapterRangeSelection = (e) => { + e.preventDefault(); + let start = parseInt(e.target?.start?.value, 10) || null; + let end = parseInt(e.target?.end?.value, 10) || null; + // hanlde start greater and end smaller + if (start > end) { + const temp = start; + start = end; + end = temp; + } + const numberArray = Array(end - start + 1).fill().map((_, idx) => start + idx); + const stringArray = numberArray.map(String); + setCurrentScope((prev) => { + // check and change the selectedChapters + setSelectedChaptersSet(new Set(stringArray)); + return ({ ...prev, [bookId.toUpperCase()]: stringArray }); + }); + // e.target.start.value = ''; + // e.target.end.value = ''; + }; + + const handleRemoveScope = (e, book) => { + e.stopPropagation(); + if (bookFilter) { + setBookFilter(''); + } + const bukId = book.key.toUpperCase(); + const newScopeObj = { ...currentScope }; + delete newScopeObj[bukId]; + setCurrentScope(newScopeObj); + }; + + /** + * Fn to toggle chapter selection for the active book + */ + const handleChapterSelection = (e, chapter) => { + if (chapterFilter) { + setChapterFilter(''); + } + const bukId = bookId.toUpperCase(); + if (bukId in currentScope) { + setCurrentScope((prev) => { + const currentCh = new Set(prev[bukId] || new Set([])); + if (currentCh.has(chapter)) { + currentCh.delete(chapter); + } else { + currentCh.add(chapter); + } + setSelectedChaptersSet(currentCh); + return { + ...prev, + [bukId]: Array.from(currentCh), + }; + }); + } else { + logger.error('ScopeManagement.js', 'Active book is not in scope'); + } + }; + + // set current scope from meta + useEffect(() => { + if (metadata?.type?.flavorType?.currentScope) { + const scopeObj = metadata?.type?.flavorType?.currentScope; + // expect at least 1 scope - because creation and scope modification won't allow 0 scope + setSelectedChaptersSet(new Set(scopeObj[0]) || new Set([])); + const bookCode = Object.keys(scopeObj)[0].toUpperCase(); + onChangeBook(bookCode, bookCode); + setCurrentScope(scopeObj); + } else { + logger.error('ScopeManagement.js', 'Unable to read the scope from burrito'); + } + }, []); + + return ( +Book Selection
++ Chapter Selection : + {bookName} +
+