Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ft/project management #358

Merged
merged 18 commits into from
Sep 4, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -14,6 +15,8 @@ export default function SelectBook({
setSelectedBooks,
scope,
existingScope = [],
disableScope = {},
call = '',
}) {
const [openNT, setOpenNT] = useState(true);
const [openOT, setOpenOT] = useState(true);
Expand Down Expand Up @@ -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}
</div>
Expand Down Expand Up @@ -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}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -23,6 +24,8 @@ export default function SelectVerse({
setVerseSelectActive,
setChapterNumber,
setVerseNumber,
scopedChapters,
call = '',
}) {
const [controlVerseSelect, setControlVerseSelect] = useState([]);
const [openChapter, setOpenChapter] = useState(true);
Expand Down Expand Up @@ -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}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -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 (
<button
type="button"
onClick={onClick}
disabled={disabled}
className={buttonClasses}
{...props}
>
{children}
</button>
);
}

export default BookButton;
25 changes: 25 additions & 0 deletions renderer/src/components/ProjectManagement/Common/Button/Button.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';

function Button({
children,
type = 'button',
onClick,
disabled = false,
...props
}) {
return (
<button
// eslint-disable-next-line react/button-has-type
type={type}
className="border border-primary/30 inline-flex items-center justify-center rounded-[4px] px-4 py-2
shadow-sm hover:bg-primary hover:text-white hover:font-medium "
onClick={onClick}
disabled={disabled}
{...props}
>
{children}
</button>
);
}

export default Button;
181 changes: 181 additions & 0 deletions renderer/src/components/ProjectManagement/ProjectManagement.js
Original file line number Diff line number Diff line change
@@ -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 (
<>
<Transition
show={open}
as={Fragment}
enter="transition duration-100 ease-out"
enterFrom="transform scale-95 opacity-0"
enterTo="transform scale-100 opacity-100"
leave="transition duration-75 ease-out"
leaveFrom="transform scale-100 opacity-100"
leaveTo="transform scale-95 opacity-0"
>
<Dialog
as="div"
className="fixed inset-0 z-10 overflow-y-auto"
initialFocus={cancelButtonRef}
static
open={open}
onClose={() => {}}
>
<Dialog.Overlay className="fixed inset-0 bg-black opacity-30" />
<div className="flex items-center justify-center h-screen">
<div className="w-[80vw] h-[80vh] max-w-[80vw] max-h-[80vh] items-center justify-center m-auto z-50 shadow overflow-hidden rounded">
<div className="relative h-full rounded shadow overflow-hidden bg-white flex flex-col">

<div className="flex justify-between items-center bg-secondary">
<div className="uppercase bg-secondary text-white py-2 px-2 text-xs tracking-widest leading-snug rounded-tl text-center flex gap-2">
<span>Project Management</span>
<span>:</span>
<span>{project?.name}</span>
</div>
<button
onClick={close}
type="button"
className="focus:outline-none"
>
<CloseIcon
className="h-6 w-7"
aria-hidden="true"
/>
</button>
</div>

<div className=" w-full h-full flex-1 flex flex-col overflow-y-scroll mb-5">

<div className="flex-grow-[5]">
{loading ? <LoadingSpinner /> : <ScopeManagement metadata={metadata} currentScope={currentScope} setCurrentScope={setCurrentScope} backendScope={backendScope} />}
</div>

<div className="h-[10%] flex justify-end items-center me-5">
<button
type="button"
onClick={close}
className="mr-5 bg-error w-28 h-8 border-color-error rounded
uppercase shadow text-white text-xs tracking-wide leading-4 font-light focus:outline-none"
>
{t('btn-cancel')}
</button>
<button
type="button"
aria-label="edit-language"
className=" bg-success w-28 h-8 border-color-success rounded uppercase text-white text-xs shadow focus:outline-none"
onClick={() => handleProject()}
>
Apply
</button>
</div>

</div>

</div>
</div>
</div>
</Dialog>
</Transition>
<SnackBar
openSnackBar={snackBar}
snackText={snackText}
setOpenSnackBar={setOpenSnackBar}
setSnackText={setSnackText}
error={notify}
/>
</>
);
}
ProjectMangement.propTypes = {
open: PropTypes.bool,
closePopUp: PropTypes.func,
project: PropTypes.object,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/* eslint-disable no-nested-ternary */
import React from 'react';
import BookButton from '../Common/Button/BookButton';
import XMark from '@/icons/Xelah/XMark.svg';

function BookItem({
book, handleSelectBook, handleRemoveScope, isInScope, disable,
}) {
return (
<div className="flex items-center">
<BookButton
className={`flex items-center gap-1.5 w-full border ${disable ? 'bg-gray-400' : (isInScope ? 'bg-primary/25' : '')}`}
onClick={(e) => handleSelectBook(e, book)}
>
<div
title={disable ? 'Auto-Selected' : isInScope ? 'Modify Chapters' : 'Add to scope'}
role="button"
tabIndex={-2}
className="flex-[3.5] truncate text-left"
>
{book.name}
</div>

<XMark
title="Remove Scope"
className={`flex-1 w-2 h-5 text-black hover:text-white ${disable ? 'opacity-0 pointer-events-none' : (isInScope ? 'visible' : 'opacity-0 pointer-events-none')}`}
onClick={(e) => handleRemoveScope(e, book)}
/>
</BookButton>
</div>
);
}

export default BookItem;
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';

function BulkSelectionGroup({
selectedOption = '',
handleSelect,
toggleOptions = [],
}) {
return (
<div className="px-2 h-8 flex gap-3 text-xs text-gray-800 items-center">
{toggleOptions.map((option) => (
<div key={option?.key} className="flex gap-1 items-center">
<input
type="radio"
value={option?.key}
className="pr-2"
checked={selectedOption === option?.key}
onChange={handleSelect}
/>
<label className="">
{option?.name}
</label>
</div>
))}
</div>
);
}

export default BulkSelectionGroup;
Loading
Loading