style
This commit is contained in:
@@ -26,6 +26,15 @@ The compose service uses tmpfs storage, giving each test run a clean database.
|
||||
- `ocrmypdf` (optional but recommended): Used by the OCR worker to extract text from PDFs when no embedded text layer is available. Ensure it is installed and available on the worker hosts if OCR is desired.
|
||||
- Quickwit (optional): The Quickwit indexer is used to ingest extracted text for search. Set `QUICKWIT_ENDPOINT` and `QUICKWIT_INDEX` in the environment when running workers if you want indexing jobs to run. The local compose file starts a Quickwit instance on `http://localhost:7280` and seeds the `documents` index automatically.
|
||||
|
||||
## Configuration
|
||||
|
||||
The backend reads its settings from environment variables (see `backend/.env` for local defaults). In particular:
|
||||
|
||||
- `DATABASE_URL` – connection string for the primary Postgres database (required).
|
||||
- `DATABASE_MAX_POOL_SIZE` – optional override for the r2d2 connection pool size. Defaults to `2`; increase it in staging/production to match expected concurrency.
|
||||
|
||||
On startup each binary logs the effective configuration with secrets redacted (for example, the database password is masked). This makes it easier to confirm the runtime settings in staging without exposing credentials.
|
||||
|
||||
## Running Migrations in Kubernetes
|
||||
|
||||
The backend container image ships the `diesel` CLI, so schema migrations can be executed as a short-lived Job (or Helm hook) before rolling out new pods. Example manifest:
|
||||
|
||||
@@ -76,6 +76,7 @@ const DocumentsTable = ({
|
||||
onDocumentTagDrop,
|
||||
viewMode = 'list',
|
||||
onViewModeChange,
|
||||
onClearSelection,
|
||||
}) => {
|
||||
const showingSearchResults = searchResults !== null;
|
||||
const rows = showingSearchResults ? searchResults : documents;
|
||||
@@ -213,6 +214,16 @@ const DocumentsTable = ({
|
||||
[isTagDragEvent, onDocumentTagDrop],
|
||||
);
|
||||
|
||||
const handleGridBackgroundClick = useCallback(
|
||||
(event) => {
|
||||
if (event.target !== event.currentTarget) {
|
||||
return;
|
||||
}
|
||||
onClearSelection?.();
|
||||
},
|
||||
[onClearSelection],
|
||||
);
|
||||
|
||||
return (
|
||||
<section
|
||||
className={`documents-panel column documents-panel--view-${isGridView ? 'grid' : 'list'}`}
|
||||
@@ -307,7 +318,11 @@ const DocumentsTable = ({
|
||||
Drop files anywhere or onto a folder to upload documents.
|
||||
</div>
|
||||
) : isGridView ? (
|
||||
<div className="documents-grid" role="list">
|
||||
<div
|
||||
className="documents-grid"
|
||||
role="list"
|
||||
onClick={handleGridBackgroundClick}
|
||||
>
|
||||
{!showingSearchResults &&
|
||||
subfolders.map((folder) => {
|
||||
const canDragFolder = folder.id !== 'root';
|
||||
@@ -353,7 +368,7 @@ const DocumentsTable = ({
|
||||
}}
|
||||
>
|
||||
<div className="folder-card__icon">
|
||||
<FolderIcon className="icon-inline" size={32} />
|
||||
<FolderIcon className="folder-card__icon-svg" size={128} />
|
||||
</div>
|
||||
<div className="folder-card__meta">
|
||||
<div className="folder-card__name" title={folder.name}>
|
||||
|
||||
@@ -1246,6 +1246,11 @@ const AppLayout = () => {
|
||||
[handleRowSelection],
|
||||
);
|
||||
|
||||
const clearDocumentSelection = useCallback(() => {
|
||||
setFocusedRowKey(null);
|
||||
applySelection([], { anchor: null, interactedKeys: [] });
|
||||
}, [applySelection]);
|
||||
|
||||
const handleFolderRowClick = useCallback(
|
||||
(folderId, event) => {
|
||||
const rowKey = resolveFolderRowKey(folderId);
|
||||
@@ -4522,6 +4527,7 @@ const AppLayout = () => {
|
||||
onDocumentTagDrop: handleDocumentTagDrop,
|
||||
viewMode: documentsViewMode,
|
||||
onViewModeChange: handleDocumentsViewModeChange,
|
||||
onClearSelection: clearDocumentSelection,
|
||||
};
|
||||
|
||||
const detailPanelProps = {
|
||||
|
||||
+10
-14
@@ -1309,7 +1309,7 @@ button.icon-button.ghost:hover:not([disabled]) {
|
||||
.documents-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(128px, 1fr));
|
||||
gap: 0.2rem 1.3rem;
|
||||
gap: 0.3rem 1.3rem;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@@ -1317,7 +1317,7 @@ button.icon-button.ghost:hover:not([disabled]) {
|
||||
padding: 0rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.1rem;
|
||||
gap: 0.3rem;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
@@ -1364,6 +1364,7 @@ button.icon-button.ghost:hover:not([disabled]) {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.25rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.folder-card {
|
||||
@@ -1375,11 +1376,16 @@ button.icon-button.ghost:hover:not([disabled]) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
aspect-ratio: 1;
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.folder-card__icon-svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.folder-card__meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -1396,16 +1402,6 @@ button.icon-button.ghost:hover:not([disabled]) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.folder-card__badge {
|
||||
display: inline-flex;
|
||||
align-self: center;
|
||||
padding: 0.12rem 0.45rem;
|
||||
font-size: 0.72rem;
|
||||
border-radius: 999px;
|
||||
background: var(--surface-soft);
|
||||
color: var(--muted);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.doc-name__title {
|
||||
font-weight: 600;
|
||||
|
||||
Reference in New Issue
Block a user