diff --git a/src/common/Header.jsx b/src/common/Header.jsx index 4a2ff3e3..131dcdd5 100644 --- a/src/common/Header.jsx +++ b/src/common/Header.jsx @@ -230,7 +230,8 @@ const Header = () => { Tasks - {userData?.role == "ADMIN" && + {(userData?.role === "ADMIN" || userData?.role==="ORG_OWNER") && ( + @@ -241,7 +242,8 @@ const Header = () => { > Admin - } + + )} {/* { +// Tab Panel Component +function TabPanel(props) { const { children, value, index, ...other } = props; return ( @@ -31,18 +34,30 @@ const TabPanel = (props) => { {value === index && {children}} ); -}; +} const DashBoard = () => { const classes = DatasetStyle(); const navigate = useNavigate(); const dispatch = useDispatch(); - const [value, setValue] = useState(0); + // Fetch userData from Redux + const userData = useSelector((state) => state.getLoggedInUserDetails.data); + + const [value, setValue] = useState(0); const [addUserDialog, setAddUserDialog] = useState(false); const [newMemberEmail, setNewMemberEmail] = useState(""); const [openMemberDialog, setOpenMemberDialog] = useState(false); + // Fetch user data on component mount + useEffect(() => { + const fetchUserData = () => { + const loggedInUserObj = new FetchLoggedInUserDetailsAPI(); + dispatch(APITransport(loggedInUserObj)); + }; + fetchUserData(); + }, [dispatch]); + const addNewMemberHandler = async () => { const data = { role: "ORG_OWNER", @@ -55,146 +70,137 @@ const DashBoard = () => { setNewMemberEmail(""); }; + const handleTabChange = (event, newValue) => { + setValue(newValue); + }; + + const adminTabs = [ + { label: "Organizations", component: }, + { label: "Members", component: }, + { label: "Reports", component: }, + { label: "Newsletter", component: }, + { label: "Onboarding Requests", component: }, + ]; + + const orgOwnerTabs = [ + { label: "Video Task Details", component: }, + { label: "Task Details", component: }, + ]; + + const isAdmin = userData?.role === "ADMIN"; + const isOrgOwner = userData?.role === "ORG_OWNER"; + return ( - setValue(newValue)} - aria-label="basic tabs example" - > - - - - - + + {isAdmin && + adminTabs.map((tab, index) => ( + + ))} + + {isOrgOwner && + orgOwnerTabs.map((tab, index) => ( + + ))} - - - - - - - - - - - -
- -
-
-
-
- - - - - -
- -
-
-
- - - -
- -
-
-
- - - -
- -
-
-
- - - - -
- -
-
-
-
+ + {isAdmin && ( + <> + + + + + + + + + + + +
+ +
+
+
+
+ + + + + +
+ +
+
+
+ + + +
+ +
+
+
+ + + +
+ +
+
+
+ + + + +
+ +
+
+
+
+ + )} + + {isOrgOwner && ( + <> + + + + + + + + + + + + + )} +
+ {/* Dialogs */} {addUserDialog && ( { /> )} - {openMemberDialog && ( - setOpenMemberDialog(false)} - /> - )} + {openMemberDialog && setOpenMemberDialog(false)} />}
); }; -export default DashBoard; +export default DashBoard; \ No newline at end of file diff --git a/src/containers/Admin/TaskDetails.jsx b/src/containers/Admin/TaskDetails.jsx new file mode 100644 index 00000000..baf0d3d2 --- /dev/null +++ b/src/containers/Admin/TaskDetails.jsx @@ -0,0 +1,142 @@ +import React, { useState } from 'react'; +import { Grid, TextField, Button, Box, Typography, CircularProgress, Tabs, Tab } from '@mui/material'; +import { JSONTree } from 'react-json-tree'; +import GetTaskDetailsAPI from "redux/actions/api/Admin/GetTaskDetails.js"; +import { snakeToTitleCase } from '../../utils/utils.js'; + +function TaskDetails() { + const [taskId, setTaskId] = useState(''); + const [tabValue, setTabValue] = useState(0); + const [taskDetails, setTaskDetails] = useState(null); + const [loading, setLoading] = useState(false); + + const fetchTaskDetails = async () => { + setLoading(true); + setTaskDetails(null); + const apiObj = new GetTaskDetailsAPI(taskId); + fetch(apiObj.apiEndPoint(), apiObj.getHeaders()) + .then(async (res) => { + if (res.status === 200) { + const data = await res.json(); + setTaskDetails(data); + } else if (res.status === 404) { + setTaskDetails({ error: 'Task not found' }); + } else { + setTaskDetails({ error: 'Something went wrong' }); + } + setLoading(false); + }); + }; + + const theme = { + extend: { + base00: '#000', + base01: '#383830', + base02: '#49483e', + base03: '#75715e', + base04: '#a59f85', + base05: '#f8f8f2', + base06: '#f5f4f1', + base07: '#f9f8f5', + base08: '#f92672', + base09: '#fd971f', //orange + base0A: '#f4bf75', + base0B: '#a6e22e', //green + base0C: '#a1efe4', + base0D: '#66d9ef', + base0E: '#ae81ff', + base0F: '#cc6633', + }, + value: ({ style }, nodeType, keyPath) => ({ + style: { + ...style, + borderLeft: '2px solid #ccc', + marginLeft: '1.375em', + paddingLeft: '2em', + }, + }), + nestedNode: ({ style }, nodeType, keyPath) => ({ + style: { + ...style, + borderLeft: '2px solid #ccc', + marginLeft: keyPath.length > 1 ? '1.375em' : 0, + textIndent: '-0.375em', + }, + }), + arrowContainer: ({ style }, arrowStyle) => ({ + style: { + ...style, + paddingRight: '1.375rem', + textIndent: '0rem', + backgroundColor: 'white', + }, + }), + }; + + function TabPanel(props) { + const { children, value, index, ...other } = props; + + return ( + + ); + } + + return ( + + + + setTaskId(event.target.value)} + /> + + + + {loading && ( + + + + )} + {taskDetails && ( + <> + + setTabValue(v)} aria-label="task-details-tabs"> + + + + + + {typeof key === "string" ? snakeToTitleCase(key) : key}} + valueRenderer={(raw) => {typeof raw === "string" && raw.match(/^"(.*)"$/) ? raw.slice(1, -1) : raw}} + theme={theme} + /> + + + + )} + + ); +} + +export default TaskDetails; diff --git a/src/containers/Admin/VideoTaskDetails.jsx b/src/containers/Admin/VideoTaskDetails.jsx new file mode 100644 index 00000000..572c422f --- /dev/null +++ b/src/containers/Admin/VideoTaskDetails.jsx @@ -0,0 +1,231 @@ +import React, { useState } from 'react'; +import { Grid, TextField, Button, Tab, Tabs, Box, Typography, CircularProgress } from '@mui/material'; +import { JSONTree } from 'react-json-tree'; +import GetAllTranscriptionsAPI from "redux/actions/api/Admin/GetAllTranscriptions.js"; +import GetAllTranslationsAPI from "redux/actions/api/Admin/GetAllTranslations.js"; +import GetVideoTaskDetailsAPI from "redux/actions/api/Admin/GetVideoTaskDetails.js"; +import { snakeToTitleCase } from '../../utils/utils.js'; + +function VideoTaskDetails() { + const [videoId, setVideoId] = useState(''); + const [tabValue, setTabValue] = useState(0); + const [taskDetails, setTaskDetails] = useState(null); + const [transcriptions, setTranscriptions] = useState(null); + const [translations, setTranslations] = useState(null); + const [loading, setLoading] = useState(false); + const [loadingTranscriptions, setLoadingTranscriptions] = useState(false); + const [loadingTranslations, setLoadingTranslations] = useState(false); + + const fetchVideoTaskDetails = async () => { + setLoading(true); + setTaskDetails(null); + setTranscriptions(null); + setTranslations(null); + + const apiObj = new GetVideoTaskDetailsAPI(videoId); + fetch(apiObj.apiEndPoint(), apiObj.getHeaders()) + .then(async (res) => { + if (res.status === 200) { + const data = await res.json(); + return data; + } else if (res.status === 404) { + return { error: 'Task not found' }; + } else { + return { error: 'Something went wrong' }; + } + }) + .then(data => { + setLoading(false); + setTaskDetails(data); + fetchTranscriptions(); + fetchTranslations(); + }); + }; + + const fetchTranscriptions = async () => { + setLoadingTranscriptions(true); + const apiObj = new GetAllTranscriptionsAPI(videoId); + fetch(apiObj.apiEndPoint(), apiObj.getHeaders()) + .then(async (res) => { + if (res.status === 200) { + const data = await res.json(); + return data; + } else { + return { error: 'Failed to fetch transcriptions' }; + } + }) + .then(data => { + setTranscriptions(data.transcripts); + setLoadingTranscriptions(false); + }); + }; + + const fetchTranslations = async () => { + setLoadingTranslations(true); + const apiObj = new GetAllTranslationsAPI(videoId); + fetch(apiObj.apiEndPoint(), apiObj.getHeaders()) + .then(async (res) => { + if (res.status === 200) { + const data = await res.json(); + return data; + } else { + return { error: 'Failed to fetch translations' }; + } + }) + .then(data => { + setTranslations(data); + setLoadingTranslations(false); + }); + }; + + const theme = { + extend: { + base00: '#000', + base01: '#383830', + base02: '#49483e', + base03: '#75715e', + base04: '#a59f85', + base05: '#f8f8f2', + base06: '#f5f4f1', + base07: '#f9f8f5', + base08: '#f92672', + base09: '#fd971f', + base0A: '#f4bf75', + base0B: '#a6e22e', + base0C: '#a1efe4', + base0D: '#66d9ef', + base0E: '#ae81ff', + base0F: '#cc6633', + }, + value: ({ style }, nodeType, keyPath) => ({ + style: { + ...style, + borderLeft: '2px solid #ccc', + marginLeft: '1.375em', + paddingLeft: '2em', + }, + }), + nestedNode: ({ style }, nodeType, keyPath) => ({ + style: { + ...style, + borderLeft: '2px solid #ccc', + marginLeft: keyPath.length > 1 ? '1.375em' : 0, + textIndent: '-0.375em', + }, + }), + arrowContainer: ({ style }, arrowStyle) => ({ + style: { + ...style, + paddingRight: '1.375rem', + textIndent: '0rem', + backgroundColor: 'white', + }, + }), + }; + + function TabPanel(props) { + const { children, value, index, ...other } = props; + + return ( + + ); + } + + return ( + + + + setVideoId(event.target.value)} + /> + + + + {loading && ( + + + + )} + {taskDetails && ( + <> + + setTabValue(v)} aria-label="video-task-details-tabs"> + + + + + + + + + {typeof key === "string" ? snakeToTitleCase(key) : key}} + valueRenderer={(raw) => {typeof raw === "string" && raw.match(/^"(.*)"$/) ? raw.slice(1, -1) : raw}} + theme={theme} + /> + + + {loadingTranscriptions ? ( + + + + ) : transcriptions ? ( + {typeof key === "string" ? snakeToTitleCase(key) : key}} + valueRenderer={(raw) => {typeof raw === "string" && raw.match(/^"(.*)"$/) ? raw.slice(1, -1) : raw}} + theme={theme} + /> + ) : ( + No transcriptions available. + )} + + + {loadingTranslations ? ( + + + + ) : translations ? ( + {typeof key === "string" ? snakeToTitleCase(key) : key}} + valueRenderer={(raw) => {typeof raw === "string" && raw.match(/^"(.*)"$/) ? raw.slice(1, -1) : raw}} + theme={theme} + /> + ) : ( + No translations available. + )} + + + + )} + + ); +} + +export default VideoTaskDetails; diff --git a/src/redux/actions/api/Admin/GetAllTranscriptions.js b/src/redux/actions/api/Admin/GetAllTranscriptions.js new file mode 100644 index 00000000..48a6cae4 --- /dev/null +++ b/src/redux/actions/api/Admin/GetAllTranscriptions.js @@ -0,0 +1,43 @@ +import API from "../../../api"; +import ENDPOINTS from "../../../../config/apiendpoint"; +import constants from "../../../constants"; + +export default class GetAllTranscriptions extends API { + constructor(videoId, timeout = 2000) { + super("GET", timeout, false); + this.type = constants.GET_ALL_TRANSCRIPTIONS; + this.endpoint = `${super.apiEndPointAuto()}${ENDPOINTS.GetAllTranscriptions}?video_id=${videoId}`; + } + + processResponse(res) { + super.processResponse(res); + if (res) { + this.taskDetails = res; + } else { + this.taskDetails = { error: 'No data received' }; + } + } + + apiEndPoint() { + return this.endpoint; + } + + getHeaders() { + const token = localStorage.getItem('token'); + if (!token) { + console.warn('Authorization token not found in localStorage'); + } + + this.headers = { + headers: { + "Content-Type": "application/json", + "Authorization": `JWT ${token}`, + }, + }; + return this.headers; + } + + getPayload() { + return this.taskDetails; + } +} diff --git a/src/redux/actions/api/Admin/GetAllTranslations.js b/src/redux/actions/api/Admin/GetAllTranslations.js new file mode 100644 index 00000000..c535f824 --- /dev/null +++ b/src/redux/actions/api/Admin/GetAllTranslations.js @@ -0,0 +1,43 @@ +import API from "../../../api"; +import ENDPOINTS from "../../../../config/apiendpoint"; +import constants from "../../../constants"; + +export default class GetAllTranslationsAPI extends API { + constructor(videoId, timeout = 2000) { + super("GET", timeout, false); + this.type = constants.GET_ALL_TRANSLATIONS; + this.endpoint = `${super.apiEndPointAuto()}${ENDPOINTS.GetAllTranslations}?video_id=${videoId}`; + } + + processResponse(res) { + super.processResponse(res); + if (res) { + this.taskDetails = res; + } else { + this.taskDetails = { error: 'No data received' }; + } + } + + apiEndPoint() { + return this.endpoint; + } + + getHeaders() { + const token = localStorage.getItem('token'); + if (!token) { + console.warn('Authorization token not found in localStorage'); + } + + this.headers = { + headers: { + "Content-Type": "application/json", + "Authorization": `JWT ${token}`, + }, + }; + return this.headers; + } + + getPayload() { + return this.taskDetails; + } +} diff --git a/src/redux/actions/api/Admin/GetTaskDetails.js b/src/redux/actions/api/Admin/GetTaskDetails.js new file mode 100644 index 00000000..5516edf0 --- /dev/null +++ b/src/redux/actions/api/Admin/GetTaskDetails.js @@ -0,0 +1,47 @@ +/** + * GetTaskDetails + */ + +import API from "../../../api"; +import ENDPOINTS from "../../../../config/apiendpoint"; +import constants from "../../../constants"; + +export default class GetTaskDetailsAPI extends API { + constructor(taskId, timeout = 2000) { + super("GET", timeout, false); + this.type = constants.GET_TASK_DETAILS; + this.endpoint = `${super.apiEndPointAuto()}${ENDPOINTS.task}${taskId}/`; + } + + processResponse(res) { + super.processResponse(res); + if (res) { + this.taskDetails = res; + } + } + + apiEndPoint() { + return this.endpoint; + } + + getBody() {} + + getHeaders() { + const token = localStorage.getItem('token'); + if (!token) { + console.warn('Authorization token not found in localStorage'); + } + + this.headers = { + headers: { + "Content-Type": "application/json", + "Authorization": `JWT ${token}`, + }, + }; + return this.headers; + } + + getPayload() { + return this.taskDetails + } +} diff --git a/src/redux/actions/api/Admin/GetVideoTaskDetails.js b/src/redux/actions/api/Admin/GetVideoTaskDetails.js new file mode 100644 index 00000000..99e0bbb6 --- /dev/null +++ b/src/redux/actions/api/Admin/GetVideoTaskDetails.js @@ -0,0 +1,43 @@ +import API from "../../../api"; +import ENDPOINTS from "../../../../config/apiendpoint"; +import constants from "../../../constants"; + +export default class GetVideoTaskDetailsAPI extends API { + constructor(videoId, timeout = 2000) { + super("GET", timeout, false); + this.type = constants.GET_VIDEO_TASK_DETAILS; + this.endpoint = `${super.apiEndPointAuto()}${ENDPOINTS.getVideoTasks}?video_id=${videoId}`; + } + + processResponse(res) { + super.processResponse(res); + if (res) { + this.taskDetails = res; + } else { + this.taskDetails = { error: 'No data received' }; + } + } + + apiEndPoint() { + return this.endpoint; + } + + getHeaders() { + const token = localStorage.getItem('token'); + if (!token) { + console.warn('Authorization token not found in localStorage'); + } + + this.headers = { + headers: { + "Content-Type": "application/json", + "Authorization": `JWT ${token}`, + }, + }; + return this.headers; + } + + getPayload() { + return this.taskDetails; + } +} diff --git a/src/redux/constants.js b/src/redux/constants.js index 1298aa6b..abe48a61 100644 --- a/src/redux/constants.js +++ b/src/redux/constants.js @@ -72,6 +72,9 @@ const constants = { ONBOARDING: "ONBOARDING", //Task + GET_ALL_TRANSCRIPTIONS:"GET_ALL_TRANSCRIPTIONS", + GET_TASK_ANNOTATIONS:"GET_TASK_ANNOTATIONS", + GET_ALL_TRANSLATIONS:"GET_ALL_TRANSLATIONS", CREATE_NEW_TASk: "CREATE_NEW_TASk", GET_TASK_LIST: "GET_TASK_LIST", GET_TASK_DETAILS: "GET_TASK_DETAILS", @@ -137,6 +140,7 @@ const constants = { CREATE_MEMBER: "CREATE_MEMBER", UPDATE_ONBOARDING_FORM: "UPDATE_ONBOARDING_FORM", GET_ONBOARDING_LIST: "GET_ONBOARDING_LIST", + GET_VIDEO_TASK_DETAILS:"GET_VIDEO_TASK_DETAILS", //clear state CLEAR_STATE: "CLEAR_STATE", @@ -167,4 +171,4 @@ const constants = { GET_VOICEOVER_CHART: "GET_VOICEOVER_CHART", }; -export default constants; +export default constants; \ No newline at end of file