mirror of
				https://github.com/chartdb/chartdb.git
				synced 2025-10-31 03:53:55 +00:00 
			
		
		
		
	add helmet instead of updating document.title (#301)
This commit is contained in:
		
							
								
								
									
										35
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								index.html
									
									
									
									
									
								
							| @@ -4,41 +4,8 @@ | |||||||
|         <meta charset="UTF-8" /> |         <meta charset="UTF-8" /> | ||||||
|         <link rel="icon" type="image/svg+xml" href="/favicon.ico" /> |         <link rel="icon" type="image/svg+xml" href="/favicon.ico" /> | ||||||
|         <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |         <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||||
|         <meta |  | ||||||
|             name="description" |  | ||||||
|             content="Free and Open-source database diagrams editor, visualize and design your database with a single query. Tool to help you draw your DB relationship diagrams and export DDL scripts." |  | ||||||
|         /> |  | ||||||
|         <!-- Open Graph / Facebook --> |  | ||||||
|         <meta property="og:type" content="website" /> |  | ||||||
|         <meta |  | ||||||
|             property="og:title" |  | ||||||
|             content="ChartDB - Database schema diagrams visualizer" |  | ||||||
|         /> |  | ||||||
|         <meta |  | ||||||
|             property="og:description" |  | ||||||
|             content="Free and Open-source database diagrams editor, visualize and design your database with a single query. Tool to help you draw your DB relationship diagrams and export DDL scripts." |  | ||||||
|         /> |  | ||||||
|         <meta |  | ||||||
|             property="og:image" |  | ||||||
|             content="https://app.chartdb.io/ChartDB.png" |  | ||||||
|         /> |  | ||||||
|         <meta property="og:url" content="https://app.chartdb.io" /> |  | ||||||
|         <!-- Twitter --> |  | ||||||
|         <meta name="twitter:card" content="summary_large_image" /> |  | ||||||
|         <meta |  | ||||||
|             name="twitter:title" |  | ||||||
|             content="ChartDB - Database schema diagrams visualizer" |  | ||||||
|         /> |  | ||||||
|         <meta |  | ||||||
|             name="twitter:description" |  | ||||||
|             content="Free and Open-source database diagrams editor, visualize and design your database with a single query. Tool to help you draw your DB relationship diagrams and export DDL scripts." |  | ||||||
|         /> |  | ||||||
|         <meta |  | ||||||
|             name="twitter:image" |  | ||||||
|             content="https://github.com/chartdb/chartdb/raw/main/public/ChartDB.png" |  | ||||||
|         /> |  | ||||||
|         <meta name="robots" content="max-image-preview:large" /> |         <meta name="robots" content="max-image-preview:large" /> | ||||||
|         <title>ChartDB - Database schema diagrams visualizer</title> |         <title>ChartDB - Create & Visualize Database Schema Diagrams</title> | ||||||
|         <link rel="preconnect" href="https://fonts.googleapis.com" /> |         <link rel="preconnect" href="https://fonts.googleapis.com" /> | ||||||
|         <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> |         <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> | ||||||
|         <link |         <link | ||||||
|   | |||||||
							
								
								
									
										21
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										21
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -50,6 +50,7 @@ | |||||||
|         "node-sql-parser": "^5.3.2", |         "node-sql-parser": "^5.3.2", | ||||||
|         "react": "^18.3.1", |         "react": "^18.3.1", | ||||||
|         "react-dom": "^18.3.1", |         "react-dom": "^18.3.1", | ||||||
|  |         "react-helmet-async": "^2.0.5", | ||||||
|         "react-hotkeys-hook": "^4.5.0", |         "react-hotkeys-hook": "^4.5.0", | ||||||
|         "react-i18next": "^15.0.1", |         "react-i18next": "^15.0.1", | ||||||
|         "react-resizable-panels": "^2.0.22", |         "react-resizable-panels": "^2.0.22", | ||||||
| @@ -8463,6 +8464,20 @@ | |||||||
|       "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", |       "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", | ||||||
|       "license": "MIT" |       "license": "MIT" | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/react-helmet-async": { | ||||||
|  |       "version": "2.0.5", | ||||||
|  |       "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-2.0.5.tgz", | ||||||
|  |       "integrity": "sha512-rYUYHeus+i27MvFE+Jaa4WsyBKGkL6qVgbJvSBoX8mbsWoABJXdEO0bZyi0F6i+4f0NuIb8AvqPMj3iXFHkMwg==", | ||||||
|  |       "license": "Apache-2.0", | ||||||
|  |       "dependencies": { | ||||||
|  |         "invariant": "^2.2.4", | ||||||
|  |         "react-fast-compare": "^3.2.2", | ||||||
|  |         "shallowequal": "^1.1.0" | ||||||
|  |       }, | ||||||
|  |       "peerDependencies": { | ||||||
|  |         "react": "^16.6.0 || ^17.0.0 || ^18.0.0" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/react-hotkeys-hook": { |     "node_modules/react-hotkeys-hook": { | ||||||
|       "version": "4.5.1", |       "version": "4.5.1", | ||||||
|       "resolved": "https://registry.npmjs.org/react-hotkeys-hook/-/react-hotkeys-hook-4.5.1.tgz", |       "resolved": "https://registry.npmjs.org/react-hotkeys-hook/-/react-hotkeys-hook-4.5.1.tgz", | ||||||
| @@ -9052,6 +9067,12 @@ | |||||||
|       "integrity": "sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg==", |       "integrity": "sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg==", | ||||||
|       "license": "MIT" |       "license": "MIT" | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/shallowequal": { | ||||||
|  |       "version": "1.1.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", | ||||||
|  |       "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", | ||||||
|  |       "license": "MIT" | ||||||
|  |     }, | ||||||
|     "node_modules/shebang-command": { |     "node_modules/shebang-command": { | ||||||
|       "version": "2.0.0", |       "version": "2.0.0", | ||||||
|       "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", |       "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", | ||||||
|   | |||||||
| @@ -54,6 +54,7 @@ | |||||||
|     "node-sql-parser": "^5.3.2", |     "node-sql-parser": "^5.3.2", | ||||||
|     "react": "^18.3.1", |     "react": "^18.3.1", | ||||||
|     "react-dom": "^18.3.1", |     "react-dom": "^18.3.1", | ||||||
|  |     "react-helmet-async": "^2.0.5", | ||||||
|     "react-hotkeys-hook": "^4.5.0", |     "react-hotkeys-hook": "^4.5.0", | ||||||
|     "react-i18next": "^15.0.1", |     "react-i18next": "^15.0.1", | ||||||
|     "react-resizable-panels": "^2.0.22", |     "react-resizable-panels": "^2.0.22", | ||||||
|   | |||||||
							
								
								
									
										43
									
								
								src/app.tsx
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								src/app.tsx
									
									
									
									
									
								
							| @@ -2,11 +2,48 @@ import React from 'react'; | |||||||
| import { RouterProvider } from 'react-router-dom'; | import { RouterProvider } from 'react-router-dom'; | ||||||
| import { router } from './router'; | import { router } from './router'; | ||||||
| import { TooltipProvider } from './components/tooltip/tooltip'; | import { TooltipProvider } from './components/tooltip/tooltip'; | ||||||
|  | import { Helmet, HelmetProvider } from 'react-helmet-async'; | ||||||
|  |  | ||||||
| export const App = () => { | export const App = () => { | ||||||
|     return ( |     return ( | ||||||
|         <TooltipProvider> |         <HelmetProvider> | ||||||
|             <RouterProvider router={router} /> |             <Helmet> | ||||||
|         </TooltipProvider> |                 <meta | ||||||
|  |                     name="description" | ||||||
|  |                     content="Free and Open-source database diagrams editor, visualize and design your database with a single query. Tool to help you draw your DB relationship diagrams and export DDL scripts." | ||||||
|  |                 /> | ||||||
|  |                 <meta property="og:type" content="website" /> | ||||||
|  |                 <meta | ||||||
|  |                     property="og:title" | ||||||
|  |                     content="ChartDB - Database schema diagrams visualizer" | ||||||
|  |                 /> | ||||||
|  |                 <meta | ||||||
|  |                     property="og:description" | ||||||
|  |                     content="Free and Open-source database diagrams editor, visualize and design your database with a single query. Tool to help you draw your DB relationship diagrams and export DDL scripts." | ||||||
|  |                 /> | ||||||
|  |                 <meta | ||||||
|  |                     property="og:image" | ||||||
|  |                     content="https://app.chartdb.io/ChartDB.png" | ||||||
|  |                 /> | ||||||
|  |                 <meta property="og:url" content="https://app.chartdb.io" /> | ||||||
|  |                 <meta name="twitter:card" content="summary_large_image" /> | ||||||
|  |                 <meta | ||||||
|  |                     name="twitter:title" | ||||||
|  |                     content="ChartDB - Database schema diagrams visualizer" | ||||||
|  |                 /> | ||||||
|  |                 <meta | ||||||
|  |                     name="twitter:description" | ||||||
|  |                     content="Free and Open-source database diagrams editor, visualize and design your database with a single query. Tool to help you draw your DB relationship diagrams and export DDL scripts." | ||||||
|  |                 /> | ||||||
|  |                 <meta | ||||||
|  |                     name="twitter:image" | ||||||
|  |                     content="https://github.com/chartdb/chartdb/raw/main/public/ChartDB.png" | ||||||
|  |                 /> | ||||||
|  |                 <title>ChartDB - Database schema diagrams visualizer</title> | ||||||
|  |             </Helmet> | ||||||
|  |             <TooltipProvider> | ||||||
|  |                 <RouterProvider router={router} /> | ||||||
|  |             </TooltipProvider> | ||||||
|  |         </HelmetProvider> | ||||||
|     ); |     ); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| import React, { useCallback, useEffect, useMemo, useState } from 'react'; | import React, { useCallback, useMemo, useState } from 'react'; | ||||||
| import type { DBTable } from '@/lib/domain/db-table'; | import type { DBTable } from '@/lib/domain/db-table'; | ||||||
| import { deepCopy, generateId } from '@/lib/utils'; | import { deepCopy, generateId } from '@/lib/utils'; | ||||||
| import { randomColor } from '@/lib/colors'; | import { randomColor } from '@/lib/colors'; | ||||||
| @@ -28,11 +28,10 @@ import { storageInitialValue } from '../storage-context/storage-context'; | |||||||
| export interface ChartDBProviderProps { | export interface ChartDBProviderProps { | ||||||
|     diagram?: Diagram; |     diagram?: Diagram; | ||||||
|     readonly?: boolean; |     readonly?: boolean; | ||||||
|     skipTitleUpdate?: boolean; |  | ||||||
| } | } | ||||||
| export const ChartDBProvider: React.FC< | export const ChartDBProvider: React.FC< | ||||||
|     React.PropsWithChildren<ChartDBProviderProps> |     React.PropsWithChildren<ChartDBProviderProps> | ||||||
| > = ({ children, diagram, readonly, skipTitleUpdate }) => { | > = ({ children, diagram, readonly }) => { | ||||||
|     let db = useStorage(); |     let db = useStorage(); | ||||||
|     const events = useEventEmitter<ChartDBEvent>(); |     const events = useEventEmitter<ChartDBEvent>(); | ||||||
|     const navigate = useNavigate(); |     const navigate = useNavigate(); | ||||||
| @@ -64,19 +63,6 @@ export const ChartDBProvider: React.FC< | |||||||
|         db = storageInitialValue; |         db = storageInitialValue; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     useEffect(() => { |  | ||||||
|         if (skipTitleUpdate) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (diagramName) { |  | ||||||
|             document.title = `ChartDB - ${diagramName} Diagram | Visualize Database Schemas`; |  | ||||||
|         } else { |  | ||||||
|             document.title = |  | ||||||
|                 'ChartDB - Create & Visualize Database Schema Diagrams'; |  | ||||||
|         } |  | ||||||
|     }, [diagramName, skipTitleUpdate]); |  | ||||||
|  |  | ||||||
|     const schemas = useMemo( |     const schemas = useMemo( | ||||||
|         () => |         () => | ||||||
|             databasesWithSchemas.includes(databaseType) |             databasesWithSchemas.includes(databaseType) | ||||||
|   | |||||||
| @@ -34,6 +34,7 @@ import { ExportImageProvider } from '@/context/export-image-context/export-image | |||||||
| import { DialogProvider } from '@/context/dialog-context/dialog-provider'; | import { DialogProvider } from '@/context/dialog-context/dialog-provider'; | ||||||
| import { KeyboardShortcutsProvider } from '@/context/keyboard-shortcuts-context/keyboard-shortcuts-provider'; | import { KeyboardShortcutsProvider } from '@/context/keyboard-shortcuts-context/keyboard-shortcuts-provider'; | ||||||
| import { Spinner } from '@/components/spinner/spinner'; | import { Spinner } from '@/components/spinner/spinner'; | ||||||
|  | import { Helmet } from 'react-helmet-async'; | ||||||
|  |  | ||||||
| const OPEN_STAR_US_AFTER_SECONDS = 30; | const OPEN_STAR_US_AFTER_SECONDS = 30; | ||||||
| const SHOW_STAR_US_AGAIN_AFTER_DAYS = 1; | const SHOW_STAR_US_AGAIN_AFTER_DAYS = 1; | ||||||
| @@ -47,8 +48,13 @@ export const EditorMobileLayoutLazy = React.lazy( | |||||||
| ); | ); | ||||||
|  |  | ||||||
| const EditorPageComponent: React.FC = () => { | const EditorPageComponent: React.FC = () => { | ||||||
|     const { loadDiagram, currentDiagram, schemas, filteredSchemas } = |     const { | ||||||
|         useChartDB(); |         loadDiagram, | ||||||
|  |         diagramName, | ||||||
|  |         currentDiagram, | ||||||
|  |         schemas, | ||||||
|  |         filteredSchemas, | ||||||
|  |     } = useChartDB(); | ||||||
|     const { openSelectSchema, showSidePanel } = useLayout(); |     const { openSelectSchema, showSidePanel } = useLayout(); | ||||||
|     const { resetRedoStack, resetUndoStack } = useRedoUndoStack(); |     const { resetRedoStack, resetUndoStack } = useRedoUndoStack(); | ||||||
|     const { showLoader, hideLoader } = useFullScreenLoader(); |     const { showLoader, hideLoader } = useFullScreenLoader(); | ||||||
| @@ -210,6 +216,13 @@ const EditorPageComponent: React.FC = () => { | |||||||
|  |  | ||||||
|     return ( |     return ( | ||||||
|         <> |         <> | ||||||
|  |             <Helmet> | ||||||
|  |                 <title> | ||||||
|  |                     {diagramName | ||||||
|  |                         ? `ChartDB - ${diagramName} Diagram | Visualize Database Schemas` | ||||||
|  |                         : 'ChartDB - Create & Visualize Database Schema Diagrams'} | ||||||
|  |                 </title> | ||||||
|  |             </Helmet> | ||||||
|             <section |             <section | ||||||
|                 className={`bg-background ${isDesktop ? 'h-screen w-screen' : 'h-dvh w-dvw'} flex select-none flex-col overflow-x-hidden`} |                 className={`bg-background ${isDesktop ? 'h-screen w-screen' : 'h-dvh w-dvw'} flex select-none flex-col overflow-x-hidden`} | ||||||
|             > |             > | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| import React, { useEffect } from 'react'; | import React from 'react'; | ||||||
| import ChartDBLogo from '@/assets/logo-light.png'; | import ChartDBLogo from '@/assets/logo-light.png'; | ||||||
| import ChartDBDarkLogo from '@/assets/logo-dark.png'; | import ChartDBDarkLogo from '@/assets/logo-dark.png'; | ||||||
| import { examples } from './examples-data/examples-data'; | import { examples } from './examples-data/examples-data'; | ||||||
| @@ -7,51 +7,56 @@ import { useTheme } from '@/hooks/use-theme'; | |||||||
| import { LocalConfigProvider } from '@/context/local-config-context/local-config-provider'; | import { LocalConfigProvider } from '@/context/local-config-context/local-config-provider'; | ||||||
| import { StorageProvider } from '@/context/storage-context/storage-provider'; | import { StorageProvider } from '@/context/storage-context/storage-provider'; | ||||||
| import { ThemeProvider } from '@/context/theme-context/theme-provider'; | import { ThemeProvider } from '@/context/theme-context/theme-provider'; | ||||||
|  | import { Helmet } from 'react-helmet-async'; | ||||||
|  |  | ||||||
| const ExamplesPageComponent: React.FC = () => { | const ExamplesPageComponent: React.FC = () => { | ||||||
|     const { effectiveTheme } = useTheme(); |     const { effectiveTheme } = useTheme(); | ||||||
|     useEffect(() => { |  | ||||||
|         document.title = 'ChartDB - Example Database Diagrams & Schemas'; |  | ||||||
|     }, []); |  | ||||||
|  |  | ||||||
|     return ( |     return ( | ||||||
|         <section className="flex w-screen flex-col bg-background"> |         <> | ||||||
|             <nav className="flex h-12 flex-row items-center justify-between border-b px-4"> |             <Helmet> | ||||||
|                 <div className="flex flex-1 justify-start gap-x-3"> |                 <title>ChartDB - Example Database Diagrams & Schemas</title> | ||||||
|                     <div className="flex items-center font-primary"> |             </Helmet> | ||||||
|                         <a |             <section className="flex w-screen flex-col bg-background"> | ||||||
|                             href="https://chartdb.io" |                 <nav className="flex h-12 flex-row items-center justify-between border-b px-4"> | ||||||
|                             className="cursor-pointer" |                     <div className="flex flex-1 justify-start gap-x-3"> | ||||||
|                             rel="noreferrer" |                         <div className="flex items-center font-primary"> | ||||||
|                         > |                             <a | ||||||
|                             <img |                                 href="https://chartdb.io" | ||||||
|                                 src={ |                                 className="cursor-pointer" | ||||||
|                                     effectiveTheme === 'light' |                                 rel="noreferrer" | ||||||
|                                         ? ChartDBLogo |                             > | ||||||
|                                         : ChartDBDarkLogo |                                 <img | ||||||
|                                 } |                                     src={ | ||||||
|                                 alt="chartDB" |                                         effectiveTheme === 'light' | ||||||
|                                 className="h-4 max-w-fit" |                                             ? ChartDBLogo | ||||||
|                             /> |                                             : ChartDBDarkLogo | ||||||
|                         </a> |                                     } | ||||||
|  |                                     alt="chartDB" | ||||||
|  |                                     className="h-4 max-w-fit" | ||||||
|  |                                 /> | ||||||
|  |                             </a> | ||||||
|  |                         </div> | ||||||
|  |                     </div> | ||||||
|  |                     <div className="group flex flex-1 flex-row items-center justify-center"></div> | ||||||
|  |                     <div className="hidden flex-1 justify-end sm:flex"></div> | ||||||
|  |                 </nav> | ||||||
|  |                 <div className="flex flex-col px-3 pt-3 text-center md:px-28 md:text-left"> | ||||||
|  |                     <h1 className="font-primary text-2xl font-bold"> | ||||||
|  |                         Examples | ||||||
|  |                     </h1> | ||||||
|  |                     <h2 className="mt-1 font-primary text-base text-muted-foreground"> | ||||||
|  |                         A collection of examples to help you get started with | ||||||
|  |                         ChartDB. | ||||||
|  |                     </h2> | ||||||
|  |                     <div className="mt-6 grid grid-flow-row grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3"> | ||||||
|  |                         {examples.map((example) => ( | ||||||
|  |                             <ExampleCard key={example.id} example={example} /> | ||||||
|  |                         ))} | ||||||
|                     </div> |                     </div> | ||||||
|                 </div> |                 </div> | ||||||
|                 <div className="group flex flex-1 flex-row items-center justify-center"></div> |             </section> | ||||||
|                 <div className="hidden flex-1 justify-end sm:flex"></div> |         </> | ||||||
|             </nav> |  | ||||||
|             <div className="flex flex-col px-3 pt-3 text-center md:px-28 md:text-left"> |  | ||||||
|                 <h1 className="font-primary text-2xl font-bold">Examples</h1> |  | ||||||
|                 <h2 className="mt-1 font-primary text-base text-muted-foreground"> |  | ||||||
|                     A collection of examples to help you get started with |  | ||||||
|                     ChartDB. |  | ||||||
|                 </h2> |  | ||||||
|                 <div className="mt-6 grid grid-flow-row grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3"> |  | ||||||
|                     {examples.map((example) => ( |  | ||||||
|                         <ExampleCard key={example.id} example={example} /> |  | ||||||
|                     ))} |  | ||||||
|                 </div> |  | ||||||
|             </div> |  | ||||||
|         </section> |  | ||||||
|     ); |     ); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ import { StorageProvider } from '@/context/storage-context/storage-provider'; | |||||||
| import { ThemeProvider } from '@/context/theme-context/theme-provider'; | import { ThemeProvider } from '@/context/theme-context/theme-provider'; | ||||||
| import { Button } from '@/components/button/button'; | import { Button } from '@/components/button/button'; | ||||||
| import { CloudDownload } from 'lucide-react'; | import { CloudDownload } from 'lucide-react'; | ||||||
| import { useNavigate, useParams } from 'react-router-dom'; | import { useLoaderData, useNavigate, useParams } from 'react-router-dom'; | ||||||
| import type { Template } from '../../templates-data/templates-data'; | import type { Template } from '../../templates-data/templates-data'; | ||||||
| import { | import { | ||||||
|     Breadcrumb, |     Breadcrumb, | ||||||
| @@ -34,37 +34,27 @@ import { ChartDBProvider } from '@/context/chartdb-context/chartdb-provider'; | |||||||
| import { convertTemplateToNewDiagram } from '@/templates-data/template-utils'; | import { convertTemplateToNewDiagram } from '@/templates-data/template-utils'; | ||||||
| import { useStorage } from '@/hooks/use-storage'; | import { useStorage } from '@/hooks/use-storage'; | ||||||
| import type { Diagram } from '@/lib/domain/diagram'; | import type { Diagram } from '@/lib/domain/diagram'; | ||||||
|  | import { Helmet } from 'react-helmet-async'; | ||||||
|  |  | ||||||
|  | export interface TemplatePageLoaderData { | ||||||
|  |     template: Template | undefined; | ||||||
|  | } | ||||||
|  |  | ||||||
| const TemplatePageComponent: React.FC = () => { | const TemplatePageComponent: React.FC = () => { | ||||||
|     const { addDiagram } = useStorage(); |     const { addDiagram } = useStorage(); | ||||||
|     const { templateSlug } = useParams<{ templateSlug: string }>(); |     const { templateSlug } = useParams<{ templateSlug: string }>(); | ||||||
|     const [template, setTemplate] = React.useState<Template>(); |  | ||||||
|     const navigate = useNavigate(); |     const navigate = useNavigate(); | ||||||
|  |     const data = useLoaderData() as TemplatePageLoaderData; | ||||||
|  |  | ||||||
|  |     const template = data.template; | ||||||
|  |  | ||||||
|     useEffect(() => { |     useEffect(() => { | ||||||
|         const loadTemplate = async () => { |         if (!template) { | ||||||
|             const { templates } = await import( |             navigate('/templates'); | ||||||
|                 '@/templates-data/templates-data' |  | ||||||
|             ); |  | ||||||
|             const template = templates.find((t) => t.slug === templateSlug); |  | ||||||
|  |  | ||||||
|             if (!template) { |  | ||||||
|                 navigate('/templates'); |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             setTemplate(template); |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         loadTemplate(); |  | ||||||
|     }, [templateSlug, navigate]); |  | ||||||
|     const { effectiveTheme } = useTheme(); |  | ||||||
|     useEffect(() => { |  | ||||||
|         if (template) { |  | ||||||
|             document.title = `ChartDB - ${template.name} - ${template.shortDescription}`; |  | ||||||
|         } else { |  | ||||||
|             document.title = 'ChartDB - Database Schema Template'; |  | ||||||
|         } |         } | ||||||
|     }, [template]); |     }, [template, navigate]); | ||||||
|  |  | ||||||
|  |     const { effectiveTheme } = useTheme(); | ||||||
|  |  | ||||||
|     const cloneTemplate = useCallback(async () => { |     const cloneTemplate = useCallback(async () => { | ||||||
|         if (!template) { |         if (!template) { | ||||||
| @@ -85,156 +75,168 @@ const TemplatePageComponent: React.FC = () => { | |||||||
|     }, [addDiagram, navigate, template]); |     }, [addDiagram, navigate, template]); | ||||||
|  |  | ||||||
|     return ( |     return ( | ||||||
|         <section className="flex h-screen w-screen flex-col bg-background"> |         <> | ||||||
|             <nav className="flex h-12 shrink-0 flex-row items-center justify-between border-b px-4"> |             <Helmet> | ||||||
|                 <div className="flex flex-1 justify-start gap-x-3"> |                 <title> | ||||||
|                     <div className="flex items-center font-primary"> |                     {template | ||||||
|                         <a |                         ? `ChartDB - ${template.name} - ${template.shortDescription}` | ||||||
|                             href="https://chartdb.io" |                         : 'ChartDB - Database Schema Template'} | ||||||
|                             className="cursor-pointer" |                 </title> | ||||||
|                             rel="noreferrer" |             </Helmet> | ||||||
|                         > |  | ||||||
|                             <img |  | ||||||
|                                 src={ |  | ||||||
|                                     effectiveTheme === 'light' |  | ||||||
|                                         ? ChartDBLogo |  | ||||||
|                                         : ChartDBDarkLogo |  | ||||||
|                                 } |  | ||||||
|                                 alt="chartDB" |  | ||||||
|                                 className="h-4 max-w-fit" |  | ||||||
|                             /> |  | ||||||
|                         </a> |  | ||||||
|                     </div> |  | ||||||
|                 </div> |  | ||||||
|                 <div className="group flex flex-1 flex-row items-center justify-center"></div> |  | ||||||
|                 <div className="hidden flex-1 justify-end sm:flex"></div> |  | ||||||
|             </nav> |  | ||||||
|             {!template ? ( |  | ||||||
|                 <Spinner size={'large'} className="mt-20 text-pink-600" /> |  | ||||||
|             ) : ( |  | ||||||
|                 <div className="flex flex-1 flex-col p-3 pb-5 text-center md:px-28 md:text-left"> |  | ||||||
|                     <Breadcrumb className="mb-2"> |  | ||||||
|                         <BreadcrumbList> |  | ||||||
|                             <BreadcrumbItem> |  | ||||||
|                                 <BreadcrumbLink href={`/templates`}> |  | ||||||
|                                     Templates |  | ||||||
|                                 </BreadcrumbLink> |  | ||||||
|                             </BreadcrumbItem> |  | ||||||
|                             <BreadcrumbSeparator /> |  | ||||||
|                             <BreadcrumbItem> |  | ||||||
|                                 <BreadcrumbLink |  | ||||||
|                                     href={`/templates/${templateSlug}`} |  | ||||||
|                                 > |  | ||||||
|                                     {templateSlug} |  | ||||||
|                                 </BreadcrumbLink> |  | ||||||
|                             </BreadcrumbItem> |  | ||||||
|                         </BreadcrumbList> |  | ||||||
|                     </Breadcrumb> |  | ||||||
|                     <div className="flex flex-col items-center gap-4 md:flex-row md:items-start md:justify-between md:gap-0"> |  | ||||||
|                         <div className="flex flex-col pr-0 md:pr-20"> |  | ||||||
|                             <h1 className="font-primary text-2xl font-bold"> |  | ||||||
|                                 {template?.name} |  | ||||||
|                             </h1> |  | ||||||
|                             <h2 className="mt-3"> |  | ||||||
|                                 <span className="font-semibold"> |  | ||||||
|                                     {template?.shortDescription} |  | ||||||
|                                     {': '} |  | ||||||
|                                 </span> |  | ||||||
|                                 {template?.description} |  | ||||||
|                             </h2> |  | ||||||
|                         </div> |  | ||||||
|                         <div className="flex justify-end"> |  | ||||||
|                             <Button onClick={cloneTemplate}> |  | ||||||
|                                 <CloudDownload className="mr-2" size="16" /> |  | ||||||
|                                 Clone Template |  | ||||||
|                             </Button> |  | ||||||
|                         </div> |  | ||||||
|                     </div> |  | ||||||
|                     <Separator className="my-5" /> |  | ||||||
|                     <div className="flex w-full flex-1 flex-col gap-4 md:flex-row"> |  | ||||||
|                         <div className="relative top-0 flex h-fit w-full shrink-0 flex-col gap-4 md:sticky md:top-1 md:w-60"> |  | ||||||
|                             <div> |  | ||||||
|                                 <h4 className="mb-1 text-base font-semibold md:text-left"> |  | ||||||
|                                     Metadata |  | ||||||
|                                 </h4> |  | ||||||
|  |  | ||||||
|                                 <div className="text-sm text-muted-foreground"> |             <section className="flex h-screen w-screen flex-col bg-background"> | ||||||
|                                     <div className="inline-flex"> |                 <nav className="flex h-12 shrink-0 flex-row items-center justify-between border-b px-4"> | ||||||
|                                         <span className="mr-2">Database:</span> |                     <div className="flex flex-1 justify-start gap-x-3"> | ||||||
|                                         <Tooltip> |                         <div className="flex items-center font-primary"> | ||||||
|                                             <TooltipTrigger asChild> |                             <a | ||||||
|                                                 <img |                                 href="https://chartdb.io" | ||||||
|                                                     src={ |                                 className="cursor-pointer" | ||||||
|                                                         databaseSecondaryLogoMap[ |                                 rel="noreferrer" | ||||||
|  |                             > | ||||||
|  |                                 <img | ||||||
|  |                                     src={ | ||||||
|  |                                         effectiveTheme === 'light' | ||||||
|  |                                             ? ChartDBLogo | ||||||
|  |                                             : ChartDBDarkLogo | ||||||
|  |                                     } | ||||||
|  |                                     alt="chartDB" | ||||||
|  |                                     className="h-4 max-w-fit" | ||||||
|  |                                 /> | ||||||
|  |                             </a> | ||||||
|  |                         </div> | ||||||
|  |                     </div> | ||||||
|  |                     <div className="group flex flex-1 flex-row items-center justify-center"></div> | ||||||
|  |                     <div className="hidden flex-1 justify-end sm:flex"></div> | ||||||
|  |                 </nav> | ||||||
|  |                 {!template ? ( | ||||||
|  |                     <Spinner size={'large'} className="mt-20 text-pink-600" /> | ||||||
|  |                 ) : ( | ||||||
|  |                     <div className="flex flex-1 flex-col p-3 pb-5 text-center md:px-28 md:text-left"> | ||||||
|  |                         <Breadcrumb className="mb-2"> | ||||||
|  |                             <BreadcrumbList> | ||||||
|  |                                 <BreadcrumbItem> | ||||||
|  |                                     <BreadcrumbLink href={`/templates`}> | ||||||
|  |                                         Templates | ||||||
|  |                                     </BreadcrumbLink> | ||||||
|  |                                 </BreadcrumbItem> | ||||||
|  |                                 <BreadcrumbSeparator /> | ||||||
|  |                                 <BreadcrumbItem> | ||||||
|  |                                     <BreadcrumbLink | ||||||
|  |                                         href={`/templates/${templateSlug}`} | ||||||
|  |                                     > | ||||||
|  |                                         {templateSlug} | ||||||
|  |                                     </BreadcrumbLink> | ||||||
|  |                                 </BreadcrumbItem> | ||||||
|  |                             </BreadcrumbList> | ||||||
|  |                         </Breadcrumb> | ||||||
|  |                         <div className="flex flex-col items-center gap-4 md:flex-row md:items-start md:justify-between md:gap-0"> | ||||||
|  |                             <div className="flex flex-col pr-0 md:pr-20"> | ||||||
|  |                                 <h1 className="font-primary text-2xl font-bold"> | ||||||
|  |                                     {template?.name} | ||||||
|  |                                 </h1> | ||||||
|  |                                 <h2 className="mt-3"> | ||||||
|  |                                     <span className="font-semibold"> | ||||||
|  |                                         {template?.shortDescription} | ||||||
|  |                                         {': '} | ||||||
|  |                                     </span> | ||||||
|  |                                     {template?.description} | ||||||
|  |                                 </h2> | ||||||
|  |                             </div> | ||||||
|  |                             <div className="flex justify-end"> | ||||||
|  |                                 <Button onClick={cloneTemplate}> | ||||||
|  |                                     <CloudDownload className="mr-2" size="16" /> | ||||||
|  |                                     Clone Template | ||||||
|  |                                 </Button> | ||||||
|  |                             </div> | ||||||
|  |                         </div> | ||||||
|  |                         <Separator className="my-5" /> | ||||||
|  |                         <div className="flex w-full flex-1 flex-col gap-4 md:flex-row"> | ||||||
|  |                             <div className="relative top-0 flex h-fit w-full shrink-0 flex-col gap-4 md:sticky md:top-1 md:w-60"> | ||||||
|  |                                 <div> | ||||||
|  |                                     <h4 className="mb-1 text-base font-semibold md:text-left"> | ||||||
|  |                                         Metadata | ||||||
|  |                                     </h4> | ||||||
|  |  | ||||||
|  |                                     <div className="text-sm text-muted-foreground"> | ||||||
|  |                                         <div className="inline-flex"> | ||||||
|  |                                             <span className="mr-2"> | ||||||
|  |                                                 Database: | ||||||
|  |                                             </span> | ||||||
|  |                                             <Tooltip> | ||||||
|  |                                                 <TooltipTrigger asChild> | ||||||
|  |                                                     <img | ||||||
|  |                                                         src={ | ||||||
|  |                                                             databaseSecondaryLogoMap[ | ||||||
|  |                                                                 template.diagram | ||||||
|  |                                                                     .databaseType | ||||||
|  |                                                             ] | ||||||
|  |                                                         } | ||||||
|  |                                                         className="h-5 max-w-fit" | ||||||
|  |                                                         alt="database" | ||||||
|  |                                                     /> | ||||||
|  |                                                 </TooltipTrigger> | ||||||
|  |                                                 <TooltipContent> | ||||||
|  |                                                     { | ||||||
|  |                                                         databaseTypeToLabelMap[ | ||||||
|                                                             template.diagram |                                                             template.diagram | ||||||
|                                                                 .databaseType |                                                                 .databaseType | ||||||
|                                                         ] |                                                         ] | ||||||
|                                                     } |                                                     } | ||||||
|                                                     className="h-5 max-w-fit" |                                                 </TooltipContent> | ||||||
|                                                     alt="database" |                                             </Tooltip> | ||||||
|                                                 /> |                                         </div> | ||||||
|                                             </TooltipTrigger> |                                     </div> | ||||||
|                                             <TooltipContent> |                                     <div className="text-sm text-muted-foreground"> | ||||||
|                                                 { |                                         <span>Tables:</span> | ||||||
|                                                     databaseTypeToLabelMap[ |                                         <span className="ml-2 font-semibold"> | ||||||
|                                                         template.diagram |                                             {template?.diagram?.tables | ||||||
|                                                             .databaseType |                                                 ?.length ?? 0} | ||||||
|                                                     ] |                                         </span> | ||||||
|                                                 } |                                     </div> | ||||||
|                                             </TooltipContent> |                                     <div className="text-sm text-muted-foreground"> | ||||||
|                                         </Tooltip> |                                         <span>Relationships:</span> | ||||||
|  |                                         <span className="ml-2 font-semibold"> | ||||||
|  |                                             {template?.diagram?.relationships | ||||||
|  |                                                 ?.length ?? 0} | ||||||
|  |                                         </span> | ||||||
|                                     </div> |                                     </div> | ||||||
|                                 </div> |                                 </div> | ||||||
|                                 <div className="text-sm text-muted-foreground"> |                                 <div> | ||||||
|                                     <span>Tables:</span> |                                     <h4 className="mb-1 text-base font-semibold md:text-left"> | ||||||
|                                     <span className="ml-2 font-semibold"> |                                         Tags | ||||||
|                                         {template?.diagram?.tables?.length ?? 0} |                                     </h4> | ||||||
|                                     </span> |                                     <div className="flex flex-wrap justify-center gap-1 md:justify-start"> | ||||||
|                                 </div> |                                         {template.tags.map((tag) => ( | ||||||
|                                 <div className="text-sm text-muted-foreground"> |                                             <Badge | ||||||
|                                     <span>Relationships:</span> |                                                 variant="outline" | ||||||
|                                     <span className="ml-2 font-semibold"> |                                                 key={`${template.id}_${tag}`} | ||||||
|                                         {template?.diagram?.relationships |                                             > | ||||||
|                                             ?.length ?? 0} |                                                 {tag} | ||||||
|                                     </span> |                                             </Badge> | ||||||
|  |                                         ))} | ||||||
|  |                                     </div> | ||||||
|                                 </div> |                                 </div> | ||||||
|                             </div> |                             </div> | ||||||
|                             <div> |                             <div className="flex min-h-96 overflow-hidden rounded border md:flex-1 md:rounded-lg"> | ||||||
|                                 <h4 className="mb-1 text-base font-semibold md:text-left"> |                                 <div className="size-full"> | ||||||
|                                     Tags |                                     <ChartDBProvider | ||||||
|                                 </h4> |                                         diagram={template.diagram} | ||||||
|                                 <div className="flex flex-wrap justify-center gap-1 md:justify-start"> |  | ||||||
|                                     {template.tags.map((tag) => ( |  | ||||||
|                                         <Badge |  | ||||||
|                                             variant="outline" |  | ||||||
|                                             key={`${template.id}_${tag}`} |  | ||||||
|                                         > |  | ||||||
|                                             {tag} |  | ||||||
|                                         </Badge> |  | ||||||
|                                     ))} |  | ||||||
|                                 </div> |  | ||||||
|                             </div> |  | ||||||
|                         </div> |  | ||||||
|                         <div className="flex min-h-96 overflow-hidden rounded border md:flex-1 md:rounded-lg"> |  | ||||||
|                             <div className="size-full"> |  | ||||||
|                                 <ChartDBProvider |  | ||||||
|                                     diagram={template.diagram} |  | ||||||
|                                     readonly |  | ||||||
|                                     skipTitleUpdate |  | ||||||
|                                 > |  | ||||||
|                                     <Canvas |  | ||||||
|                                         readonly |                                         readonly | ||||||
|                                         initialTables={ |                                     > | ||||||
|                                             template.diagram.tables ?? [] |                                         <Canvas | ||||||
|                                         } |                                             readonly | ||||||
|                                     /> |                                             initialTables={ | ||||||
|                                 </ChartDBProvider> |                                                 template.diagram.tables ?? [] | ||||||
|  |                                             } | ||||||
|  |                                         /> | ||||||
|  |                                     </ChartDBProvider> | ||||||
|  |                                 </div> | ||||||
|                             </div> |                             </div> | ||||||
|                         </div> |                         </div> | ||||||
|                     </div> |                     </div> | ||||||
|                 </div> |                 )} | ||||||
|             )} |             </section> | ||||||
|         </section> |         </> | ||||||
|     ); |     ); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ import { useMatches, useParams } from 'react-router-dom'; | |||||||
| import type { Template } from '@/templates-data/templates-data'; | import type { Template } from '@/templates-data/templates-data'; | ||||||
| import { Spinner } from '@/components/spinner/spinner'; | import { Spinner } from '@/components/spinner/spinner'; | ||||||
| import { removeDups } from '@/lib/utils'; | import { removeDups } from '@/lib/utils'; | ||||||
|  | import { Helmet } from 'react-helmet-async'; | ||||||
|  |  | ||||||
| const TemplatesPageComponent: React.FC = () => { | const TemplatesPageComponent: React.FC = () => { | ||||||
|     const { effectiveTheme } = useTheme(); |     const { effectiveTheme } = useTheme(); | ||||||
| @@ -25,10 +26,6 @@ const TemplatesPageComponent: React.FC = () => { | |||||||
|     const isAllTemplates = matches.some((match) => match.id === 'templates'); |     const isAllTemplates = matches.some((match) => match.id === 'templates'); | ||||||
|     const isTags = matches.some((match) => match.id === 'templates_tags'); |     const isTags = matches.some((match) => match.id === 'templates_tags'); | ||||||
|  |  | ||||||
|     useEffect(() => { |  | ||||||
|         document.title = 'ChartDB - Database Schema Templates'; |  | ||||||
|     }, []); |  | ||||||
|  |  | ||||||
|     useEffect(() => { |     useEffect(() => { | ||||||
|         const loadTemplates = async () => { |         const loadTemplates = async () => { | ||||||
|             const { templates: loadedTemplates } = await import( |             const { templates: loadedTemplates } = await import( | ||||||
| @@ -55,88 +52,97 @@ const TemplatesPageComponent: React.FC = () => { | |||||||
|     }, [isFeatured, isTags, tag]); |     }, [isFeatured, isTags, tag]); | ||||||
|  |  | ||||||
|     return ( |     return ( | ||||||
|         <section className="flex w-screen flex-col bg-background"> |         <> | ||||||
|             <nav className="flex h-12 shrink-0 flex-row items-center justify-between border-b px-4"> |             <Helmet> | ||||||
|                 <div className="flex flex-1 justify-start gap-x-3"> |                 <title>ChartDB - Database Schema Templates</title> | ||||||
|                     <div className="flex items-center font-primary"> |             </Helmet> | ||||||
|                         <a |  | ||||||
|                             href="https://chartdb.io" |  | ||||||
|                             className="cursor-pointer" |  | ||||||
|                             rel="noreferrer" |  | ||||||
|                         > |  | ||||||
|                             <img |  | ||||||
|                                 src={ |  | ||||||
|                                     effectiveTheme === 'light' |  | ||||||
|                                         ? ChartDBLogo |  | ||||||
|                                         : ChartDBDarkLogo |  | ||||||
|                                 } |  | ||||||
|                                 alt="chartDB" |  | ||||||
|                                 className="h-4 max-w-fit" |  | ||||||
|                             /> |  | ||||||
|                         </a> |  | ||||||
|                     </div> |  | ||||||
|                 </div> |  | ||||||
|                 <div className="group flex flex-1 flex-row items-center justify-center"></div> |  | ||||||
|                 <div className="hidden flex-1 justify-end sm:flex"></div> |  | ||||||
|             </nav> |  | ||||||
|             <div className="flex flex-col p-3 text-center md:px-28 md:text-left"> |  | ||||||
|                 <h1 className="font-primary text-2xl font-bold"> |  | ||||||
|                     Database Schema Templates |  | ||||||
|                 </h1> |  | ||||||
|                 <h2 className="mt-1 font-primary text-base text-muted-foreground"> |  | ||||||
|                     Explore a collection of real-world database schemas drawn |  | ||||||
|                     from real-world live applications and open-source projects. |  | ||||||
|                     Use these as a foundation or source of inspiration when |  | ||||||
|                     designing your app’s architecture. |  | ||||||
|                 </h2> |  | ||||||
|                 {!templates ? ( |  | ||||||
|                     <Spinner size={'large'} className="mt-20 text-pink-600" /> |  | ||||||
|                 ) : ( |  | ||||||
|                     <div className="mt-6 flex w-full flex-col-reverse gap-4 md:flex-row"> |  | ||||||
|                         <div className="relative top-0 flex h-fit w-full shrink-0 flex-col md:sticky md:top-1 md:w-44"> |  | ||||||
|                             <ListMenu |  | ||||||
|                                 items={[ |  | ||||||
|                                     { |  | ||||||
|                                         title: 'Featured', |  | ||||||
|                                         href: '/templates/featured', |  | ||||||
|                                         icon: Star, |  | ||||||
|                                         selected: isFeatured, |  | ||||||
|                                     }, |  | ||||||
|                                     { |  | ||||||
|                                         title: 'All Templates', |  | ||||||
|                                         href: '/templates', |  | ||||||
|                                         icon: Component, |  | ||||||
|                                         selected: isAllTemplates, |  | ||||||
|                                     }, |  | ||||||
|                                 ]} |  | ||||||
|                             /> |  | ||||||
|  |  | ||||||
|                             <h4 className="mt-4 text-left text-sm font-semibold"> |             <section className="flex w-screen flex-col bg-background"> | ||||||
|                                 Tags |                 <nav className="flex h-12 shrink-0 flex-row items-center justify-between border-b px-4"> | ||||||
|                             </h4> |                     <div className="flex flex-1 justify-start gap-x-3"> | ||||||
|                             {tags ? ( |                         <div className="flex items-center font-primary"> | ||||||
|                                 <ListMenu |                             <a | ||||||
|                                     className="mt-1 w-44 shrink-0" |                                 href="https://chartdb.io" | ||||||
|                                     items={tags.map((currentTag) => ({ |                                 className="cursor-pointer" | ||||||
|                                         title: currentTag, |                                 rel="noreferrer" | ||||||
|                                         href: `/templates/tags/${currentTag}`, |                             > | ||||||
|                                         selected: tag === currentTag, |                                 <img | ||||||
|                                     }))} |                                     src={ | ||||||
|  |                                         effectiveTheme === 'light' | ||||||
|  |                                             ? ChartDBLogo | ||||||
|  |                                             : ChartDBDarkLogo | ||||||
|  |                                     } | ||||||
|  |                                     alt="chartDB" | ||||||
|  |                                     className="h-4 max-w-fit" | ||||||
|                                 /> |                                 /> | ||||||
|                             ) : null} |                             </a> | ||||||
|                         </div> |  | ||||||
|                         <div className="grid flex-1 grid-flow-row grid-cols-1 gap-6 md:grid-cols-1 lg:grid-cols-2 xl:grid-cols-4"> |  | ||||||
|                             {templates.map((template) => ( |  | ||||||
|                                 <TemplateCard |  | ||||||
|                                     key={`${template.id}`} |  | ||||||
|                                     template={template} |  | ||||||
|                                 /> |  | ||||||
|                             ))} |  | ||||||
|                         </div> |                         </div> | ||||||
|                     </div> |                     </div> | ||||||
|                 )} |                     <div className="group flex flex-1 flex-row items-center justify-center"></div> | ||||||
|             </div> |                     <div className="hidden flex-1 justify-end sm:flex"></div> | ||||||
|         </section> |                 </nav> | ||||||
|  |                 <div className="flex flex-col p-3 text-center md:px-28 md:text-left"> | ||||||
|  |                     <h1 className="font-primary text-2xl font-bold"> | ||||||
|  |                         Database Schema Templates | ||||||
|  |                     </h1> | ||||||
|  |                     <h2 className="mt-1 font-primary text-base text-muted-foreground"> | ||||||
|  |                         Explore a collection of real-world database schemas | ||||||
|  |                         drawn from real-world live applications and open-source | ||||||
|  |                         projects. Use these as a foundation or source of | ||||||
|  |                         inspiration when designing your app’s architecture. | ||||||
|  |                     </h2> | ||||||
|  |                     {!templates ? ( | ||||||
|  |                         <Spinner | ||||||
|  |                             size={'large'} | ||||||
|  |                             className="mt-20 text-pink-600" | ||||||
|  |                         /> | ||||||
|  |                     ) : ( | ||||||
|  |                         <div className="mt-6 flex w-full flex-col-reverse gap-4 md:flex-row"> | ||||||
|  |                             <div className="relative top-0 flex h-fit w-full shrink-0 flex-col md:sticky md:top-1 md:w-44"> | ||||||
|  |                                 <ListMenu | ||||||
|  |                                     items={[ | ||||||
|  |                                         { | ||||||
|  |                                             title: 'Featured', | ||||||
|  |                                             href: '/templates/featured', | ||||||
|  |                                             icon: Star, | ||||||
|  |                                             selected: isFeatured, | ||||||
|  |                                         }, | ||||||
|  |                                         { | ||||||
|  |                                             title: 'All Templates', | ||||||
|  |                                             href: '/templates', | ||||||
|  |                                             icon: Component, | ||||||
|  |                                             selected: isAllTemplates, | ||||||
|  |                                         }, | ||||||
|  |                                     ]} | ||||||
|  |                                 /> | ||||||
|  |  | ||||||
|  |                                 <h4 className="mt-4 text-left text-sm font-semibold"> | ||||||
|  |                                     Tags | ||||||
|  |                                 </h4> | ||||||
|  |                                 {tags ? ( | ||||||
|  |                                     <ListMenu | ||||||
|  |                                         className="mt-1 w-44 shrink-0" | ||||||
|  |                                         items={tags.map((currentTag) => ({ | ||||||
|  |                                             title: currentTag, | ||||||
|  |                                             href: `/templates/tags/${currentTag}`, | ||||||
|  |                                             selected: tag === currentTag, | ||||||
|  |                                         }))} | ||||||
|  |                                     /> | ||||||
|  |                                 ) : null} | ||||||
|  |                             </div> | ||||||
|  |                             <div className="grid flex-1 grid-flow-row grid-cols-1 gap-6 md:grid-cols-1 lg:grid-cols-2 xl:grid-cols-4"> | ||||||
|  |                                 {templates.map((template) => ( | ||||||
|  |                                     <TemplateCard | ||||||
|  |                                         key={`${template.id}`} | ||||||
|  |                                         template={template} | ||||||
|  |                                     /> | ||||||
|  |                                 ))} | ||||||
|  |                             </div> | ||||||
|  |                         </div> | ||||||
|  |                     )} | ||||||
|  |                 </div> | ||||||
|  |             </section> | ||||||
|  |         </> | ||||||
|     ); |     ); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import React from 'react'; | import React from 'react'; | ||||||
| import type { RouteObject } from 'react-router-dom'; | import type { RouteObject } from 'react-router-dom'; | ||||||
| import { createBrowserRouter } from 'react-router-dom'; | import { createBrowserRouter } from 'react-router-dom'; | ||||||
|  | import type { TemplatePageLoaderData } from './pages/template-page/template-page'; | ||||||
|  |  | ||||||
| const routes: RouteObject[] = [ | const routes: RouteObject[] = [ | ||||||
|     ...['', 'diagrams/:diagramId'].map((path) => ({ |     ...['', 'diagrams/:diagramId'].map((path) => ({ | ||||||
| @@ -63,6 +64,7 @@ const routes: RouteObject[] = [ | |||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|  |         id: 'templates_templateSlug', | ||||||
|         path: 'templates/:templateSlug', |         path: 'templates/:templateSlug', | ||||||
|         async lazy() { |         async lazy() { | ||||||
|             const { TemplatePage } = await import( |             const { TemplatePage } = await import( | ||||||
| @@ -72,6 +74,16 @@ const routes: RouteObject[] = [ | |||||||
|                 element: <TemplatePage />, |                 element: <TemplatePage />, | ||||||
|             }; |             }; | ||||||
|         }, |         }, | ||||||
|  |         loader: async ({ params }): Promise<TemplatePageLoaderData> => { | ||||||
|  |             const { templates } = await import( | ||||||
|  |                 './templates-data/templates-data' | ||||||
|  |             ); | ||||||
|  |             return { | ||||||
|  |                 template: templates.find( | ||||||
|  |                     (template) => template.slug === params.templateSlug | ||||||
|  |                 ), | ||||||
|  |             }; | ||||||
|  |         }, | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|         path: '*', |         path: '*', | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user