diff --git a/app/Dashboard/Households/HouseholdDetail/_components/HouseholderVisits/HouseholderVisits.tsx b/app/Dashboard/Households/HouseholdDetail/_components/HouseholderVisits/HouseholderVisits.tsx
index e13b30c2..55c1e7a2 100644
--- a/app/Dashboard/Households/HouseholdDetail/_components/HouseholderVisits/HouseholderVisits.tsx
+++ b/app/Dashboard/Households/HouseholdDetail/_components/HouseholderVisits/HouseholderVisits.tsx
@@ -31,7 +31,7 @@ interface Props {
const columns = [
visitColumnHelper.accessor('documents', {
header: t.table.columns.documents,
- cell: doc => ,
+ cell: doc => ,
}),
visitColumnHelper.accessor('date', {
id: 'date',
diff --git a/app/Dashboard/Households/HouseholdDetail/_components/HouseholderVisits/VisitDetail/VisitDetail.tsx b/app/Dashboard/Households/HouseholdDetail/_components/HouseholderVisits/VisitDetail/VisitDetail.tsx
index ea0ab1c3..28e64e09 100644
--- a/app/Dashboard/Households/HouseholdDetail/_components/HouseholderVisits/VisitDetail/VisitDetail.tsx
+++ b/app/Dashboard/Households/HouseholdDetail/_components/HouseholderVisits/VisitDetail/VisitDetail.tsx
@@ -5,9 +5,10 @@ import {
import { FullPageLoader, showNotification } from '@camp/design';
import type { Document } from '@camp/domain';
import { fileStorageClient } from '@camp/file-storage-client';
-import { DownloadIcon, PdfFileIcon, TrashIcon } from '@camp/icons';
+import { DownloadIcon, PdfFileIcon, TrashIcon, VideoIcon } from '@camp/icons';
import { errorMessages, messages } from '@camp/messages';
import { getFileName, getFileType } from '@camp/router';
+import { isEmpty } from '@fullstacksjs/toolbox';
import {
Box,
Button,
@@ -15,7 +16,6 @@ import {
Group,
Image,
SimpleGrid,
- Skeleton,
Stack,
} from '@mantine/core';
import download from 'downloadjs';
@@ -59,17 +59,8 @@ export const VisitDetail = ({ id }: VisitDetailProps) => {
);
const visit = data!.visit;
- // const fileType = getFileType(selectedDocument!.url);
const deleteDocument = async (d: Document): Promise => {
- if (visit?.documents.length === 1) {
- return showNotification({
- type: 'failure',
- title: t.title,
- message: messages.notification.visits.delete.cantDeleteLst,
- });
- }
-
const deleted = await deleteOneDocument({
variables: { id: d.id },
});
@@ -87,82 +78,92 @@ export const VisitDetail = ({ id }: VisitDetailProps) => {
download(file, getFileName(url));
};
- const fileType = getFileType(selectedDocument!.url);
+ const fileType = selectedDocument?.url
+ ? getFileType(selectedDocument.url)
+ : null;
return (
-
- {visit?.documents.map(doc => {
- const isSelected = selectedDocument?.url === doc.url;
+ {isEmpty(visit?.documents ?? []) ? null : (
+ <>
+
+ {visit?.documents.map(doc => {
+ const isSelected = selectedDocument?.url === doc.url;
- return (
- setSelectedDocument(d)}
- onDelete={deleteDocument}
- />
- );
- })}
-
- ({
- flexGrow: 1,
- borderLeft: `1px solid ${theme.colors.bg[5]}`,
- })}
- >
- {fileType === 'pdf' ? (
-
-
-
- ) : (
-
- )}
-
- }
- >
- {messages.actions.download}
-
- }
- onClick={() => deleteDocument(selectedDocument!)}
+ return (
+ setSelectedDocument(d)}
+ onDelete={deleteDocument}
+ />
+ );
+ })}
+
+ ({
+ flexGrow: 1,
+ borderLeft: `1px solid ${theme.colors.bg[5]}`,
+ })}
>
- {messages.actions.delete}
-
-
-
+ {fileType === 'pdf' ? (
+
+
+
+ ) : fileType === 'video' ? (
+
+
+
+ ) : selectedDocument != null ? (
+
+ ) : null}
+
+ }
+ >
+ {messages.actions.download}
+
+ }
+ onClick={() => deleteDocument(selectedDocument!)}
+ >
+ {messages.actions.delete}
+
+
+
+ >
+ )}
);
};
diff --git a/app/Dashboard/Households/HouseholdDetail/_components/HouseholderVisits/VisitDetail/VisitDetailDocumentItem.tsx b/app/Dashboard/Households/HouseholdDetail/_components/HouseholderVisits/VisitDetail/VisitDetailDocumentItem.tsx
index 98f45369..b6045fd2 100644
--- a/app/Dashboard/Households/HouseholdDetail/_components/HouseholderVisits/VisitDetail/VisitDetailDocumentItem.tsx
+++ b/app/Dashboard/Households/HouseholdDetail/_components/HouseholderVisits/VisitDetail/VisitDetailDocumentItem.tsx
@@ -1,8 +1,7 @@
import { debug, DebugScopes } from '@camp/debug';
import type { Document } from '@camp/domain';
-import { PdfFileIcon, VerticalMenuIcon } from '@camp/icons';
+import { PdfFileIcon, VerticalMenuIcon, VideoIcon } from '@camp/icons';
import { messages } from '@camp/messages';
-import type { FileType } from '@camp/router';
import { getFileName, getFileType } from '@camp/router';
import {
ActionIcon,
@@ -124,10 +123,14 @@ export const VisitDetailDocumentItem = ({
sx={{ objectPosition: 'top' }}
alt={document.id}
/>
- ) : (
+ ) : fileType === 'pdf' ? (
+ ) : (
+
+
+
)}
diff --git a/configs/cspell/charity.en.txt b/configs/cspell/charity.en.txt
index c57bcfd8..b37b43ae 100644
--- a/configs/cspell/charity.en.txt
+++ b/configs/cspell/charity.en.txt
@@ -53,4 +53,5 @@ pkey
alian
Prray
clsx
-downloadjs
\ No newline at end of file
+downloadjs
+matroska
\ No newline at end of file
diff --git a/libs/design/ImagePreview/ImagePreview.tsx b/libs/design/ImagePreview/ImagePreview.tsx
index 277a3e0c..781ad965 100644
--- a/libs/design/ImagePreview/ImagePreview.tsx
+++ b/libs/design/ImagePreview/ImagePreview.tsx
@@ -1,12 +1,14 @@
import type { ImageProps } from '@mantine/core';
-import { Box, Image } from '@mantine/core';
+import { Box, Center, Image } from '@mantine/core';
+
+import { CameraOffIcon } from '../../icons';
interface Props extends ImageProps {
size: number;
pad?: number;
}
-export const ImagePreview = ({ size, pad = 10, ...rest }: Props) => {
+export const ImagePreview = ({ size, pad = 10, src, ...rest }: Props) => {
return (
({
@@ -16,7 +18,13 @@ export const ImagePreview = ({ size, pad = 10, ...rest }: Props) => {
padding: pad,
})}
>
-
+
+ {src == null ? (
+
+ ) : (
+
+ )}
+
);
};
diff --git a/libs/domain/Document.ts b/libs/domain/Document.ts
index 47db5bc7..e90b8868 100644
--- a/libs/domain/Document.ts
+++ b/libs/domain/Document.ts
@@ -13,11 +13,21 @@ export const documentSchema = {
export const documentFileValidator = z
.object({
name: z.string(),
- type: z.enum(['image/png', 'application/pdf', 'image/jpg', 'image/jpeg'], {
- errorMap: () => ({
- message: messages.notification.addDocument.unsupportedType,
- }),
- }),
+ type: z.enum(
+ [
+ 'image/png',
+ 'application/pdf',
+ 'image/jpg',
+ 'image/jpeg',
+ 'video/mp4',
+ 'video/x-matroska',
+ ],
+ {
+ errorMap: () => ({
+ message: messages.notification.addDocument.unsupportedType,
+ }),
+ },
+ ),
size: z
.number()
.lt(20000000, messages.notification.addDocument.maxSizeExceeded),
diff --git a/libs/icons/index.ts b/libs/icons/index.ts
index 5fe12632..6e4928cc 100644
--- a/libs/icons/index.ts
+++ b/libs/icons/index.ts
@@ -4,6 +4,7 @@ export {
ArrowDown as ArrowDownIcon,
ArrowUp as ArrowUpIcon,
Calendar as CalendarIcon,
+ CameraOff as CameraOffIcon,
Check as CheckIcon,
ChevronDown as ChevronDownIcon,
ChevronLeft as ChevronLeftIcon,
@@ -23,4 +24,5 @@ export {
Upload as UploadIcon,
User as UserIcon,
MoreVertical as VerticalMenuIcon,
+ Video as VideoIcon,
} from 'react-feather';
diff --git a/libs/router/getFileType.ts b/libs/router/getFileType.ts
index 35e98a7a..745bf747 100644
--- a/libs/router/getFileType.ts
+++ b/libs/router/getFileType.ts
@@ -1,8 +1,9 @@
import { getFileName } from './getFileName';
-export type FileType = 'image' | 'pdf';
+export type FileType = 'image' | 'pdf' | 'video';
export const getFileType = (url: string): FileType => {
const type = getFileName(url).replace(/.+\./g, '');
- return type === 'pdf' ? 'pdf' : 'image';
+
+ return type === 'pdf' ? 'pdf' : /mp4|mkv/.exec(type) ? 'video' : 'image';
};