From d8a20ebbd9118989690a40fcd3aa59fb156b446f Mon Sep 17 00:00:00 2001 From: Guy Ben-Aharon Date: Mon, 4 Nov 2024 18:01:04 +0200 Subject: [PATCH] fix(templates): fetch templates data from router (#321) --- src/pages/templates-page/templates-page.tsx | 46 ++++++--------------- src/router.tsx | 31 ++++++++++++++ src/templates-data/template-utils.ts | 29 ++++++++++++- 3 files changed, 71 insertions(+), 35 deletions(-) diff --git a/src/pages/templates-page/templates-page.tsx b/src/pages/templates-page/templates-page.tsx index 3c9cd7ec..658f8569 100644 --- a/src/pages/templates-page/templates-page.tsx +++ b/src/pages/templates-page/templates-page.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React from 'react'; import ChartDBLogo from '@/assets/logo-light.png'; import ChartDBDarkLogo from '@/assets/logo-dark.png'; import { useTheme } from '@/hooks/use-theme'; @@ -7,49 +7,27 @@ import { ThemeProvider } from '@/context/theme-context/theme-provider'; import { Component, Star } from 'lucide-react'; import { ListMenu } from '@/components/list-menu/list-menu'; import { TemplateCard } from './template-card/template-card'; -import { useMatches, useParams } from 'react-router-dom'; +import { useLoaderData, useMatches, useParams } from 'react-router-dom'; import type { Template } from '@/templates-data/templates-data'; import { Spinner } from '@/components/spinner/spinner'; -import { removeDups } from '@/lib/utils'; import { Helmet } from 'react-helmet-async'; +export interface TemplatesPageLoaderData { + templates: Template[] | undefined; + allTags: string[] | undefined; +} + const TemplatesPageComponent: React.FC = () => { const { effectiveTheme } = useTheme(); + const data = useLoaderData() as TemplatesPageLoaderData; + + const { templates, allTags } = data ?? {}; const { tag } = useParams<{ tag: string }>(); const matches = useMatches(); - const [templates, setTemplates] = React.useState(); - const [tags, setTags] = React.useState(); - const isFeatured = matches.some( (match) => match.id === 'templates_featured' ); const isAllTemplates = matches.some((match) => match.id === 'templates'); - const isTags = matches.some((match) => match.id === 'templates_tags'); - - useEffect(() => { - const loadTemplates = async () => { - const { templates: loadedTemplates } = await import( - '@/templates-data/templates-data' - ); - - let templatesToLoad = loadedTemplates; - - if (isFeatured) { - templatesToLoad = loadedTemplates.filter((t) => t.featured); - } - - if (isTags && tag) { - templatesToLoad = loadedTemplates.filter((t) => - t.tags.includes(tag) - ); - } - - setTemplates(templatesToLoad); - setTags(removeDups(loadedTemplates?.flatMap((t) => t.tags) ?? [])); - }; - - loadTemplates(); - }, [isFeatured, isTags, tag]); return ( <> @@ -125,10 +103,10 @@ const TemplatesPageComponent: React.FC = () => {

Tags

- {tags ? ( + {allTags ? ( ({ + items={allTags.map((currentTag) => ({ title: currentTag, href: `/templates/tags/${currentTag}`, selected: tag === currentTag, diff --git a/src/router.tsx b/src/router.tsx index 87c454ce..fd14a348 100644 --- a/src/router.tsx +++ b/src/router.tsx @@ -2,6 +2,8 @@ import React from 'react'; import type { RouteObject } from 'react-router-dom'; import { createBrowserRouter } from 'react-router-dom'; import type { TemplatePageLoaderData } from './pages/template-page/template-page'; +import type { TemplatesPageLoaderData } from './pages/templates-page/templates-page'; +import { getTemplatesAndAllTags } from './templates-data/template-utils'; const routes: RouteObject[] = [ ...['', 'diagrams/:diagramId'].map((path) => ({ @@ -38,6 +40,15 @@ const routes: RouteObject[] = [ element: , }; }, + + loader: async (): Promise => { + const { tags, templates } = await getTemplatesAndAllTags(); + + return { + allTags: tags, + templates, + }; + }, }, { id: 'templates_featured', @@ -50,6 +61,16 @@ const routes: RouteObject[] = [ element: , }; }, + loader: async (): Promise => { + const { tags, templates } = await getTemplatesAndAllTags({ + featured: true, + }); + + return { + allTags: tags, + templates, + }; + }, }, { id: 'templates_tags', @@ -62,6 +83,16 @@ const routes: RouteObject[] = [ element: , }; }, + loader: async ({ params }): Promise => { + const { tags, templates } = await getTemplatesAndAllTags({ + tag: params.tag, + }); + + return { + allTags: tags, + templates, + }; + }, }, { id: 'templates_templateSlug', diff --git a/src/templates-data/template-utils.ts b/src/templates-data/template-utils.ts index 17c37ec0..2b77fe5f 100644 --- a/src/templates-data/template-utils.ts +++ b/src/templates-data/template-utils.ts @@ -1,6 +1,6 @@ import type { Diagram } from '@/lib/domain/diagram'; import type { Template } from './templates-data'; -import { generateId } from '@/lib/utils'; +import { generateId, removeDups } from '@/lib/utils'; import type { DBTable } from '@/lib/domain/db-table'; import type { DBField } from '@/lib/domain/db-field'; import type { DBIndex } from '@/lib/domain/db-index'; @@ -87,3 +87,30 @@ export const convertTemplateToNewDiagram = (template: Template): Diagram => { tables, }; }; + +export const getTemplatesAndAllTags = async ({ + featured, + tag, +}: { + featured?: boolean; + tag?: string; +} = {}): Promise<{ templates: Template[]; tags: string[] }> => { + const { templates } = await import('@/templates-data/templates-data'); + const allTags = removeDups(templates?.flatMap((t) => t.tags) ?? []); + + if (featured) { + return { + templates: templates.filter((t) => t.featured), + tags: allTags, + }; + } + + if (tag) { + return { + templates: templates.filter((t) => t.tags.includes(tag)), + tags: allTags, + }; + } + + return { templates, tags: allTags }; +};