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: 3 additions & 0 deletions app/src/app/admin/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ export default async function AdminLayout({
<Link href="/admin/subscriptions" className="block hover:underline">
Subscriptions
</Link>
<Link href="/transcriptions" className="block hover:underline">
Sample Recordings
</Link>
</nav>
</aside>
<main className="flex-1 bg-gray-50 p-6 overflow-auto">{children}</main>
Expand Down
4 changes: 3 additions & 1 deletion app/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { secondaryFont } from "@/fonts";
import { CookiesProvider } from "next-client-cookies/server";
import Header from "@/components/Header";
import { isSignedIn } from "@/actions/auth";
import { validateRequest } from "@/auth";

export const metadata: Metadata = {
title: "Lingo.ai",
Expand All @@ -17,14 +18,15 @@ export default async function RootLayout({
children: React.ReactNode;
}>) {
const isUserSignedIn = await isSignedIn();
const { user } = await validateRequest();

return (
<html lang="en" suppressHydrationWarning>
<head />
<body className={`h-screen flex flex-col overflow-y-auto `}>
<CookiesProvider>
<TanstackQueryProvider>
<Header isSignedIn={isUserSignedIn} />
<Header isSignedIn={isUserSignedIn} userRole={user?.role ?? "guest"}/>
<section className="flex-1 overflow-y-auto">
<div className="w-full h-full">{children}</div>
</section>
Expand Down
1 change: 1 addition & 0 deletions app/src/app/transcriptions/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const page = async () => {
<TranscriptionItem
initialTranscriptionsData={userTranscriptions}
userId={user?.id || null}
userRole={user?.role || null}
/>
</div>
);
Expand Down
4 changes: 3 additions & 1 deletion app/src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import Navigation from "./Navigation";

type HeaderProps = {
isSignedIn: boolean;
userRole: string;
};

const Header = ({ isSignedIn }: HeaderProps) => {
const Header = ({ isSignedIn, userRole }: HeaderProps) => {
return (
<Navigation
isSignedIn={isSignedIn}
userRole={userRole}
{...(!isSignedIn && {
navItems: [
{ label: "Features", href: "#features" },
Expand Down
21 changes: 18 additions & 3 deletions app/src/components/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type NavItem = {

type NavigationProps = {
isSignedIn?: boolean;
userRole?: string;
};

export type ProfileMenuItems = {
Expand All @@ -55,7 +56,7 @@ export type StateType = {
isProfileModalOpen: boolean;
recordsLabel: string;
};
const Navigation = ({ isSignedIn }: NavigationProps) => {
const Navigation = ({ isSignedIn, userRole }: NavigationProps) => {
const pathname = usePathname() as string;
const router = useRouter();
const [uiState, setUIState] = useState<StateType>({
Expand Down Expand Up @@ -222,7 +223,21 @@ const Navigation = ({ isSignedIn }: NavigationProps) => {
</Button>
)}

{isSignedIn && (
{isSignedIn && userRole === "ADMIN" && (
<Button
variant={"greenTheme"}
className={`${pathname === "/new" ? "" : "hidden"}`}
>
<Link
href={"/admin/users"}
className="flex justify-center items-center"
>
<span className="text-md">Dashboard</span>
</Link>
</Button>
)}

{isSignedIn && userRole !== "ADMIN" && (
<Button
variant={"greenTheme"}
className={`${pathname === "/new" ? "hidden" : ""}`}
Expand All @@ -236,7 +251,7 @@ const Navigation = ({ isSignedIn }: NavigationProps) => {
</Link>
</Button>
)}
{pathname !== "/transcriptions" && (
{pathname !== "/transcriptions" && userRole !== "ADMIN" && (
<Button variant={"greenTheme"}>
<Files className="mr-2 w-4 h-4" />
<Link href={"/transcriptions"}>{uiState.recordsLabel}</Link>
Expand Down
39 changes: 33 additions & 6 deletions app/src/components/TranscriptionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,30 @@ import { useEffect, useRef, useState } from "react";
import { format } from "date-fns";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { FileAudio, Pause, Play } from "lucide-react";
import { FileAudio, Pause, Play, Trash2 } from "lucide-react"; // Added Trash icon
import Link from "next/link";
import { getAudioDuration, getFileSize } from "@/utils/recording";
import { userTranscriptions } from "@/types/transcriptions";

interface TranscriptionRowProps {
transcription: userTranscriptions;
index: number;
isPlaying: boolean;
userRole: string | null;
onPlayPause: () => void;
onAudioEnd: () => void;
onDelete?: () => void;
onToggleDefault?: (checked: boolean) => void;
rowRef?: (node: HTMLTableRowElement | null) => void;
}

const TranscriptionRow = ({
transcription,
index,
isPlaying,
userRole,
onPlayPause,
onAudioEnd,
onDelete,
onToggleDefault,
rowRef,
}: TranscriptionRowProps) => {
const [audioDuration, setAudioDuration] = useState<string | null>(null);
Expand Down Expand Up @@ -76,6 +80,18 @@ const TranscriptionRow = ({

return (
<tr className="border-b border-gray-200 mt-4" ref={rowRef}>
{userRole === "ADMIN" && (
<td className="py-3 px-2">
<input
type="checkbox"
className="h-4 w-4"
checked={transcription?.isDefault}
onChange={(e) => {
if (onToggleDefault) onToggleDefault(e.target.checked);
}}
/>
</td>
)}
<td className="py-3 px-2">
<Button
variant="ghost"
Expand All @@ -99,9 +115,7 @@ const TranscriptionRow = ({
</Link>
</td>
<td className="text-muted-foreground ">
<div className="ml-4">
{fileSize ? fileSize : "Loading..."}
</div>
<div className="ml-4">{fileSize ? fileSize : "Loading..."}</div>
</td>
<td>
<Badge className="ml-4" variant="secondary">
Expand All @@ -117,6 +131,19 @@ const TranscriptionRow = ({
? format(new Date(transcription.createdAt), "dd MMM yyyy | hh:mm a")
: "N/A"}
</td>

{userRole === "ADMIN" && (
<td className="py-3 px-2">
<Button
variant="ghost"
size="sm"
onClick={onDelete}
className="w-8 h-8 p-2 rounded-full flex items-center justify-center shadow-xl hover:bg-red-500 cursor-pointer hover:text-white"
>
<Trash2 className="h-4 w-4" />
</Button>
</td>
)}
</tr>
);
};
Expand Down
70 changes: 62 additions & 8 deletions app/src/components/TranscriptionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,18 @@ import {
SelectTrigger,
SelectValue,
} from "./ui/select";
import { toast } from "sonner";
import { API } from "@/lib/axios";
import { Button } from "./ui/button";

interface TranscriptionItemProps {
initialTranscriptionsData: userTranscriptions[];
userId: string | null;
userRole: string | null;
}

const TranscriptionItem = (props: TranscriptionItemProps) => {
const { initialTranscriptionsData, userId } = props;
const { initialTranscriptionsData, userId, userRole } = props;

const [defaultTranscriptionFilter, setDefaultTranscriptionFilter] =
useState<string>(userId ? "user" : "true");
Expand All @@ -30,7 +34,7 @@ const TranscriptionItem = (props: TranscriptionItemProps) => {
);
const [isLoading, setIsLoading] = useState<boolean>(true);

const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, refetch } =
useTranscriptions(
initialTranscriptionsData,
defaultTranscriptionFilter,
Expand Down Expand Up @@ -79,6 +83,36 @@ const TranscriptionItem = (props: TranscriptionItemProps) => {
(page: { transcriptions: userTranscriptions[] }) => page.transcriptions
) || [];

const handleDeleteRecording = async (recordingId: String) => {
try {
const confirmDelete = confirm(
"Are you sure you want to delete this recording?"
);
if (!confirmDelete) return;

await API.delete(`/admin/transcriptions/${recordingId}`);
refetch();
toast.success("Recording deleted successfully");
} catch (error) {
console.error("Failed to delete recording:", error);
toast.error("Failed to delete recording");
}
};

const handleMakeDefault = async (recordingId: String) => {
console.log("Make default clicked for recording ID:", recordingId);
// try {
// await API.post(`/admin/transcriptions/${recordingId}/make-default`);
// refetch();
// toast.success("Recording set as default successfully");
// } catch (error) {
// console.error("Failed to set recording as default:", error);
// toast.error("Failed to set recording as default");
// }
};

console.log("Filtered Transcriptions:", filteredTranscriptions);

return (
<div>
<div className="container mx-auto px-4 pt-4 pb-8 max-w-7xl">
Expand Down Expand Up @@ -116,11 +150,23 @@ const TranscriptionItem = (props: TranscriptionItemProps) => {
</div>
) : (
<>
<div className="mb-8">
<h1 className="text-3xl font-bold mb-2">Audio Recordings</h1>
<p className="text-muted-foreground">
Manage and play your uploaded audio recordings
</p>
<div className="flex">
<div className="mb-8">
<h1 className="text-3xl font-bold mb-2">Audio Recordings</h1>
<p className="text-muted-foreground">
Manage and play your uploaded audio recordings
</p>
</div>
<div className="ml-auto mt-3">
{userRole === "ADMIN" && (
<Button
variant={"greenTheme"}
onClick={() => (window.location.href = "/new")}
>
Add Sample Recording
</Button>
)}
</div>
</div>
<Card>
<CardHeader>
Expand All @@ -133,20 +179,23 @@ const TranscriptionItem = (props: TranscriptionItemProps) => {
<Table>
<TableHeader>
<TableRow>
{userRole === "ADMIN" && (
<TableHead className="w-12">Sample</TableHead>
)}
<TableHead className="w-12"></TableHead>
<TableHead>File Name</TableHead>
<TableHead>File Size</TableHead>
<TableHead>Language</TableHead>
<TableHead>Duration</TableHead>
<TableHead>Upload Date</TableHead>
{userRole === "ADMIN" && <TableHead>Actions</TableHead>}
</TableRow>
</TableHeader>
<TableBody>
{filteredTranscriptions.map((transcription, idx) => (
<TranscriptionRow
key={idx}
transcription={transcription}
index={idx}
isPlaying={currentPlayingIndex === idx}
onPlayPause={() => handlePlayPause(idx)}
onAudioEnd={handleAudioEnd}
Expand All @@ -155,6 +204,11 @@ const TranscriptionItem = (props: TranscriptionItemProps) => {
? lastItemRef
: undefined
}
onDelete={() => handleDeleteRecording(transcription.id)}
onToggleDefault={() =>
handleMakeDefault(transcription.id)
}
userRole={userRole}
/>
))}
</TableBody>
Expand Down