diff --git a/backend/endpoints/v1/organizations/index.js b/backend/endpoints/v1/organizations/index.js
index 8f8a873..aac0fa4 100644
--- a/backend/endpoints/v1/organizations/index.js
+++ b/backend/endpoints/v1/organizations/index.js
@@ -504,7 +504,9 @@ function organizationEndpoints(app) {
true
);
- const totalDocuments = await WorkspaceDocument.count(`organization_id = ${organization.id}`);
+ const totalDocuments = await WorkspaceDocument.count(
+ `organization_id = ${organization.id}`
+ );
response.status(200).json({ documents, totalDocuments });
} catch (e) {
console.log(e.message, e);
diff --git a/backend/endpoints/v1/workspaces/index.js b/backend/endpoints/v1/workspaces/index.js
index 040ccfd..2f826b6 100644
--- a/backend/endpoints/v1/workspaces/index.js
+++ b/backend/endpoints/v1/workspaces/index.js
@@ -174,6 +174,8 @@ function workspaceEndpoints(app) {
async function (request, response) {
try {
const { orgSlug, wsSlug } = request.params;
+ const page = parseInt(request.query.page) || 1;
+ const pageSize = parseInt(request.query.pageSize) || 10;
const user = await userFromSession(request);
if (!user) {
response.sendStatus(403).end();
@@ -196,10 +198,14 @@ function workspaceEndpoints(app) {
const documents = await WorkspaceDocument.where(
`organization_id = ${organization.id} AND workspace_id = ${workspace.id}`,
- null,
+ pageSize,
+ (page - 1) * pageSize,
true
);
- response.status(200).json({ documents });
+ const totalDocuments = await WorkspaceDocument.count(
+ `organization_id = ${organization.id} AND workspace_id = ${workspace.id}`
+ );
+ response.status(200).json({ documents, totalDocuments });
} catch (e) {
console.log(e.message, e);
response.sendStatus(500).end();
diff --git a/backend/models/workspaceDocument.js b/backend/models/workspaceDocument.js
index 9bae90e..2dc77b5 100644
--- a/backend/models/workspaceDocument.js
+++ b/backend/models/workspaceDocument.js
@@ -134,16 +134,25 @@ const WorkspaceDocument = {
count: async function (clause = null) {
const db = await this.db();
const result = await db.get(
- `SELECT COUNT(*) as total FROM ${this.tablename} ${clause ? `WHERE ${clause}` : ""}`
+ `SELECT COUNT(*) as total FROM ${this.tablename} ${
+ clause ? `WHERE ${clause}` : ""
+ }`
);
await db.close();
return result.total;
},
- where: async function (clause = null, limit = null, offset = null, withReferences = false) {
+ where: async function (
+ clause = null,
+ limit = null,
+ offset = null,
+ withReferences = false
+ ) {
if (!withReferences) {
const db = await this.db();
const results = await db.all(
- `SELECT * FROM ${this.tablename} ${clause ? `WHERE ${clause}` : ""} ${offset ? `OFFSET ${offset}` : ""} ${limit ? `LIMIT ${limit}` : ""}`
+ `SELECT * FROM ${this.tablename} ${clause ? `WHERE ${clause}` : ""} ${
+ offset ? `OFFSET ${offset}` : ""
+ } ${limit ? `LIMIT ${limit}` : ""}`
);
await db.close();
return results;
@@ -162,7 +171,9 @@ const WorkspaceDocument = {
LEFT JOIN ${
OrganizationWorkspace.tablename
} as ow ON ow.id = wd.workspace_id
- ${clause ? `WHERE wd.${clause}` : ""} ${limit ? `LIMIT ${limit}` : ""} ${offset ? `OFFSET ${offset}` : ""}`
+ ${clause ? `WHERE wd.${clause}` : ""} ${limit ? `LIMIT ${limit}` : ""} ${
+ offset ? `OFFSET ${offset}` : ""
+ }`
);
await db.close();
diff --git a/frontend/src/components/DocumentPaginator/index.tsx b/frontend/src/components/DocumentPaginator/index.tsx
new file mode 100644
index 0000000..3d72889
--- /dev/null
+++ b/frontend/src/components/DocumentPaginator/index.tsx
@@ -0,0 +1,63 @@
+import { numberWithCommas } from '../../utils/numbers';
+
+function generatePageItems(total: number, current: number) {
+ if (total <= 1) return [];
+ const center = [current - 2, current - 1, current, current + 1, current + 2],
+ filteredCenter = center.filter((p) => p > 1 && p < total),
+ includeThreeLeft = current === 5,
+ includeThreeRight = current === total - 4,
+ includeLeftDots = current > 5,
+ includeRightDots = current < total - 4;
+
+ if (includeThreeLeft) filteredCenter.unshift(2);
+ if (includeThreeRight) filteredCenter.push(total - 1);
+
+ if (includeLeftDots) filteredCenter.unshift('...');
+ if (includeRightDots) filteredCenter.push('...');
+
+ return [1, ...filteredCenter, total];
+}
+
+interface IPaginationProps {
+ pageCount: number;
+ currentPage?: number;
+ gotoPage: (page: number) => void;
+}
+
+export default function DocumentListPagination({
+ pageCount,
+ currentPage = 0,
+ gotoPage,
+}: IPaginationProps) {
+ const pageItems = generatePageItems(pageCount, currentPage);
+ return (
+
+
+ {pageItems.map((item, i) =>
+ typeof item === 'number' ? (
+
+ ) : (
+
+ )
+ )}
+
+
+ );
+}
diff --git a/frontend/src/models/organization.ts b/frontend/src/models/organization.ts
index 60fd4ee..a136282 100644
--- a/frontend/src/models/organization.ts
+++ b/frontend/src/models/organization.ts
@@ -2,6 +2,7 @@ import { API_BASE } from '../utils/constants';
import { baseHeaders } from '../utils/request';
const Organization = {
+ documentPageSize: 25,
create: async (orgName: string) => {
let error;
const organization = await fetch(`${API_BASE}/v1/org/create`, {
@@ -58,20 +59,24 @@ const Organization = {
headers: baseHeaders(),
}).then((res) => res.json());
},
- documents: async (slug: string, page: number = 1) => {
- return fetch(`${API_BASE}/v1/org/${slug}/documents?page=${page}`, {
- method: 'GET',
- cache: 'no-cache',
- headers: baseHeaders(),
- })
+ documents: async (slug: string, page: number = 1, pageSize?: number) => {
+ return fetch(
+ `${API_BASE}/v1/org/${slug}/documents?page=${page}&pageSize=${
+ pageSize || Organization.documentPageSize
+ }`,
+ {
+ method: 'GET',
+ cache: 'no-cache',
+ headers: baseHeaders(),
+ }
+ )
.then((res) => {
console.log('Response:', res);
return res.json();
})
-
.catch((e) => {
console.error(e);
- return {documents: [], totalDocuments: 0};
+ return { documents: [], totalDocuments: 0 };
});
},
workspaces: async (slug: string) => {
diff --git a/frontend/src/models/workspace.ts b/frontend/src/models/workspace.ts
index 717bdd8..b55f2a0 100644
--- a/frontend/src/models/workspace.ts
+++ b/frontend/src/models/workspace.ts
@@ -2,6 +2,7 @@ import { API_BASE } from '../utils/constants';
import { baseHeaders } from '../utils/request';
const Workspace = {
+ documentPageSize: 100,
createNew: async (orgSlug: string, workspaceName: string) => {
let error;
const workspace = await fetch(
@@ -54,9 +55,16 @@ const Workspace = {
}
).then((res) => res.json());
},
- documents: async (orgSlug: string, workspaceSlug: string) => {
+ documents: async (
+ orgSlug: string,
+ workspaceSlug: string,
+ page: number = 1,
+ pageSize?: number
+ ) => {
return fetch(
- `${API_BASE}/v1/org/${orgSlug}/workspace/${workspaceSlug}/documents`,
+ `${API_BASE}/v1/org/${orgSlug}/workspace/${workspaceSlug}/documents?page=${page}&pageSize=${
+ pageSize || Workspace.documentPageSize
+ }`,
{
method: 'GET',
cache: 'no-cache',
@@ -64,10 +72,9 @@ const Workspace = {
}
)
.then((res) => res.json())
- .then((res) => res?.documents || [])
.catch((e) => {
console.error(e);
- return [];
+ return { documents: [], totalDocuments: 0 };
});
},
delete: async (orgSlug: string, workspaceSlug: string) => {
diff --git a/frontend/src/pages/Dashboard/DocumentsList/index.tsx b/frontend/src/pages/Dashboard/DocumentsList/index.tsx
index a8149cc..b6c59d3 100644
--- a/frontend/src/pages/Dashboard/DocumentsList/index.tsx
+++ b/frontend/src/pages/Dashboard/DocumentsList/index.tsx
@@ -9,6 +9,8 @@ import truncate from 'truncate';
import System from '../../../models/system';
import UploadDocumentModal from './UploadModal';
import UploadModalNoKey from './UploadModal/UploadModalNoKey';
+import DocumentListPagination from '../../../components/DocumentPaginator';
+import useQuery from '../../../hooks/useQuery';
export default function DocumentsList({
organization,
@@ -19,12 +21,25 @@ export default function DocumentsList({
workspaces: any;
knownConnector: any;
}) {
+ const query = useQuery();
const [loading, setLoading] = useState(true);
const [documents, setDocuments] = useState([]);
- const [totalDocuments, setTotalDocuments] = useState();
+ const [totalDocuments, setTotalDocuments] = useState(0);
const [canUpload, setCanUpload] = useState(false);
- const [currentPage, setCurrentPage] = useState(1);
- const pageSize = 10;
+ const [currentPage, setCurrentPage] = useState(
+ Number(query.get('docPage')) || 1
+ );
+
+ function updatePage(pgNum: number) {
+ const setTo = pgNum <= 0 ? 1 : pgNum;
+ query.set('docPage', setTo.toString());
+ window.history.replaceState(
+ {},
+ '',
+ `${location.pathname}?${query.toString()}`
+ );
+ setCurrentPage(setTo);
+ }
useEffect(() => {
async function getDocs(slug?: string) {
@@ -131,7 +146,8 @@ export default function DocumentsList({
{documents.map((document) => {
return (
@@ -144,42 +160,40 @@ export default function DocumentsList({
-
-
- {moment(document.created_at).format('MMM D, YYYY')}
+
+
+ {moment.unix(document.createdAt).format('lll')}
-
-
- {document.status}
-
-
+
+ Cached
+
-
@@ -188,38 +202,30 @@ export default function DocumentsList({
>
) : (
-
-
-
- No documents found
-
-
- Once you upload a document, you'll see it here.
-
+
+
+
+
You have no documents in any workspaces!
+
+ Get started managing documents by adding them to workspaces
+ via the UI or code.
+
+
+
)}
-
-
- {Array.from(
- { length: Math.ceil(totalDocuments! / pageSize) },
- (_, i) => i + 1
- ).map((page) => (
-
- ))}
-
-
+
{canUpload ? (
) : (
diff --git a/frontend/src/pages/WorkspaceDashboard/DocumentsList/index.tsx b/frontend/src/pages/WorkspaceDashboard/DocumentsList/index.tsx
index f24ff18..710162f 100644
--- a/frontend/src/pages/WorkspaceDashboard/DocumentsList/index.tsx
+++ b/frontend/src/pages/WorkspaceDashboard/DocumentsList/index.tsx
@@ -10,8 +10,10 @@ import System from '../../../models/system';
import UploadDocumentModal from './UploadModal';
import UploadModalNoKey from './UploadModal/UploadModalNoKey';
import Document from '../../../models/document';
+import useQuery from '../../../hooks/useQuery';
import { APP_NAME } from '../../../utils/constants';
import { useParams } from 'react-router-dom';
+import DocumentListPagination from '../../../components/DocumentPaginator';
export default function DocumentsList({
knownConnector,
@@ -24,9 +26,26 @@ export default function DocumentsList({
workspace: any;
workspaces: any[];
}) {
+ const query = useQuery();
const [loading, setLoading] = useState(true);
const [documents, setDocuments] = useState([]);
+ const [totalDocuments, setTotalDocuments] = useState(0);
const [canUpload, setCanUpload] = useState(false);
+ const [currentPage, setCurrentPage] = useState(
+ Number(query.get('docPage')) || 1
+ );
+
+ function updatePage(pgNum: number) {
+ const setTo = pgNum <= 0 ? 1 : pgNum;
+ query.set('docPage', setTo.toString());
+ window.history.replaceState(
+ {},
+ '',
+ `${location.pathname}?${query.toString()}`
+ );
+ setCurrentPage(setTo);
+ }
+
const deleteDocument = async (documentId: number) => {
if (
!confirm(
@@ -42,11 +61,13 @@ export default function DocumentsList({
useEffect(() => {
async function getDocs(orgSlug: string, wsSlug?: string) {
if (!orgSlug || !wsSlug) return false;
- const documents = await Workspace.documents(orgSlug, wsSlug);
+ const response = await Workspace.documents(orgSlug, wsSlug, currentPage);
const { exists: hasOpenAIKey } = await System.hasSetting(
'open_ai_api_key'
);
- setDocuments(documents);
+
+ setTotalDocuments(response.totalDocuments);
+ setDocuments(response.documents);
setCanUpload(hasOpenAIKey);
setLoading(false);
}
@@ -221,6 +242,11 @@ export default function DocumentsList({
>
)}
+
{canUpload ? (
) : (