Skip to content

Commit

Permalink
feat: Transcription Management page
Browse files Browse the repository at this point in the history
Transcription Management page to handle the transcription process.
The page features two tables: one for the transcription backlog and
another for the transcription queue. Items from the backlog can be
added to the queue for transcription. After transcription starts, it
provides visibility on the state of active transcription jobs.

Note: There is currently no visibility on past transcription jobs.
  • Loading branch information
kouloumos committed Jul 17, 2024
1 parent d0f54c5 commit 3fa7c96
Show file tree
Hide file tree
Showing 12 changed files with 571 additions and 3 deletions.
11 changes: 11 additions & 0 deletions src/components/navbar/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { CgTranscript } from "react-icons/cg";
import { FaGithub } from "react-icons/fa";
import { FiUser, FiUsers } from "react-icons/fi";
import { HiOutlineBookOpen, HiOutlineSwitchHorizontal } from "react-icons/hi";
import { MdOutlineSource } from "react-icons/md";
import MenuNav from "./MenuNav";
import AdminMenu from "./AdminMenu";
import { useHasPermission } from "@/hooks/useHasPermissions";
Expand All @@ -34,6 +35,7 @@ const Menu = () => {
const canAccessAdminNav = useHasPermission("accessAdminNav");
const canAccessTransactions = useHasPermission("accessTransactions");
const canAccessUsers = useHasPermission("accessUsers");
const canAccessTranscription = useHasPermission("accessTranscription");
const router = useRouter();
const currentRoute = router.asPath?.split("/")[1] ?? "";
const fullCurrentRoute = router.asPath;
Expand Down Expand Up @@ -194,6 +196,15 @@ const Menu = () => {
icon={FiUsers}
/>
)}
{canAccessTranscription && (
<MenuNav
currentRoute={fullCurrentRoute}
routeName={"Transcription"}
routeLink={ROUTES_CONFIG.TRANSCRIPTION}
handleClose={closeMenu}
icon={MdOutlineSource}
/>
)}
</Flex>
</AdminMenu>
) : null}
Expand Down
9 changes: 6 additions & 3 deletions src/components/tables/TitleWithTags.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type TitleWithTagsProps = {
categories: string | string[];
loc?: string;
transcriptUrl?: string | null;
url?: string;
allTags: string[];
length: number;
shouldSlice?: boolean;
Expand All @@ -37,6 +38,7 @@ const TitleWithTags = ({
categories,
loc,
transcriptUrl = null,
url,
id,
length,
shouldSlice = true,
Expand All @@ -50,13 +52,14 @@ const TitleWithTags = ({
);
const tags = shouldSlice ? allTags.slice(0, 1) : allTags;
const transcript = resolveTranscriptUrl(transcriptUrl);
const hyperlink = transcript?.url || url
return (
<Td width="40%">
<Flex gap={2} flexDir="column">
<Box>
{!transcript && <Text>{title}</Text>}
{transcript && (
<Link target="_blank" rel="noopener" href={transcript.url}>
{!hyperlink && <Text>{title}</Text>}
{hyperlink && (
<Link target="_blank" rel="noopener" href={hyperlink}>
<Text>{title}</Text>
</Link>
)}
Expand Down
49 changes: 49 additions & 0 deletions src/components/tables/components/SelectSourceMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {
Button,
Flex,
Menu,
MenuButton,
MenuItem,
MenuList,
Text,
} from "@chakra-ui/react";
import { MdArrowDropDown } from "react-icons/md";

const SelectSourceMenu = ({
isLoading,
sources,
onSelection: selectSource,
}: {
isLoading?: boolean;
sources?: string[];
onSelection: (source: string) => void;
}) => (
<Menu>
<MenuButton
as={Button}
isLoading={isLoading}
aria-label="select source"
colorScheme="orange"
>
<Flex gap={2} alignItems={"center"}>
<Text> Select Source </Text>
<MdArrowDropDown size={20} />
</Flex>
</MenuButton>
<MenuList color="black">
{sources &&
sources.map((source) => (
<MenuItem
key={source}
onClick={() => {
selectSource(source);
}}
>
{source}
</MenuItem>
))}
</MenuList>
</Menu>
);

export default SelectSourceMenu;
1 change: 1 addition & 0 deletions src/components/tables/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as SelectSourceMenu } from "./SelectSourceMenu"
2 changes: 2 additions & 0 deletions src/config/permissions.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"accessUsers": true,
"resetReviews": true,
"archiveTranscripts": true,
"accessTranscription": true,
"accessTransactions": true,
"accessAdminNav": true,
"submitToOwnRepo": true
Expand All @@ -13,6 +14,7 @@
"accessUsers": false,
"resetReviews": false,
"archiveTranscripts": true,
"accessTranscription": true,
"accessTransactions": false,
"accessAdminNav": true,
"submitToOwnRepo": false
Expand Down
1 change: 1 addition & 0 deletions src/config/ui-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const ROUTES_CONFIG = {
ALL_REVIEWS: "admin/reviews?status=active",
REVIEWS: "reviews",
USERS: "admin/users",
TRANSCRIPTION: "admin/transcription"
};

export const UI_CONFIG = {
Expand Down
1 change: 1 addition & 0 deletions src/hooks/useHasPermissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import permissions from "../config/permissions.json";
type Permissions = {
accessReviews: boolean;
accessUsers: boolean;
accessTranscription: boolean;
resetReviews: boolean;
archiveTranscripts: boolean;
accessTransactions: boolean;
Expand Down
192 changes: 192 additions & 0 deletions src/pages/admin/transcription.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import { useState } from "react";
import { Button, Flex, Heading, Text } from "@chakra-ui/react";

import { useHasPermission } from "@/hooks/useHasPermissions";
import AuthStatus from "@/components/transcript/AuthStatus";
import {
useTranscriptionQueue,
useTranscriptionBacklog,
} from "@/services/api/admin";
import { SelectSourceMenu } from "@/components/tables/components";

import BaseTable from "@/components/tables/BaseTable";
import { convertStringToArray } from "@/utils";
import TitleWithTags from "@/components/tables/TitleWithTags";
import { TableStructure } from "@/components/tables/types";
import { TranscriptMetadata, TranscriptionQueueItem } from "../../../types";

const Sources = () => {
const [selectedSource, setSelectedSource] = useState<string>("all");
const [removeFromQueueSelection, setRemoveFromQueueSelection] = useState<
string[]
>([]);
const [addToQueueSelection, setAddToQueueSelection] = useState<string[]>([]);
const canAccessTranscription = useHasPermission("accessTranscription");

const { transcriptionBacklog, sources } =
useTranscriptionBacklog(selectedSource);
const {
transcriptionQueue,
remainingBacklog,
addToQueue,
removeFromQueue,
startTranscription,
isTranscribing,
refetch,
} = useTranscriptionQueue(transcriptionBacklog.data);

const handleAddToQueue = async () => {
await addToQueue.mutateAsync(addToQueueSelection);
setAddToQueueSelection([]);
};

const handleRemoveFromQueue = async () => {
await removeFromQueue.mutateAsync(removeFromQueueSelection);
setRemoveFromQueueSelection([]);
};

const tableStructure = [
{
name: "Title",
type: "default",
modifier: () => null,
component: (data) => {
const allTags = convertStringToArray(data.tags);
return (
<TitleWithTags
title={data.title}
allTags={allTags}
categories={[]}
loc={data.loc}
url={data.media}
id={0}
length={allTags.length}
shouldSlice={false}
/>
);
},
},
{
name: "speakers",
type: "text-long",
modifier: (data) => data.speakers.join(", "),
},
{ name: "Publish Date", type: "text-short", modifier: (data) => data.date },
] satisfies TableStructure<TranscriptMetadata>[];

const transcriptionQueueTableStructure = [
...tableStructure,
{
name: "status",
type: "text-short",
modifier: (data) => data.status,
},
] satisfies TableStructure<TranscriptionQueueItem>[];

if (!canAccessTranscription) {
return (
<AuthStatus
title="Unauthorized"
message="You are not authorized to access this page"
/>
);
}

return (
<>
<Flex flexDir="column">
<Heading size={"md"} mb={10}>
{`Transcription Management`}
</Heading>
<BaseTable
data={transcriptionQueue.data}
emptyView={
<Flex w="full" justifyContent="center" alignItems="center" gap={2}>
<Text>Transcription queue is empty</Text>
</Flex>
}
isLoading={transcriptionQueue.isLoading}
isError={transcriptionQueue.isError}
tableStructure={transcriptionQueueTableStructure}
tableHeaderComponent={
<Heading size="sm" mb={1}>
Transcription Queue
</Heading>
}
enableCheckboxes={!isTranscribing}
selectedRowIds={removeFromQueueSelection}
onSelectedRowIdsChange={setRemoveFromQueueSelection}
getRowId={(row) => row.media}
refetch={refetch}
actionItems={
<>
{!isTranscribing && (
<Button
isDisabled={removeFromQueueSelection.length == 0}
isLoading={removeFromQueue.isLoading}
onClick={handleRemoveFromQueue}
>
Remove from Queue
</Button>
)}
<Button
isDisabled={
transcriptionBacklog.isLoading ||
transcriptionQueue.data?.length == 0 ||
isTranscribing
}
onClick={() => startTranscription.mutate()}
>
{`${
isTranscribing
? "Transcription in Progress..."
: "Start Transcription"
}`}
</Button>
</>
}
/>
<BaseTable
data={remainingBacklog}
emptyView={
<Flex w="full" justifyContent="center" alignItems="center" gap={2}>
<Text>
Transcription backlog is empty for the selected source
</Text>
</Flex>
}
isLoading={transcriptionBacklog.isLoading}
isError={transcriptionBacklog.isError}
tableStructure={tableStructure}
tableHeaderComponent={
<Heading size="sm" mb={1}>
{`Transcription Backlog (${selectedSource})`}
</Heading>
}
enableCheckboxes
selectedRowIds={addToQueueSelection}
onSelectedRowIdsChange={setAddToQueueSelection}
getRowId={(row) => row.media}
actionItems={
<>
<Button
isLoading={addToQueue.isLoading}
isDisabled={addToQueueSelection.length == 0}
onClick={handleAddToQueue}
>
Add to Queue
</Button>
<SelectSourceMenu
sources={sources.data}
isLoading={sources.isLoading}
onSelection={(source: string) => setSelectedSource(source)}
/>
</>
}
/>
</Flex>
</>
);
};

export default Sources;
2 changes: 2 additions & 0 deletions src/services/api/admin/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export * from "./useTransaction";
export * from "./useReviews";
export * from "./useUsers";
export * from "./useTranscriptionQueue";
export * from "./useTranscriptionBacklog";
Loading

0 comments on commit 3fa7c96

Please sign in to comment.