Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/portfolio/template-library
102 changes: 83 additions & 19 deletions apps/studio/app/(main)/(dashboard)/components/OverviewHome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@
import type { LucideIcon } from "lucide-react";

import Link from "next/link";
import { useSyncExternalStore } from "react";
import { BookOpen, FolderOpen, ArrowRight, BriefcaseBusiness } from "lucide-react";

import { Card } from "@veriworkly/ui";

import {
getDocumentLibrarySnapshot,
subscribeToDocumentLibrary,
DOCUMENT_LIBRARY_SERVER_SNAPSHOT,
} from "@/features/documents/services/document-library";
import DestructiveModal from "@/components/modals/DestructiveModal";
import SyncDetailsModal from "@/components/modals/SyncDetailsModal";
import ShareDocumentModal from "@/components/modals/ShareDocumentModal";
import RenameDocumentModal from "@/components/modals/RenameDocumentModal";

import { useDocumentsWorkspace } from "../documents/useDocumentsWorkspace";
import RecentCard from "./RecentCard";
import OverviewHomeHeader from "./OverviewHomeHeader";
import OverviewReferenceCard from "./OverviewReferenceCard";
Expand All @@ -32,18 +31,33 @@ function MiniLink({ href, icon: Icon, label }: { href: string; icon: LucideIcon;
}

const OverviewHome = () => {
const snapshot = useSyncExternalStore(
subscribeToDocumentLibrary,
() => getDocumentLibrarySnapshot(),
() => DOCUMENT_LIBRARY_SERVER_SNAPSHOT,
);

const totalCount = Object.values(snapshot.counts).reduce((sum, count) => sum + count, 0);

const resumeCount = snapshot.counts.RESUME;
const coverLetterCount = snapshot.counts.COVER_LETTER;

const recentDocs = snapshot.docs.slice(0, 6);
const {
counts,
handleSyncNow,
handleConfirmDelete,
handleKeepLocalOnly,
handleResolveUseCloud,
handleResolveUseLocal,
isDeleting,
deleteTarget,
shareTarget,
renameTarget,
syncDetailsTarget,
setDeleteTarget,
setShareTarget,
setRenameTarget,
setSyncDetailsTargetId,
syncingDocumentId,
syncTargetTelemetry,
totalCount,
visibleDocs,
bump,
} = useDocumentsWorkspace();

const resumeCount = counts.RESUME;
const coverLetterCount = counts.COVER_LETTER;

const recentDocs = visibleDocs.slice(0, 6);

return (
<section className="space-y-7" aria-label="Studio overview">
Expand Down Expand Up @@ -71,7 +85,18 @@ const OverviewHome = () => {

<div className="mt-5 grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
{recentDocs.length > 0 ? (
recentDocs.map((doc) => <RecentCard key={`${doc.type}-${doc.id}`} doc={doc} />)
recentDocs.map((doc) => (
<RecentCard
key={`${doc.type}-${doc.id}`}
doc={doc}
syncing={syncingDocumentId === doc.id}
onDeleteAction={setDeleteTarget}
onShareAction={setShareTarget}
onRenameAction={setRenameTarget}
onSyncNowAction={handleSyncNow}
onSyncDetailsAction={setSyncDetailsTargetId}
/>
))
) : (
<div className="border-border bg-background/70 col-span-full rounded-xl border p-5">
<p className="font-bold">No files yet</p>
Expand Down Expand Up @@ -100,6 +125,45 @@ const OverviewHome = () => {
</aside>
</div>
</Card>

<DestructiveModal
loading={isDeleting}
open={Boolean(deleteTarget)}
onConfirmAction={handleConfirmDelete}
onCloseAction={() => setDeleteTarget(null)}
entityName={deleteTarget?.title ?? "document"}
/>

{syncDetailsTarget ? (
<SyncDetailsModal
document={syncDetailsTarget}
onSyncNow={handleSyncNow}
telemetry={syncTargetTelemetry}
syncingDocumentId={syncingDocumentId}
onKeepLocalOnly={handleKeepLocalOnly}
onResolveUseCloud={handleResolveUseCloud}
onResolveUseLocal={handleResolveUseLocal}
onClose={() => setSyncDetailsTargetId(null)}
/>
) : null}

{shareTarget ? (
<ShareDocumentModal
document={shareTarget}
documentId={shareTarget.id}
documentTitle={shareTarget.title}
onClose={() => setShareTarget(null)}
/>
) : null}

{renameTarget ? (
<RenameDocumentModal
open={Boolean(renameTarget)}
doc={renameTarget}
onClose={() => setRenameTarget(null)}
onSuccess={bump}
/>
) : null}
</section>
);
};
Expand Down
46 changes: 40 additions & 6 deletions apps/studio/app/(main)/(dashboard)/components/RecentCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,32 @@ import { Badge } from "@veriworkly/ui";
import { getDocumentDefinition } from "@/features/documents/core/registry";
import { getDocumentEditorPath } from "@/features/documents/core/routes";
import { type DocumentLibraryItem } from "@/features/documents/services/document-library";
import { DocumentActionsMenu } from "../documents/components/DocumentActionsMenu";

const RecentCard = ({ doc }: { doc: DocumentLibraryItem }) => {
interface RecentCardProps {
doc: DocumentLibraryItem;
syncing: boolean;
onDeleteAction: (doc: DocumentLibraryItem) => void;
onShareAction: (doc: DocumentLibraryItem) => void;
onRenameAction: (doc: DocumentLibraryItem) => void;
onSyncNowAction: (id: string) => void;
onSyncDetailsAction: (id: string) => void;
}

const RecentCard = ({
doc,
syncing,
onDeleteAction,
onShareAction,
onRenameAction,
onSyncNowAction,
onSyncDetailsAction,
}: RecentCardProps) => {
const definition = getDocumentDefinition(doc.type);
const editorPath = getDocumentEditorPath(doc.type, doc.id);

return (
<Link
href={editorPath}
className="border-border bg-background/70 group hover:border-accent/40 hover:bg-card overflow-hidden rounded-xl border transition"
>
<div className="border-border bg-background/70 group hover:border-accent/40 hover:bg-card relative overflow-hidden rounded-xl border transition">
<div className="border-border/70 relative h-32 border-b bg-[color-mix(in_oklab,var(--card)_78%,var(--background))]">
{doc.previewImage ? (
<Image
Expand All @@ -44,7 +60,25 @@ const RecentCard = ({ doc }: { doc: DocumentLibraryItem }) => {

<p className="text-muted mt-1 truncate text-xs">{doc.description || doc.templateName}</p>
</div>
</Link>

<Link
href={editorPath}
aria-label={`Open ${doc.title}`}
className="focus-visible:ring-accent absolute inset-0 z-20 cursor-pointer rounded-xl outline-none focus-visible:ring-2 focus-visible:ring-offset-2"
/>

<DocumentActionsMenu
doc={doc}
syncing={syncing}
onShareAction={onShareAction}
onRenameAction={onRenameAction}
onDeleteAction={onDeleteAction}
onSyncNowAction={onSyncNowAction}
onSyncDetailsAction={onSyncDetailsAction}
className="absolute top-2 right-2 z-30"
triggerClassName="opacity-100 pointer-events-auto md:opacity-0 md:pointer-events-none transition-opacity duration-200 md:group-hover:opacity-100 md:group-hover:pointer-events-auto md:group-focus-within:opacity-100 md:group-focus-within:pointer-events-auto"
/>
</div>
);
};

Expand Down
Loading
Loading