Uses the router to handle tab state
This commit is contained in:
@@ -41,13 +41,14 @@ export function RelationshipList({
|
|||||||
|
|
||||||
const relationshipResult = docResult.value.relationships[relationshipType];
|
const relationshipResult = docResult.value.relationships[relationshipType];
|
||||||
|
|
||||||
if (relationshipResult?.type !== "ready") {
|
const relationship =
|
||||||
return <Loader />;
|
relationshipResult?.type === "ready" ? relationshipResult.value : null;
|
||||||
}
|
|
||||||
|
|
||||||
const relationship = relationshipResult.value;
|
const itemIds =
|
||||||
|
relationshipResult?.type === "ready"
|
||||||
|
? relationshipResult.value.secondary
|
||||||
|
: [];
|
||||||
|
|
||||||
const itemIds = relationship.secondary ?? [];
|
|
||||||
const items = itemIds
|
const items = itemIds
|
||||||
.map((id) => cache.documents[id])
|
.map((id) => cache.documents[id])
|
||||||
.filter((d) => d && d.type === "ready")
|
.filter((d) => d && d.type === "ready")
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { RelationshipList } from "@/components/RelationshipList";
|
|
||||||
import { DocumentEditForm } from "@/components/documents/DocumentEditForm";
|
import { DocumentEditForm } from "@/components/documents/DocumentEditForm";
|
||||||
import { useDocument } from "@/context/document/hooks";
|
import { useDocument } from "@/context/document/hooks";
|
||||||
import { displayName, relationshipsForDocument } from "@/lib/relationships";
|
import { displayName, relationshipsForDocument } from "@/lib/relationships";
|
||||||
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@headlessui/react";
|
|
||||||
import { Link } from "@tanstack/react-router";
|
|
||||||
import { Loader } from "../Loader";
|
|
||||||
import type { DocumentId } from "@/lib/types";
|
import type { DocumentId } from "@/lib/types";
|
||||||
|
import { Route as CampaignRoute } from "@/routes/_app/_authenticated/campaigns.$campaignId";
|
||||||
|
import { Link } from "@tanstack/react-router";
|
||||||
|
import * as _ from "lodash";
|
||||||
|
import { Loader } from "../Loader";
|
||||||
|
import { Route as RelationshipRoute } from "@/routes/_app/_authenticated/document.$documentId/$relationshipType";
|
||||||
|
|
||||||
export function DocumentView({ documentId }: { documentId: DocumentId }) {
|
export function DocumentView({ documentId }: { documentId: DocumentId }) {
|
||||||
const { docResult } = useDocument(documentId);
|
const { docResult } = useDocument(documentId);
|
||||||
@@ -17,6 +18,12 @@ export function DocumentView({ documentId }: { documentId: DocumentId }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const doc = docResult.value.doc;
|
const doc = docResult.value.doc;
|
||||||
|
const relationshipCounts = _.mapValues(docResult.value.relationships, (v) => {
|
||||||
|
if (v.type === "ready") {
|
||||||
|
return v.value.secondary.length;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
const relationshipList = relationshipsForDocument(doc);
|
const relationshipList = relationshipsForDocument(doc);
|
||||||
|
|
||||||
@@ -24,8 +31,8 @@ export function DocumentView({ documentId }: { documentId: DocumentId }) {
|
|||||||
<div key={doc.id} className="max-w-xl mx-auto py-2 px-4">
|
<div key={doc.id} className="max-w-xl mx-auto py-2 px-4">
|
||||||
<div>
|
<div>
|
||||||
<Link
|
<Link
|
||||||
to="/document/$documentId/print"
|
to={CampaignRoute.to}
|
||||||
params={{ documentId: doc.id }}
|
params={{ campaignId: doc.campaign }}
|
||||||
className="text-slate-400 hover:text-violet-400 text-sm underline underline-offset-2 transition-colors mb-4"
|
className="text-slate-400 hover:text-violet-400 text-sm underline underline-offset-2 transition-colors mb-4"
|
||||||
>
|
>
|
||||||
Back to campaign
|
Back to campaign
|
||||||
@@ -39,29 +46,27 @@ export function DocumentView({ documentId }: { documentId: DocumentId }) {
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<DocumentEditForm document={doc} />
|
<DocumentEditForm document={doc} />
|
||||||
<TabGroup>
|
<nav>
|
||||||
<TabList className="flex flex-row flex-wrap gap-1 mt-2">
|
<ul className="flex flex-row gap-1">
|
||||||
{relationshipList.map((relationshipType) => (
|
{relationshipList.map((relationshipType) => (
|
||||||
<Tab
|
<Link
|
||||||
key={relationshipType}
|
to={RelationshipRoute.to}
|
||||||
className="px-3 py-2 rounded bg-slate-800 text-slate-100 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-violet-500 data-selected:bg-violet-900 data-selected:border-violet-700"
|
params={{
|
||||||
|
documentId,
|
||||||
|
relationshipType,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{displayName(relationshipType)}
|
<div
|
||||||
</Tab>
|
|
||||||
))}
|
|
||||||
</TabList>
|
|
||||||
<TabPanels>
|
|
||||||
{relationshipList.map((relationshipType) => (
|
|
||||||
<TabPanel key={relationshipType}>
|
|
||||||
<RelationshipList
|
|
||||||
key={relationshipType}
|
key={relationshipType}
|
||||||
root={doc}
|
className="px-3 py-2 rounded bg-slate-800 text-slate-100 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-violet-500 data-selected:bg-violet-900 data-selected:border-violet-700 whitespace-nowrap"
|
||||||
relationshipType={relationshipType}
|
>
|
||||||
/>
|
{displayName(relationshipType)} (
|
||||||
</TabPanel>
|
{relationshipCounts[relationshipType] ?? 0})
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
))}
|
))}
|
||||||
</TabPanels>
|
</ul>
|
||||||
</TabGroup>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
31
src/components/documents/RelatedDocumentList.tsx
Normal file
31
src/components/documents/RelatedDocumentList.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { useDocument, useDocumentCache } from "@/context/document/hooks";
|
||||||
|
import type { DocumentId, RelationshipType } from "@/lib/types";
|
||||||
|
import { RelationshipList } from "../RelationshipList";
|
||||||
|
import { Loader } from "../Loader";
|
||||||
|
|
||||||
|
export type Props = {
|
||||||
|
documentId: DocumentId;
|
||||||
|
relationshipType: RelationshipType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function RelatedDocumentList({ documentId, relationshipType }: Props) {
|
||||||
|
const { docResult } = useDocument(documentId);
|
||||||
|
|
||||||
|
const { cache } = useDocumentCache();
|
||||||
|
|
||||||
|
console.log(documentId, docResult, cache);
|
||||||
|
|
||||||
|
if (docResult?.type !== "ready") {
|
||||||
|
return <Loader />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const doc = docResult.value.doc;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RelationshipList
|
||||||
|
key={relationshipType}
|
||||||
|
root={doc}
|
||||||
|
relationshipType={relationshipType}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ import { Route as AppAuthenticatedCampaignsIndexImport } from './routes/_app/_au
|
|||||||
import { Route as AppAuthenticatedDocumentDocumentIdImport } from './routes/_app/_authenticated/document.$documentId'
|
import { Route as AppAuthenticatedDocumentDocumentIdImport } from './routes/_app/_authenticated/document.$documentId'
|
||||||
import { Route as AppAuthenticatedCampaignsCampaignIdImport } from './routes/_app/_authenticated/campaigns.$campaignId'
|
import { Route as AppAuthenticatedCampaignsCampaignIdImport } from './routes/_app/_authenticated/campaigns.$campaignId'
|
||||||
import { Route as AppauthenticatedDocumentDocumentIdPrintImport } from './routes/_app_._authenticated.document_.$documentId.print'
|
import { Route as AppauthenticatedDocumentDocumentIdPrintImport } from './routes/_app_._authenticated.document_.$documentId.print'
|
||||||
|
import { Route as AppAuthenticatedDocumentDocumentIdRelationshipTypeImport } from './routes/_app/_authenticated/document.$documentId/$relationshipType'
|
||||||
|
|
||||||
// Create/Update Routes
|
// Create/Update Routes
|
||||||
|
|
||||||
@@ -79,6 +80,13 @@ const AppauthenticatedDocumentDocumentIdPrintRoute =
|
|||||||
getParentRoute: () => rootRoute,
|
getParentRoute: () => rootRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
|
||||||
|
const AppAuthenticatedDocumentDocumentIdRelationshipTypeRoute =
|
||||||
|
AppAuthenticatedDocumentDocumentIdRelationshipTypeImport.update({
|
||||||
|
id: '/$relationshipType',
|
||||||
|
path: '/$relationshipType',
|
||||||
|
getParentRoute: () => AppAuthenticatedDocumentDocumentIdRoute,
|
||||||
|
} as any)
|
||||||
|
|
||||||
// Populate the FileRoutesByPath interface
|
// Populate the FileRoutesByPath interface
|
||||||
|
|
||||||
declare module '@tanstack/react-router' {
|
declare module '@tanstack/react-router' {
|
||||||
@@ -139,6 +147,13 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof AppAuthenticatedCampaignsIndexImport
|
preLoaderRoute: typeof AppAuthenticatedCampaignsIndexImport
|
||||||
parentRoute: typeof AppAuthenticatedImport
|
parentRoute: typeof AppAuthenticatedImport
|
||||||
}
|
}
|
||||||
|
'/_app/_authenticated/document/$documentId/$relationshipType': {
|
||||||
|
id: '/_app/_authenticated/document/$documentId/$relationshipType'
|
||||||
|
path: '/$relationshipType'
|
||||||
|
fullPath: '/document/$documentId/$relationshipType'
|
||||||
|
preLoaderRoute: typeof AppAuthenticatedDocumentDocumentIdRelationshipTypeImport
|
||||||
|
parentRoute: typeof AppAuthenticatedDocumentDocumentIdImport
|
||||||
|
}
|
||||||
'/_app_/_authenticated/document_/$documentId/print': {
|
'/_app_/_authenticated/document_/$documentId/print': {
|
||||||
id: '/_app_/_authenticated/document_/$documentId/print'
|
id: '/_app_/_authenticated/document_/$documentId/print'
|
||||||
path: '/document/$documentId/print'
|
path: '/document/$documentId/print'
|
||||||
@@ -151,9 +166,24 @@ declare module '@tanstack/react-router' {
|
|||||||
|
|
||||||
// Create and export the route tree
|
// Create and export the route tree
|
||||||
|
|
||||||
|
interface AppAuthenticatedDocumentDocumentIdRouteChildren {
|
||||||
|
AppAuthenticatedDocumentDocumentIdRelationshipTypeRoute: typeof AppAuthenticatedDocumentDocumentIdRelationshipTypeRoute
|
||||||
|
}
|
||||||
|
|
||||||
|
const AppAuthenticatedDocumentDocumentIdRouteChildren: AppAuthenticatedDocumentDocumentIdRouteChildren =
|
||||||
|
{
|
||||||
|
AppAuthenticatedDocumentDocumentIdRelationshipTypeRoute:
|
||||||
|
AppAuthenticatedDocumentDocumentIdRelationshipTypeRoute,
|
||||||
|
}
|
||||||
|
|
||||||
|
const AppAuthenticatedDocumentDocumentIdRouteWithChildren =
|
||||||
|
AppAuthenticatedDocumentDocumentIdRoute._addFileChildren(
|
||||||
|
AppAuthenticatedDocumentDocumentIdRouteChildren,
|
||||||
|
)
|
||||||
|
|
||||||
interface AppAuthenticatedRouteChildren {
|
interface AppAuthenticatedRouteChildren {
|
||||||
AppAuthenticatedCampaignsCampaignIdRoute: typeof AppAuthenticatedCampaignsCampaignIdRoute
|
AppAuthenticatedCampaignsCampaignIdRoute: typeof AppAuthenticatedCampaignsCampaignIdRoute
|
||||||
AppAuthenticatedDocumentDocumentIdRoute: typeof AppAuthenticatedDocumentDocumentIdRoute
|
AppAuthenticatedDocumentDocumentIdRoute: typeof AppAuthenticatedDocumentDocumentIdRouteWithChildren
|
||||||
AppAuthenticatedCampaignsIndexRoute: typeof AppAuthenticatedCampaignsIndexRoute
|
AppAuthenticatedCampaignsIndexRoute: typeof AppAuthenticatedCampaignsIndexRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +191,7 @@ const AppAuthenticatedRouteChildren: AppAuthenticatedRouteChildren = {
|
|||||||
AppAuthenticatedCampaignsCampaignIdRoute:
|
AppAuthenticatedCampaignsCampaignIdRoute:
|
||||||
AppAuthenticatedCampaignsCampaignIdRoute,
|
AppAuthenticatedCampaignsCampaignIdRoute,
|
||||||
AppAuthenticatedDocumentDocumentIdRoute:
|
AppAuthenticatedDocumentDocumentIdRoute:
|
||||||
AppAuthenticatedDocumentDocumentIdRoute,
|
AppAuthenticatedDocumentDocumentIdRouteWithChildren,
|
||||||
AppAuthenticatedCampaignsIndexRoute: AppAuthenticatedCampaignsIndexRoute,
|
AppAuthenticatedCampaignsIndexRoute: AppAuthenticatedCampaignsIndexRoute,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,8 +220,9 @@ export interface FileRoutesByFullPath {
|
|||||||
'/login': typeof AppLoginRoute
|
'/login': typeof AppLoginRoute
|
||||||
'/': typeof AppIndexRoute
|
'/': typeof AppIndexRoute
|
||||||
'/campaigns/$campaignId': typeof AppAuthenticatedCampaignsCampaignIdRoute
|
'/campaigns/$campaignId': typeof AppAuthenticatedCampaignsCampaignIdRoute
|
||||||
'/document/$documentId': typeof AppAuthenticatedDocumentDocumentIdRoute
|
'/document/$documentId': typeof AppAuthenticatedDocumentDocumentIdRouteWithChildren
|
||||||
'/campaigns': typeof AppAuthenticatedCampaignsIndexRoute
|
'/campaigns': typeof AppAuthenticatedCampaignsIndexRoute
|
||||||
|
'/document/$documentId/$relationshipType': typeof AppAuthenticatedDocumentDocumentIdRelationshipTypeRoute
|
||||||
'/document/$documentId/print': typeof AppauthenticatedDocumentDocumentIdPrintRoute
|
'/document/$documentId/print': typeof AppauthenticatedDocumentDocumentIdPrintRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,8 +232,9 @@ export interface FileRoutesByTo {
|
|||||||
'/login': typeof AppLoginRoute
|
'/login': typeof AppLoginRoute
|
||||||
'/': typeof AppIndexRoute
|
'/': typeof AppIndexRoute
|
||||||
'/campaigns/$campaignId': typeof AppAuthenticatedCampaignsCampaignIdRoute
|
'/campaigns/$campaignId': typeof AppAuthenticatedCampaignsCampaignIdRoute
|
||||||
'/document/$documentId': typeof AppAuthenticatedDocumentDocumentIdRoute
|
'/document/$documentId': typeof AppAuthenticatedDocumentDocumentIdRouteWithChildren
|
||||||
'/campaigns': typeof AppAuthenticatedCampaignsIndexRoute
|
'/campaigns': typeof AppAuthenticatedCampaignsIndexRoute
|
||||||
|
'/document/$documentId/$relationshipType': typeof AppAuthenticatedDocumentDocumentIdRelationshipTypeRoute
|
||||||
'/document/$documentId/print': typeof AppauthenticatedDocumentDocumentIdPrintRoute
|
'/document/$documentId/print': typeof AppauthenticatedDocumentDocumentIdPrintRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,8 +246,9 @@ export interface FileRoutesById {
|
|||||||
'/_app/login': typeof AppLoginRoute
|
'/_app/login': typeof AppLoginRoute
|
||||||
'/_app/': typeof AppIndexRoute
|
'/_app/': typeof AppIndexRoute
|
||||||
'/_app/_authenticated/campaigns/$campaignId': typeof AppAuthenticatedCampaignsCampaignIdRoute
|
'/_app/_authenticated/campaigns/$campaignId': typeof AppAuthenticatedCampaignsCampaignIdRoute
|
||||||
'/_app/_authenticated/document/$documentId': typeof AppAuthenticatedDocumentDocumentIdRoute
|
'/_app/_authenticated/document/$documentId': typeof AppAuthenticatedDocumentDocumentIdRouteWithChildren
|
||||||
'/_app/_authenticated/campaigns/': typeof AppAuthenticatedCampaignsIndexRoute
|
'/_app/_authenticated/campaigns/': typeof AppAuthenticatedCampaignsIndexRoute
|
||||||
|
'/_app/_authenticated/document/$documentId/$relationshipType': typeof AppAuthenticatedDocumentDocumentIdRelationshipTypeRoute
|
||||||
'/_app_/_authenticated/document_/$documentId/print': typeof AppauthenticatedDocumentDocumentIdPrintRoute
|
'/_app_/_authenticated/document_/$documentId/print': typeof AppauthenticatedDocumentDocumentIdPrintRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,6 +262,7 @@ export interface FileRouteTypes {
|
|||||||
| '/campaigns/$campaignId'
|
| '/campaigns/$campaignId'
|
||||||
| '/document/$documentId'
|
| '/document/$documentId'
|
||||||
| '/campaigns'
|
| '/campaigns'
|
||||||
|
| '/document/$documentId/$relationshipType'
|
||||||
| '/document/$documentId/print'
|
| '/document/$documentId/print'
|
||||||
fileRoutesByTo: FileRoutesByTo
|
fileRoutesByTo: FileRoutesByTo
|
||||||
to:
|
to:
|
||||||
@@ -239,6 +273,7 @@ export interface FileRouteTypes {
|
|||||||
| '/campaigns/$campaignId'
|
| '/campaigns/$campaignId'
|
||||||
| '/document/$documentId'
|
| '/document/$documentId'
|
||||||
| '/campaigns'
|
| '/campaigns'
|
||||||
|
| '/document/$documentId/$relationshipType'
|
||||||
| '/document/$documentId/print'
|
| '/document/$documentId/print'
|
||||||
id:
|
id:
|
||||||
| '__root__'
|
| '__root__'
|
||||||
@@ -250,6 +285,7 @@ export interface FileRouteTypes {
|
|||||||
| '/_app/_authenticated/campaigns/$campaignId'
|
| '/_app/_authenticated/campaigns/$campaignId'
|
||||||
| '/_app/_authenticated/document/$documentId'
|
| '/_app/_authenticated/document/$documentId'
|
||||||
| '/_app/_authenticated/campaigns/'
|
| '/_app/_authenticated/campaigns/'
|
||||||
|
| '/_app/_authenticated/document/$documentId/$relationshipType'
|
||||||
| '/_app_/_authenticated/document_/$documentId/print'
|
| '/_app_/_authenticated/document_/$documentId/print'
|
||||||
fileRoutesById: FileRoutesById
|
fileRoutesById: FileRoutesById
|
||||||
}
|
}
|
||||||
@@ -315,12 +351,19 @@ export const routeTree = rootRoute
|
|||||||
},
|
},
|
||||||
"/_app/_authenticated/document/$documentId": {
|
"/_app/_authenticated/document/$documentId": {
|
||||||
"filePath": "_app/_authenticated/document.$documentId.tsx",
|
"filePath": "_app/_authenticated/document.$documentId.tsx",
|
||||||
"parent": "/_app/_authenticated"
|
"parent": "/_app/_authenticated",
|
||||||
|
"children": [
|
||||||
|
"/_app/_authenticated/document/$documentId/$relationshipType"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"/_app/_authenticated/campaigns/": {
|
"/_app/_authenticated/campaigns/": {
|
||||||
"filePath": "_app/_authenticated/campaigns.index.tsx",
|
"filePath": "_app/_authenticated/campaigns.index.tsx",
|
||||||
"parent": "/_app/_authenticated"
|
"parent": "/_app/_authenticated"
|
||||||
},
|
},
|
||||||
|
"/_app/_authenticated/document/$documentId/$relationshipType": {
|
||||||
|
"filePath": "_app/_authenticated/document.$documentId/$relationshipType.tsx",
|
||||||
|
"parent": "/_app/_authenticated/document/$documentId"
|
||||||
|
},
|
||||||
"/_app_/_authenticated/document_/$documentId/print": {
|
"/_app_/_authenticated/document_/$documentId/print": {
|
||||||
"filePath": "_app_._authenticated.document_.$documentId.print.tsx"
|
"filePath": "_app_._authenticated.document_.$documentId.print.tsx"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { DocumentView } from "@/components/documents/DocumentView";
|
import { DocumentView } from "@/components/documents/DocumentView";
|
||||||
import { DocumentLoader } from "@/context/document/DocumentLoader";
|
import { DocumentLoader } from "@/context/document/DocumentLoader";
|
||||||
import type { DocumentId } from "@/lib/types";
|
import type { DocumentId } from "@/lib/types";
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute, Outlet } from "@tanstack/react-router";
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
export const Route = createFileRoute(
|
||||||
"/_app/_authenticated/document/$documentId",
|
"/_app/_authenticated/document/$documentId",
|
||||||
@@ -15,6 +15,7 @@ function RouteComponent() {
|
|||||||
return (
|
return (
|
||||||
<DocumentLoader documentId={documentId as DocumentId}>
|
<DocumentLoader documentId={documentId as DocumentId}>
|
||||||
<DocumentView documentId={documentId as DocumentId} />
|
<DocumentView documentId={documentId as DocumentId} />
|
||||||
|
<Outlet />
|
||||||
</DocumentLoader>
|
</DocumentLoader>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { RelatedDocumentList } from "@/components/documents/RelatedDocumentList";
|
||||||
|
import type { DocumentId, RelationshipType } from "@/lib/types";
|
||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
export const Route = createFileRoute(
|
||||||
|
"/_app/_authenticated/document/$documentId/$relationshipType",
|
||||||
|
)({
|
||||||
|
component: RouteComponent,
|
||||||
|
});
|
||||||
|
|
||||||
|
function RouteComponent() {
|
||||||
|
const { documentId, relationshipType } = Route.useParams();
|
||||||
|
return (
|
||||||
|
<RelatedDocumentList
|
||||||
|
documentId={documentId as DocumentId}
|
||||||
|
relationshipType={relationshipType as RelationshipType}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user