@@ -24,7 +25,7 @@ export const openAttachmentDialog = (
drawerOnMobile: false,
className: 'min-w-full h-screen border-0 p-0 rounded-none flex flex-col mt-0',
headerClassName: 'absolute p-4 w-full backdrop-blur-sm bg-background/50',
- title: title ?? t('common:view_item', { item: t('common:attachment').toLowerCase() }),
+ hideClose: true,
autoFocus: true,
removeCallback,
},
diff --git a/frontend/src/modules/attachments/render-audio.tsx b/frontend/src/modules/attachments/render-audio.tsx
index 7b647793f..1632302f6 100644
--- a/frontend/src/modules/attachments/render-audio.tsx
+++ b/frontend/src/modules/attachments/render-audio.tsx
@@ -1,7 +1,7 @@
import MediaThemeSutroAudio from 'player.style/sutro-audio/react';
-const RenderAudio = ({ src }: { src: string }) => (
-
+const RenderAudio = ({ src, className }: { src: string; className?: string }) => (
+
{/* biome-ignore lint/a11y/useMediaCaption: by author */}
diff --git a/frontend/src/modules/attachments/render-pdf.tsx b/frontend/src/modules/attachments/render-pdf.tsx
index 0d01d10cf..b6c819c48 100644
--- a/frontend/src/modules/attachments/render-pdf.tsx
+++ b/frontend/src/modules/attachments/render-pdf.tsx
@@ -1,26 +1,46 @@
import { Document, Page, pdfjs } from 'react-pdf';
import 'react-pdf/dist/esm/Page/TextLayer.css';
import 'react-pdf/dist/esm/Page/AnnotationLayer.css';
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
pdfjs.GlobalWorkerOptions.workerSrc = new URL('pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url).toString();
const options = { cMapUrl: '/cmaps/' };
-type PDFFile = string | File | Blob | null;
-
-export default function RenderPDF({ file, className }: { file: PDFFile; className?: string }) {
+export default function RenderPDF({ file, className }: { file: string; className?: string }) {
const [numPages, setNumPages] = useState(null);
+ const [scale, setScale] = useState(1); // Scale for fitting the page
const onDocumentLoadSuccess = ({ numPages }: { numPages: number }) => {
setNumPages(numPages);
};
+ // Adjust scale based on container width
+ const adjustScale = (width: number) => {
+ const desiredWidth = width - 60;
+ const naturalWidth = 612; // Default PDF page width in points
+ setScale(desiredWidth / naturalWidth);
+ };
+
+ useEffect(() => {
+ const handleResize = () => {
+ adjustScale(window.innerWidth - 60);
+ };
+
+ // Adjust scale on window resize
+ window.addEventListener('resize', handleResize);
+ handleResize();
+
+ return () => {
+ window.removeEventListener('resize', handleResize);
+ };
+ }, []);
+
return (
{Array.from(new Array(numPages || 0), (_el, index) => (
-
+
))}
diff --git a/frontend/src/modules/attachments/render-video.tsx b/frontend/src/modules/attachments/render-video.tsx
index 35cdb9bef..c0b7bb402 100644
--- a/frontend/src/modules/attachments/render-video.tsx
+++ b/frontend/src/modules/attachments/render-video.tsx
@@ -1,7 +1,7 @@
import MediaThemeSutro from 'player.style/sutro/react';
-const RenderVideo = ({ src }: { src: string }) => (
-
+const RenderVideo = ({ src, className }: { src: string; className?: string }) => (
+
{/* biome-ignore lint/a11y/useMediaCaption: by author */}
diff --git a/frontend/src/modules/attachments/table/columns.tsx b/frontend/src/modules/attachments/table/columns.tsx
index eb163cda3..368b7b5f8 100644
--- a/frontend/src/modules/attachments/table/columns.tsx
+++ b/frontend/src/modules/attachments/table/columns.tsx
@@ -2,7 +2,8 @@ import type { Attachment } from '~/types/common';
import { config } from 'config';
import type { TFunction } from 'i18next';
-import { CopyCheckIcon, CopyIcon } from 'lucide-react';
+import { CopyCheckIcon, CopyIcon, Download } from 'lucide-react';
+import useDownloader from 'react-use-downloader';
import { useCopyToClipboard } from '~/hooks/use-copy-to-clipboard';
import AttachmentThumb from '~/modules/attachments/attachment-thumb';
import { formatBytes } from '~/modules/attachments/table/helpers';
@@ -28,8 +29,17 @@ export const useColumns = (
visible: true,
sortable: false,
width: 32,
- renderCell: ({ row: { url, filename, contentType }, rowIdx }) => (
- openDialog(rowIdx)} contentType={contentType} />
+ renderCell: ({ row: { url, filename, contentType }, rowIdx, tabIndex }) => (
+
),
},
{
@@ -47,21 +57,53 @@ export const useColumns = (
}),
},
{
- key: 'URL',
+ key: 'url',
name: '',
visible: true,
sortable: false,
width: 32,
renderCell: ({ row, tabIndex }) => {
const { copyToClipboard, copied } = useCopyToClipboard();
+ if (!row.url.startsWith('http')) return -;
+
const shareLink = `${config.backendUrl}/${row.organizationId}/attachments/${row.id}/link`;
return (
-
);
case 'pc':
return (
-
{inView && renderCarousel(false, 'rounded-t-[.5rem]')}
+
{inView && renderCarousel('rounded-t-[.5rem]')}
@@ -36,9 +36,7 @@ const DeviceFrame = ({ type, inView, renderCarousel }: DeviceFrameProps) => {
-
- {inView && renderCarousel(false, 'rounded-[1rem]')}
-
+
{inView && renderCarousel('rounded-[1rem]')}
);
default:
diff --git a/frontend/src/modules/marketing/device-mockup/index.tsx b/frontend/src/modules/marketing/device-mockup/index.tsx
index 5ca72e4a2..8b6e0e419 100644
--- a/frontend/src/modules/marketing/device-mockup/index.tsx
+++ b/frontend/src/modules/marketing/device-mockup/index.tsx
@@ -9,8 +9,8 @@ import DeviceFrame from './frame';
type DeviceType = 'mobile' | 'tablet' | 'pc';
interface DeviceMockupProps {
- lightSlides?: { src: string }[];
- darkSlides?: { src: string }[];
+ lightSlides?: { src: string; name?: string }[];
+ darkSlides?: { src: string; name?: string }[];
className?: string;
type: DeviceType;
}
@@ -31,12 +31,8 @@ const DeviceMockup = ({ lightSlides, darkSlides, type, className }: DeviceMockup
{
- return isDialog ? (
-
- ) : (
-
- );
+ renderCarousel={(className) => {
+ return ;
}}
/>
diff --git a/frontend/src/modules/marketing/footer.tsx b/frontend/src/modules/marketing/footer.tsx
index 5fb84a00c..63770c308 100644
--- a/frontend/src/modules/marketing/footer.tsx
+++ b/frontend/src/modules/marketing/footer.tsx
@@ -46,7 +46,9 @@ export function Credits({ className }: { className?: string }) {
return (
- © {currentYear}. {productName} {t('common:is_built_by')} {companyName}.
+
+ © {currentYear}. {productName} {t('common:is_built_by', { companyName })}.
+
);
}
diff --git a/frontend/src/modules/organizations/invites/invites-count.tsx b/frontend/src/modules/organizations/invites/invites-count.tsx
index 2338d913a..e6d3d2489 100644
--- a/frontend/src/modules/organizations/invites/invites-count.tsx
+++ b/frontend/src/modules/organizations/invites/invites-count.tsx
@@ -21,8 +21,8 @@ export const InvitedUsers = ({ invitesInfo }: Props) => {
,
{
className: 'max-w-full lg:max-w-4xl',
- title: t('common:invites_table_title'),
- description: t('common:invites_table_text', { entity: t('common:organization').toLowerCase() }),
+ title: t('common:pending_invitations'),
+ description: t('common:pending_invitations.text', { entity: t('common:organization').toLowerCase() }),
id: 'invited-users-info',
scrollableOverlay: true,
side: 'right',
diff --git a/frontend/src/modules/organizations/invites/table/columns.tsx b/frontend/src/modules/organizations/invites/table/columns.tsx
index 486d4183d..2bc956c75 100644
--- a/frontend/src/modules/organizations/invites/table/columns.tsx
+++ b/frontend/src/modules/organizations/invites/table/columns.tsx
@@ -49,18 +49,9 @@ export const useColumns = () => {
minWidth: 100,
},
- {
- key: 'expiredAt',
- name: t('common:expires_at'),
- sortable: true,
- visible: true,
- renderHeaderCell: HeaderCell,
- renderCell: ({ row }) => dateShort(row.expiredAt),
- minWidth: 80,
- },
{
key: 'createdAt',
- name: t('common:created_at'),
+ name: t('common:invited_at'),
sortable: true,
visible: !isMobile,
renderHeaderCell: HeaderCell,
@@ -69,13 +60,22 @@ export const useColumns = () => {
},
{
key: 'createdBy',
- name: t('common:created_by'),
+ name: t('common:invited_by'),
sortable: true,
visible: !isMobile,
renderHeaderCell: HeaderCell,
renderCell: ({ row }) => row.createdBy,
minWidth: 80,
},
+ {
+ key: 'expiresAt',
+ name: t('common:expires_at'),
+ sortable: true,
+ visible: true,
+ renderHeaderCell: HeaderCell,
+ renderCell: ({ row }) => dateShort(row.expiresAt),
+ minWidth: 80,
+ },
];
return cols;
diff --git a/frontend/src/modules/organizations/invites/table/index.tsx b/frontend/src/modules/organizations/invites/table/index.tsx
index 213e2a1c6..c0d4cc6fc 100644
--- a/frontend/src/modules/organizations/invites/table/index.tsx
+++ b/frontend/src/modules/organizations/invites/table/index.tsx
@@ -13,7 +13,7 @@ export interface InvitesInfoProps {
info: OrganizationInvitesInfo[];
}
-export type InvitesInfoSearch = { q?: string; order?: 'asc' | 'desc'; sort: 'expiredAt' | 'createdAt' | 'createdBy' };
+export type InvitesInfoSearch = { q?: string; order?: 'asc' | 'desc'; sort: 'expiresAt' | 'createdAt' | 'createdBy' };
export const InvitesInfoTable = ({ info }: InvitesInfoProps) => {
const { search, setSearch } = useSearchParams