diff --git a/next-env.d.ts b/next-env.d.ts
index c4b7818..9edff1c 100644
--- a/next-env.d.ts
+++ b/next-env.d.ts
@@ -1,6 +1,6 @@
///
///
-import "./.next/dev/types/routes.d.ts";
+import "./.next/types/routes.d.ts";
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
diff --git a/src/app/(modules)/upload/components/CardPagination.tsx b/src/app/(modules)/upload/components/CardPagination.tsx
deleted file mode 100644
index f46e514..0000000
--- a/src/app/(modules)/upload/components/CardPagination.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-'use client';
-
-import { ChevronLeft, ChevronRight } from 'lucide-react';
-
-import { Button } from '@/components/ui/button';
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from '@/components/ui/select';
-
-const pageSizes = [10, 20, 40, 50, 100];
-
-interface CardPaginationProps {
- skip: number;
- limit: number;
- total: number;
- count: number;
- onPageChange: (skip: number) => void;
- onLimitChange: (limit: number) => void;
-}
-
-export function CardPagination({
- skip,
- limit,
- total,
- count,
- onPageChange,
- onLimitChange,
-}: CardPaginationProps) {
- return (
-
-
-
Showing {count} of {total}
-
- Rows per page
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/src/app/(modules)/upload/components/UploadActionsMenu.tsx b/src/app/(modules)/upload/components/UploadActionsMenu.tsx
index 8e7d964..b7e3f70 100644
--- a/src/app/(modules)/upload/components/UploadActionsMenu.tsx
+++ b/src/app/(modules)/upload/components/UploadActionsMenu.tsx
@@ -1,26 +1,33 @@
'use client';
-import { Eye, MoreVertical, PlayCircle } from 'lucide-react';
+import {
+ Download,
+ Eye,
+ MoreVertical,
+ PlayCircle,
+ RotateCcw,
+ Ticket,
+ Trash2,
+} from 'lucide-react';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
+ DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
-import type { UploadVideo } from '@/types';
+import type { UploadListItem } from '@/types';
interface UploadActionsMenuProps {
- upload: UploadVideo;
- onViewVideo: (upload: UploadVideo) => void;
- onViewDetails: (upload: UploadVideo) => void;
+ upload: UploadListItem;
+ onViewVideo: (upload: UploadListItem) => void;
}
export function UploadActionsMenu({
upload,
onViewVideo,
- onViewDetails,
}: UploadActionsMenuProps) {
return (
@@ -37,7 +44,7 @@ export function UploadActionsMenu({
- {upload.video_url ? (
+ {upload.raw_video_url ? (
{
event.stopPropagation();
@@ -48,14 +55,18 @@ export function UploadActionsMenu({
View Video
) : null}
- {
- event.stopPropagation();
- onViewDetails(upload);
- }}
- >
-
- View Details
+
+ View Details
+
+
+ Retry Analysis
+
+
+ Open Ticket
+
+
+
+ Delete
diff --git a/src/app/(modules)/upload/components/UploadCardGrid.tsx b/src/app/(modules)/upload/components/UploadCardGrid.tsx
deleted file mode 100644
index 4a25d07..0000000
--- a/src/app/(modules)/upload/components/UploadCardGrid.tsx
+++ /dev/null
@@ -1,107 +0,0 @@
-'use client';
-
-import { FileVideo } from 'lucide-react';
-
-import { Button } from '@/components/ui/button';
-import { Card, CardContent } from '@/components/ui/card';
-import type { UploadVideo } from '@/types';
-
-import { CardPagination } from './CardPagination';
-import { UploadActionsMenu } from './UploadActionsMenu';
-import { UploadThumbnail } from './UploadThumbnail';
-import { formatUploadedAt } from './uploadUtils';
-
-interface UploadCardGridProps {
- uploads: UploadVideo[];
- isLoading: boolean;
- skip: number;
- limit: number;
- total: number;
- onPageChange: (skip: number) => void;
- onLimitChange: (limit: number) => void;
- onUploadNew: () => void;
- onOpenUpload: (upload: UploadVideo) => void;
- onViewVideo: (upload: UploadVideo) => void;
- onViewDetails: (upload: UploadVideo) => void;
-}
-
-export function UploadCardGrid({
- uploads,
- isLoading,
- skip,
- limit,
- total,
- onPageChange,
- onLimitChange,
- onUploadNew,
- onOpenUpload,
- onViewVideo,
- onViewDetails,
-}: UploadCardGridProps) {
- const skeletonItems = Array.from({ length: 6 });
-
- if (!isLoading && uploads.length === 0) {
- return (
-
-
-
-
-
-
-
No uploads found
-
- Upload your first road inspection video.
-
-
-
-
-
- );
- }
-
- return (
-
-
- {isLoading
- ? skeletonItems.map((_, index) => (
-
- ))
- : uploads.map((upload) => (
-
onOpenUpload(upload)}
- >
-
-
-
-
-
- {upload.filename}
-
-
- Uploaded {formatUploadedAt(upload.uploaded_at)}
-
-
-
-
-
-
- ))}
-
-
-
-
- );
-}
diff --git a/src/app/(modules)/upload/components/UploadColumns.tsx b/src/app/(modules)/upload/components/UploadColumns.tsx
deleted file mode 100644
index 8bbaa4a..0000000
--- a/src/app/(modules)/upload/components/UploadColumns.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-'use client';
-
-import { useMemo } from 'react';
-import type { ColumnDef } from '@tanstack/react-table';
-
-import { UploadActionsMenu } from './UploadActionsMenu';
-import { UploadThumbnail } from './UploadThumbnail';
-import { formatUploadedAt, getUploadStatus } from './uploadUtils';
-import { Badge } from '@/components/ui/badge';
-import type { UploadVideo } from '@/types';
-
-interface UseUploadColumnsProps {
- onViewVideo: (upload: UploadVideo) => void;
- onViewDetails: (upload: UploadVideo) => void;
-}
-
-export function useUploadColumns({
- onViewVideo,
- onViewDetails,
-}: UseUploadColumnsProps) {
- return useMemo[]>(
- () => [
- {
- id: 'thumbnail',
- header: 'Thumbnail',
- size: 140,
- minSize: 120,
- enableSorting: false,
- meta: { disableTruncate: true },
- cell: ({ row }) => (
-
- ),
- },
- {
- accessorKey: 'filename',
- header: 'Filename',
- cell: ({ row }) => (
- {row.original.filename}
- ),
- },
- {
- id: 'status',
- header: 'Status',
- size: 130,
- cell: ({ row }) => (
-
- {getUploadStatus(row.original)}
-
- ),
- },
- {
- accessorKey: 'uploaded_at',
- header: 'Uploaded At',
- cell: ({ row }) => formatUploadedAt(row.original.uploaded_at),
- },
- {
- id: 'actions',
- header: () => Actions,
- size: 56,
- minSize: 56,
- enableSorting: false,
- enableHiding: false,
- meta: { disableTruncate: true },
- cell: ({ row }) => (
-
-
-
- ),
- },
- ],
- [onViewDetails, onViewVideo],
- );
-}
diff --git a/src/app/(modules)/upload/components/UploadDetailsDialog.tsx b/src/app/(modules)/upload/components/UploadDetailsDialog.tsx
deleted file mode 100644
index f67e51d..0000000
--- a/src/app/(modules)/upload/components/UploadDetailsDialog.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-'use client';
-
-import {
- Dialog,
- DialogContent,
- DialogDescription,
- DialogHeader,
- DialogTitle,
-} from '@/components/ui/dialog';
-import { Badge } from '@/components/ui/badge';
-import {
- Tooltip,
- TooltipContent,
- TooltipProvider,
- TooltipTrigger,
-} from '@/components/ui/tooltip';
-import type { UploadVideo } from '@/types';
-
-import { formatUploadedAt, getUploadStatus } from './uploadUtils';
-
-interface UploadDetailsDialogProps {
- upload: UploadVideo | null;
- open: boolean;
- onOpenChange: (open: boolean) => void;
-}
-
-export function UploadDetailsDialog({
- upload,
- open,
- onOpenChange,
-}: UploadDetailsDialogProps) {
- const title = upload?.filename ?? 'Upload Details';
-
- return (
-
- );
-}
diff --git a/src/app/(modules)/upload/components/UploadListColumns.tsx b/src/app/(modules)/upload/components/UploadListColumns.tsx
new file mode 100644
index 0000000..6feb189
--- /dev/null
+++ b/src/app/(modules)/upload/components/UploadListColumns.tsx
@@ -0,0 +1,153 @@
+'use client';
+
+import { useMemo } from 'react';
+import type { ColumnDef } from '@tanstack/react-table';
+import { Video } from 'lucide-react';
+
+import { ENV_CONSTANT } from '@/constants/secrect.constant';
+import type { UploadListItem } from '@/types';
+import { formatDate } from '@/utils/date';
+
+import { UploadActionsMenu } from './UploadActionsMenu';
+import { UploadResultCell } from './UploadResultCell';
+import { UploadStatusBadge } from './UploadStatusBadge';
+
+function resolveThumbnailUrl(value: string | null) {
+ if (!value) return null;
+ if (/^https?:\/\//i.test(value)) return value;
+
+ const baseUrl = ENV_CONSTANT.BASE_API_URL?.replace(/\/$/, '');
+ const path = value.startsWith('/') ? value : `/${value}`;
+ return baseUrl ? `${baseUrl}${path}` : path;
+}
+
+export function useUploadListColumns(
+ onViewVideo: (upload: UploadListItem) => void,
+): ColumnDef[] {
+ return useMemo(
+ () => [
+ {
+ id: 'thumbnail',
+ header: 'Thumbnail',
+ size: 104,
+ minSize: 96,
+ enableSorting: false,
+ meta: { disableTruncate: true },
+ cell: ({ row }) => {
+ const thumbnail = resolveThumbnailUrl(row.original.thumbnail);
+
+ return (
+
+ {!thumbnail ? (
+
+ ) : null}
+
+ );
+ },
+ },
+ {
+ accessorKey: 'video_name',
+ header: 'Video Name',
+ size: 150,
+ cell: ({ row }) => (
+ {row.original.video_name}
+ ),
+ },
+ {
+ id: 'uploaded_by',
+ header: 'Uploader',
+ size: 190,
+ cell: ({ row }) => (
+
+
+ {row.original.uploaded_by.name ?? '—'}
+
+
+ {row.original.uploaded_by.email ?? '—'}
+
+
+ ),
+ },
+ {
+ accessorKey: 'project',
+ header: 'Project',
+ size: 165,
+ cell: ({ row }) => row.original.project ?? '—',
+ },
+ {
+ accessorKey: 'package',
+ header: 'Package',
+ size: 110,
+ cell: ({ row }) => row.original.package ?? '—',
+ },
+ {
+ accessorKey: 'segment',
+ header: 'Segment',
+ size: 140,
+ cell: ({ row }) => row.original.segment ?? '—',
+ },
+ {
+ accessorKey: 'chainage',
+ header: 'Chainage',
+ size: 155,
+ cell: ({ row }) => row.original.chainage ?? '—',
+ },
+ {
+ accessorKey: 'uploaded_at',
+ header: 'Uploaded At',
+ size: 175,
+ cell: ({ row }) => formatDate(row.original.uploaded_at),
+ },
+ {
+ id: 'status',
+ header: 'Status',
+ size: 145,
+ meta: { disableTruncate: true },
+ cell: ({ row }) => (
+
+ ),
+ },
+ {
+ id: 'result',
+ header: 'Result',
+ size: 170,
+ meta: { disableTruncate: true },
+ cell: ({ row }) => ,
+ },
+ {
+ id: 'actions',
+ header: () => Actions,
+ size: 64,
+ minSize: 64,
+ enableSorting: false,
+ enableHiding: false,
+ meta: { disableTruncate: true },
+ cell: ({ row }) => (
+
+
+
+ ),
+ },
+ ],
+ [onViewVideo],
+ );
+}
diff --git a/src/app/(modules)/upload/components/UploadResultCell.tsx b/src/app/(modules)/upload/components/UploadResultCell.tsx
new file mode 100644
index 0000000..3344ae6
--- /dev/null
+++ b/src/app/(modules)/upload/components/UploadResultCell.tsx
@@ -0,0 +1,32 @@
+import { CircleX, TicketCheck } from 'lucide-react';
+
+import { Badge } from '@/components/ui/badge';
+import type { UploadListItem } from '@/types';
+
+export function UploadResultCell({ item }: { item: UploadListItem }) {
+ if (item.result === 'ticket_created') {
+ return (
+
+
+ Ticket Created
+
+
{item.ticket_id}
+
+ );
+ }
+
+ if (item.result === 'analysis_failed') {
+ return (
+
+
+ Analysis Failed
+
+
+ {item.error_message ?? 'Processing failed'}
+
+
+ );
+ }
+
+ return —;
+}
diff --git a/src/app/(modules)/upload/components/UploadStatusBadge.tsx b/src/app/(modules)/upload/components/UploadStatusBadge.tsx
new file mode 100644
index 0000000..445b7f6
--- /dev/null
+++ b/src/app/(modules)/upload/components/UploadStatusBadge.tsx
@@ -0,0 +1,65 @@
+import { CheckCircle2, Clock3, LoaderCircle, XCircle } from 'lucide-react';
+
+import { Badge } from '@/components/ui/badge';
+import { Progress } from '@/components/ui/progress';
+import type { UploadStatus } from '@/types';
+
+interface UploadStatusBadgeProps {
+ status: UploadStatus;
+ progress: number;
+}
+
+const statusConfig = {
+ queued: {
+ label: 'Queued',
+ icon: Clock3,
+ className: 'bg-zinc-500/15 text-zinc-500 dark:text-zinc-300',
+ },
+ processing: {
+ label: 'Processing',
+ icon: LoaderCircle,
+ className: 'bg-blue-500/15 text-blue-600 dark:text-blue-400',
+ },
+ completed: {
+ label: 'Completed',
+ icon: CheckCircle2,
+ className: 'bg-emerald-500/15 text-emerald-600 dark:text-emerald-400',
+ },
+ failed: {
+ label: 'Failed',
+ icon: XCircle,
+ className: 'bg-red-500/15 text-red-600 dark:text-red-400',
+ },
+} satisfies Record<
+ UploadStatus,
+ { label: string; icon: typeof Clock3; className: string }
+>;
+
+export function UploadStatusBadge({
+ status,
+ progress,
+}: UploadStatusBadgeProps) {
+ const config = statusConfig[status];
+ const Icon = config.icon;
+
+ return (
+
+
+
+ {config.label}
+
+ {status === 'processing' ? (
+
+
+ {progress}% complete
+
+
+
+ ) : status === 'completed' ? (
+
100%
+ ) : null}
+
+ );
+}
diff --git a/src/app/(modules)/upload/components/UploadTable.tsx b/src/app/(modules)/upload/components/UploadTable.tsx
index 70ee80f..524a62e 100644
--- a/src/app/(modules)/upload/components/UploadTable.tsx
+++ b/src/app/(modules)/upload/components/UploadTable.tsx
@@ -3,18 +3,17 @@
import type { ColumnDef } from '@tanstack/react-table';
import { DataTable } from '@/components/data-table';
-import type { UploadVideo } from '@/types';
+import type { UploadListItem } from '@/types';
interface UploadTableProps {
- columns: ColumnDef[];
- uploads: UploadVideo[];
+ columns: ColumnDef[];
+ uploads: UploadListItem[];
isLoading: boolean;
skip: number;
limit: number;
total: number;
onPageChange: (skip: number) => void;
onLimitChange: (limit: number) => void;
- onRowClick: (upload: UploadVideo) => void;
}
export function UploadTable({
@@ -26,7 +25,6 @@ export function UploadTable({
total,
onPageChange,
onLimitChange,
- onRowClick,
}: UploadTableProps) {
return (
diff --git a/src/app/(modules)/upload/components/UploadThumbnail.tsx b/src/app/(modules)/upload/components/UploadThumbnail.tsx
deleted file mode 100644
index 2b4608e..0000000
--- a/src/app/(modules)/upload/components/UploadThumbnail.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { ImageIcon } from 'lucide-react';
-
-import type { UploadVideo } from '@/types';
-
-import { resolveMediaUrl } from './uploadUtils';
-
-interface UploadThumbnailProps {
- upload: UploadVideo;
- className?: string;
-}
-
-export function UploadThumbnail({ upload, className }: UploadThumbnailProps) {
- const thumbnailUrl = resolveMediaUrl(upload.thumbnail);
-
- return (
-
-
- {thumbnailUrl ? (
-

- ) : (
-
-
-
- )}
-
-
- );
-}
diff --git a/src/app/(modules)/upload/components/UploadVideoDialog.tsx b/src/app/(modules)/upload/components/UploadVideoDialog.tsx
index 6ca09b4..4312741 100644
--- a/src/app/(modules)/upload/components/UploadVideoDialog.tsx
+++ b/src/app/(modules)/upload/components/UploadVideoDialog.tsx
@@ -8,10 +8,10 @@ import {
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
-import type { UploadVideo } from '@/types';
+import type { UploadListItem } from '@/types';
interface UploadVideoDialogProps {
- upload: UploadVideo | null;
+ upload: UploadListItem | null;
open: boolean;
onOpenChange: (open: boolean) => void;
}
@@ -29,12 +29,12 @@ export function UploadVideoDialog({
Video Preview
- {upload?.filename ?? 'Uploaded video'}
+ {upload?.video_name ?? 'Uploaded video'}
diff --git a/src/app/(modules)/upload/components/ViewSwitcher.tsx b/src/app/(modules)/upload/components/ViewSwitcher.tsx
deleted file mode 100644
index 3e277df..0000000
--- a/src/app/(modules)/upload/components/ViewSwitcher.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-'use client';
-
-import { Grid2X2, Table2 } from 'lucide-react';
-
-import { Button } from '@/components/ui/button';
-import { cn } from '@/lib/utils';
-
-import type { UploadViewMode } from '../hooks/useUploadFilters';
-
-interface ViewSwitcherProps {
- value: UploadViewMode;
- onValueChange: (value: UploadViewMode) => void;
-}
-
-export function ViewSwitcher({ value, onValueChange }: ViewSwitcherProps) {
- const items: Array<{
- value: UploadViewMode;
- label: string;
- icon: React.ReactNode;
- }> = [
- { value: 'card', label: 'Card View', icon: },
- { value: 'table', label: 'Table View', icon: },
- ];
-
- return (
-
- {items.map((item) => (
-
- ))}
-
- );
-}
-
diff --git a/src/app/(modules)/upload/components/uploadUtils.ts b/src/app/(modules)/upload/components/uploadUtils.ts
deleted file mode 100644
index 3106fa6..0000000
--- a/src/app/(modules)/upload/components/uploadUtils.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { ENV_CONSTANT } from '@/constants/secrect.constant';
-import type { UploadVideo } from '@/types';
-
-export function getUploadStatus(upload: UploadVideo) {
- return upload.status?.status || 'unknown';
-}
-
-export function formatUploadedAt(value: string) {
- const date = new Date(value);
- if (Number.isNaN(date.getTime())) return '-';
-
- return new Intl.DateTimeFormat('en-IN', {
- day: '2-digit',
- month: 'short',
- year: 'numeric',
- hour: 'numeric',
- minute: '2-digit',
- }).format(date);
-}
-
-export function resolveMediaUrl(url?: string | null) {
- if (!url) return null;
- if (/^https?:\/\//i.test(url)) return url;
-
- const baseUrl = ENV_CONSTANT.BASE_API_URL?.replace(/\/$/, '');
- const path = url.startsWith('/') ? url : `/${url}`;
- return baseUrl ? `${baseUrl}${path}` : path;
-}
-
diff --git a/src/app/(modules)/upload/hooks/useUploadFilters.ts b/src/app/(modules)/upload/hooks/useUploadFilters.ts
index 6f89585..f034bb7 100644
--- a/src/app/(modules)/upload/hooks/useUploadFilters.ts
+++ b/src/app/(modules)/upload/hooks/useUploadFilters.ts
@@ -2,14 +2,11 @@
import { useState } from 'react';
-export type UploadViewMode = 'card' | 'table';
-
const DEFAULT_LIMIT = 20;
export function useUploadFilters() {
const [skip, setSkip] = useState(0);
const [limit, setLimitValue] = useState(DEFAULT_LIMIT);
- const [viewMode, setViewMode] = useState('card');
const setLimit = (nextLimit: number) => {
setLimitValue(nextLimit);
@@ -21,7 +18,5 @@ export function useUploadFilters() {
setSkip,
limit,
setLimit,
- viewMode,
- setViewMode,
};
}
diff --git a/src/app/(modules)/upload/hooks/useUploadQueries.ts b/src/app/(modules)/upload/hooks/useUploadQueries.ts
index e4876ee..26f59ad 100644
--- a/src/app/(modules)/upload/hooks/useUploadQueries.ts
+++ b/src/app/(modules)/upload/hooks/useUploadQueries.ts
@@ -13,7 +13,7 @@ export const uploadKeys = {
list: (params: PaginationParams) => [...uploadKeys.lists(), params] as const,
};
-interface UseMyUploadsQueryParams {
+interface UseUploadsQueryParams {
skip: number;
limit: number;
}
@@ -21,14 +21,14 @@ interface UseMyUploadsQueryParams {
function buildUploadListParams({
skip,
limit,
-}: UseMyUploadsQueryParams): PaginationParams {
+}: UseUploadsQueryParams): PaginationParams {
return {
skip,
limit,
};
}
-export function useMyUploadsQuery(params: UseMyUploadsQueryParams) {
+export function useUploadsQuery(params: UseUploadsQueryParams) {
const { skip, limit } = params;
const listParams = useMemo(
() => buildUploadListParams({ skip, limit }),
@@ -37,7 +37,7 @@ export function useMyUploadsQuery(params: UseMyUploadsQueryParams) {
return useQuery({
queryKey: uploadKeys.list(listParams),
- queryFn: () => videoService.getMyUploads(listParams),
+ queryFn: () => videoService.getUploads(listParams),
});
}
diff --git a/src/app/(modules)/upload/page.tsx b/src/app/(modules)/upload/page.tsx
index 6f44623..d4ff595 100644
--- a/src/app/(modules)/upload/page.tsx
+++ b/src/app/(modules)/upload/page.tsx
@@ -1,62 +1,37 @@
'use client';
-import { useCallback, useState } from 'react';
+import { useState } from 'react';
import { Plus, UploadCloud } from 'lucide-react';
import { PageHeader } from '@/components/page-header';
import { Button } from '@/components/ui/button';
-import type { UploadVideo } from '@/types';
+import type { UploadListItem } from '@/types';
import { CreateUploadDialog } from './components/CreateUploadDialog';
-import { UploadCardGrid } from './components/UploadCardGrid';
-import { useUploadColumns } from './components/UploadColumns';
-import { UploadDetailsDialog } from './components/UploadDetailsDialog';
+import { useUploadListColumns } from './components/UploadListColumns';
import { UploadTable } from './components/UploadTable';
import { UploadVideoDialog } from './components/UploadVideoDialog';
-import { ViewSwitcher } from './components/ViewSwitcher';
import { useUploadFilters } from './hooks/useUploadFilters';
-import { useMyUploadsQuery } from './hooks/useUploadQueries';
+import { useUploadsQuery } from './hooks/useUploadQueries';
export default function UploadPage() {
- const { skip, setSkip, limit, setLimit, viewMode, setViewMode } =
- useUploadFilters();
- const uploadsQuery = useMyUploadsQuery({ skip, limit });
+ const { skip, setSkip, limit, setLimit } = useUploadFilters();
+ const uploadsQuery = useUploadsQuery({ skip, limit });
const [isCreateOpen, setIsCreateOpen] = useState(false);
- const [detailsUpload, setDetailsUpload] = useState(null);
- const [videoUpload, setVideoUpload] = useState(null);
+ const [videoUpload, setVideoUpload] = useState(null);
const uploads = uploadsQuery.data?.items ?? [];
const total = uploadsQuery.data?.total ?? 0;
const isLoading = uploadsQuery.isLoading;
- const handleViewDetails = useCallback((upload: UploadVideo) => {
- setDetailsUpload(upload);
- }, []);
-
- const handleViewUploadVideo = useCallback((upload: UploadVideo) => {
- setVideoUpload(upload);
- }, []);
-
- const handleOpenUpload = useCallback(
- (upload: UploadVideo) => {
- if (upload.video_url) {
- handleViewUploadVideo(upload);
- }
- },
- [handleViewUploadVideo],
- );
-
- const columns = useUploadColumns({
- onViewVideo: handleViewUploadVideo,
- onViewDetails: handleViewDetails,
- });
+ const columns = useUploadListColumns(setVideoUpload);
return (
<>
setIsCreateOpen(true)} size="sm">
@@ -66,47 +41,19 @@ export default function UploadPage() {
}
/>
-
-
-
-
- {viewMode === 'card' ? (
- setIsCreateOpen(true)}
- onOpenUpload={handleOpenUpload}
- onViewVideo={handleViewUploadVideo}
- onViewDetails={handleViewDetails}
- />
- ) : (
-
- )}
+
- {
- if (!open) setDetailsUpload(null);
- }}
- />
boolean,
fallback = ROUTES.ACCESS,
) {
- return findFirstMenuPath(filterMenuItems(menuItems, hasPermission)) ?? fallback;
+ return (
+ findFirstMenuPath(filterMenuItems(menuItems, hasPermission)) ?? fallback
+ );
}
export function getFirstAccessiblePathForPermissions(
diff --git a/src/constants/apiRoutes.ts b/src/constants/apiRoutes.ts
index 700d23f..788e31d 100644
--- a/src/constants/apiRoutes.ts
+++ b/src/constants/apiRoutes.ts
@@ -49,7 +49,7 @@ export const API_ROUTES = {
},
VIDEOS: {
UPLOAD: '/biz/api/v1/upload',
- MY_UPLOADS: '/biz/api/v1/videos/me',
+ UPLOADS: '/biz/api/v1/uploads',
RESULTS: (id: string) => `/biz/api/v1/results/${id}/completed`,
},
TICKETS: {
diff --git a/src/services/api/video.service.ts b/src/services/api/video.service.ts
index eca55b0..e172bbd 100644
--- a/src/services/api/video.service.ts
+++ b/src/services/api/video.service.ts
@@ -1,16 +1,20 @@
import axiosClient from '../axios/axios';
import { API_ROUTES } from '@/constants/apiRoutes';
-import { CompletedVideoResult, PaginationParams, UploadVideosResponse } from '@/types';
+import {
+ CompletedVideoResult,
+ PaginationParams,
+ UploadListResponse,
+} from '@/types';
/**
* Video Service
*/
export const videoService = {
- getMyUploads: async (
+ getUploads: async (
params?: PaginationParams,
- ): Promise => {
- const response = await axiosClient.get(
- API_ROUTES.VIDEOS.MY_UPLOADS,
+ ): Promise => {
+ const response = await axiosClient.get(
+ API_ROUTES.VIDEOS.UPLOADS,
{
params: {
skip: params?.skip ?? 0,
diff --git a/src/types/upload.ts b/src/types/upload.ts
index fce6cc8..edf0aef 100644
--- a/src/types/upload.ts
+++ b/src/types/upload.ts
@@ -1,25 +1,25 @@
-export interface UploadVideoStatus {
- status: string;
+export type UploadStatus = 'queued' | 'processing' | 'completed' | 'failed';
+export type UploadResult = 'ticket_created' | 'analysis_failed' | null;
+
+export interface UploadListItem {
+ id: string;
+ video_name: string;
+ thumbnail: string | null;
+ raw_video_url: string | null;
+ uploaded_by: { name: string | null; email: string | null };
+ project: string | null;
+ package: string | null;
+ segment: string | null;
+ chainage: string | null;
+ uploaded_at: string | null;
+ status: UploadStatus;
progress: number;
+ result: UploadResult;
+ ticket_id: string | null;
+ error_message: string | null;
}
-export interface UploadVideo {
- video_id: string;
- filename: string;
- chainage_id: string;
- detection_mode: string;
- speed_kmh?: number | null;
- thumbnail?: string | null;
- uploaded_at: string;
- uploaded_by_user_id?: number | null;
- uploaded_by_email?: string | null;
- video_url?: string | null;
- processed_video_path?: string | null;
- processed_video_url?: string | null;
- status: UploadVideoStatus;
-}
-
-export interface UploadVideosResponse {
- items: UploadVideo[];
+export interface UploadListResponse {
+ items: UploadListItem[];
total: number;
}