From 0b67bf5e683db161010b1f5b0a76b8a21e60c795 Mon Sep 17 00:00:00 2001 From: Alican Erdurmaz Date: Mon, 7 Oct 2024 09:57:11 +0300 Subject: [PATCH] feat(docs): add "Enterprise Edition" to templates (#6382) --- documentation/plugins/templates.js | 19 +- documentation/plugins/templates.ts | 19 +- documentation/src/pages/templates/index.tsx | 70 ++++++- .../enterprise-get-in-touch-button.tsx | 6 +- .../enterprise-template-contact-us-modal.tsx | 144 +++++++++++++ .../src/refine-theme/icons/close-circle.tsx | 30 +++ .../src/refine-theme/icons/locked.tsx | 19 ++ .../src/refine-theme/template-detail.tsx | 139 +++++++++---- .../src/refine-theme/templates-filters.tsx | 189 +++++++++++------- .../src/refine-theme/templates-list.tsx | 12 ++ documentation/src/types/integrations/index.ts | 6 + .../static/assets/badge-enterprise.png | Bin 0 -> 24337 bytes documentation/tailwind.config.js | 9 + 13 files changed, 540 insertions(+), 122 deletions(-) create mode 100644 documentation/src/refine-theme/enterprise-template-contact-us-modal.tsx create mode 100644 documentation/src/refine-theme/icons/close-circle.tsx create mode 100644 documentation/src/refine-theme/icons/locked.tsx create mode 100644 documentation/static/assets/badge-enterprise.png diff --git a/documentation/plugins/templates.js b/documentation/plugins/templates.js index 3636969076f3..4dd0cbccd943 100644 --- a/documentation/plugins/templates.js +++ b/documentation/plugins/templates.js @@ -40,9 +40,10 @@ const templates = [ images: [ "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-refine-crm.png", ], - runOnYourLocalPath: "app-crm", + runOnYourLocalPath: null, + edition: "Enterprise", liveDemo: "https://example.crm.refine.dev/", - github: "https://github.com/refinedev/refine/tree/master/examples/app-crm", + github: null, reactPlatform: "Vite", uiFramework: "Ant Design", dataProvider: "Nestjs-query", @@ -97,6 +98,7 @@ This CRM app template can be used in for various app requirements like B2B appli "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-storefront.png", ], runOnYourLocalPath: "finefoods-client", + edition: "Community", liveDemo: "https://example.refine.dev/", github: "https://github.com/refinedev/refine/tree/master/examples/finefoods-client", @@ -105,6 +107,7 @@ This CRM app template can be used in for various app requirements like B2B appli dataProvider: "Rest API", authProvider: "Custom", description: ` + This is a template that can serve as an example for building React-based storefronts, admin panels, or internal tools using Refine. Implemented popular tools like Tailwind CSS and Next.js, which are highly demanded by the community. ### Key Features: @@ -122,6 +125,7 @@ The source code is also open-source; feel free to use or inspect it to discover "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-finefoods-material-ui.png", ], runOnYourLocalPath: "finefoods-material-ui", + edition: "Community", liveDemo: "https://example.mui.admin.refine.dev", github: "https://github.com/refinedev/refine/tree/master/examples/finefoods-material-ui", @@ -172,6 +176,7 @@ This admin panel template can be used in for various app requirements like B2B a "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-finefoods-ant-design.png", ], runOnYourLocalPath: "finefoods-antd", + edition: "Community", liveDemo: "https://example.admin.refine.dev", github: "https://github.com/refinedev/refine/tree/master/examples/finefoods-antd", @@ -224,6 +229,7 @@ This admin panel template can be used in for various app requirements like B2B a "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-store.png", ], runOnYourLocalPath: "store", + edition: "Community", liveDemo: "https://store.refine.dev/", github: "https://github.com/refinedev/refine/tree/master/examples/store", reactPlatform: "Next.js", @@ -257,6 +263,7 @@ With its production-ready status, this template offers a solid foundation for bu "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-pixels.png", ], runOnYourLocalPath: "pixels", + edition: "Community", liveDemo: "https://pixels.refine.dev/", github: "https://github.com/refinedev/refine/tree/master/examples/pixels", tutorial: "https://refine.dev/week-of-refine-supabase/", @@ -287,6 +294,7 @@ We built this template to demonstrate how the Refine framework simplifies and sp "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-invoice-generator.png", ], runOnYourLocalPath: "invoicer", + edition: "Community", liveDemo: "https://refine-invoicer-8mk7d.ondigitalocean.app/", github: "https://github.com/refinedev/refine/tree/master/examples/invoicer", reactPlatform: "Vite", @@ -316,6 +324,7 @@ We built this template to showcase the efficiency and ease of using the Refine f "https://refine.ams3.cdn.digitaloceanspaces.com/templates/video-club-win95.png", ], runOnYourLocalPath: "win95", + edition: "Community", liveDemo: "https://videoclub.refine.dev", github: "https://github.com/refinedev/refine/tree/master/examples/win95", reactPlatform: "Vite", @@ -335,6 +344,7 @@ The source code of the CRUD app is also open-source; feel free to use or inspect "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-realworld.png", ], runOnYourLocalPath: "real-world-example", + edition: "Community", liveDemo: "https://refine-real-world.netlify.app/", github: "https://github.com/refinedev/real-world-example", reactPlatform: "Vite", @@ -368,6 +378,7 @@ Since the source code of this RealWorld app is open-source, you have the freedom "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-multitenancy-strapi.png", ], runOnYourLocalPath: "multi-tenancy-strapi", + edition: "Community", liveDemo: "https://multi-tenancy-strapi.refine.dev", github: "https://github.com/refinedev/refine/tree/master/examples/multi-tenancy-strapi", @@ -391,6 +402,7 @@ The source code of this multitenancy app is open-source, allowing you to use or "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-ant-design-template.png", ], runOnYourLocalPath: "auth-antd", + edition: "Community", liveDemo: "https://codesandbox.io/p/sandbox/github/refinedev/refine/tree/master/examples/auth-antd", github: @@ -411,6 +423,7 @@ Complete internal tool template built with Material UI. Features authentication "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-material-ui-template.png", ], runOnYourLocalPath: "auth-material-ui", + edition: "Community", liveDemo: "https://codesandbox.io/embed/github/refinedev/refine/tree/master/examples/auth-material-ui", github: @@ -431,6 +444,7 @@ Complete internal tool template built with Material UI. Features authentication "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-mantine-template.png", ], runOnYourLocalPath: "auth-mantine", + edition: "Community", liveDemo: "https://codesandbox.io/embed/github/refinedev/refine/tree/master/examples/auth-mantine", github: @@ -451,6 +465,7 @@ Complete internal tool template built with Mantine. Features authentication and "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-chakra-ui-template.png", ], runOnYourLocalPath: "auth-chakra-ui", + edition: "Community", liveDemo: "https://codesandbox.io/embed/github/refinedev/refine/tree/master/examples/auth-chakra-ui", github: diff --git a/documentation/plugins/templates.ts b/documentation/plugins/templates.ts index 72492af7d21e..1c6905059a09 100644 --- a/documentation/plugins/templates.ts +++ b/documentation/plugins/templates.ts @@ -38,9 +38,10 @@ const templates = [ images: [ "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-refine-crm.png", ], - runOnYourLocalPath: "app-crm", + runOnYourLocalPath: null, + edition: "Enterprise", liveDemo: "https://example.crm.refine.dev/", - github: "https://github.com/refinedev/refine/tree/master/examples/app-crm", + github: null, reactPlatform: "Vite", uiFramework: "Ant Design", dataProvider: "Nestjs-query", @@ -95,6 +96,7 @@ This CRM app template can be used in for various app requirements like B2B appli "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-storefront.png", ], runOnYourLocalPath: "finefoods-client", + edition: "Community", liveDemo: "https://example.refine.dev/", github: "https://github.com/refinedev/refine/tree/master/examples/finefoods-client", @@ -103,6 +105,7 @@ This CRM app template can be used in for various app requirements like B2B appli dataProvider: "Rest API", authProvider: "Custom", description: ` + This is a template that can serve as an example for building React-based storefronts, admin panels, or internal tools using Refine. Implemented popular tools like Tailwind CSS and Next.js, which are highly demanded by the community. ### Key Features: @@ -120,6 +123,7 @@ The source code is also open-source; feel free to use or inspect it to discover "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-finefoods-material-ui.png", ], runOnYourLocalPath: "finefoods-material-ui", + edition: "Community", liveDemo: "https://example.mui.admin.refine.dev", github: "https://github.com/refinedev/refine/tree/master/examples/finefoods-material-ui", @@ -170,6 +174,7 @@ This admin panel template can be used in for various app requirements like B2B a "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-finefoods-ant-design.png", ], runOnYourLocalPath: "finefoods-antd", + edition: "Community", liveDemo: "https://example.admin.refine.dev", github: "https://github.com/refinedev/refine/tree/master/examples/finefoods-antd", @@ -222,6 +227,7 @@ This admin panel template can be used in for various app requirements like B2B a "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-store.png", ], runOnYourLocalPath: "store", + edition: "Community", liveDemo: "https://store.refine.dev/", github: "https://github.com/refinedev/refine/tree/master/examples/store", reactPlatform: "Next.js", @@ -255,6 +261,7 @@ With its production-ready status, this template offers a solid foundation for bu "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-pixels.png", ], runOnYourLocalPath: "pixels", + edition: "Community", liveDemo: "https://pixels.refine.dev/", github: "https://github.com/refinedev/refine/tree/master/examples/pixels", tutorial: "https://refine.dev/week-of-refine-supabase/", @@ -285,6 +292,7 @@ We built this template to demonstrate how the Refine framework simplifies and sp "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-invoice-generator.png", ], runOnYourLocalPath: "invoicer", + edition: "Community", liveDemo: "https://refine-invoicer-8mk7d.ondigitalocean.app/", github: "https://github.com/refinedev/refine/tree/master/examples/invoicer", reactPlatform: "Vite", @@ -314,6 +322,7 @@ We built this template to showcase the efficiency and ease of using the Refine f "https://refine.ams3.cdn.digitaloceanspaces.com/templates/video-club-win95.png", ], runOnYourLocalPath: "win95", + edition: "Community", liveDemo: "https://videoclub.refine.dev", github: "https://github.com/refinedev/refine/tree/master/examples/win95", reactPlatform: "Vite", @@ -333,6 +342,7 @@ The source code of the CRUD app is also open-source; feel free to use or inspect "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-realworld.png", ], runOnYourLocalPath: "real-world-example", + edition: "Community", liveDemo: "https://refine-real-world.netlify.app/", github: "https://github.com/refinedev/real-world-example", reactPlatform: "Vite", @@ -366,6 +376,7 @@ Since the source code of this RealWorld app is open-source, you have the freedom "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-multitenancy-strapi.png", ], runOnYourLocalPath: "multi-tenancy-strapi", + edition: "Community", liveDemo: "https://multi-tenancy-strapi.refine.dev", github: "https://github.com/refinedev/refine/tree/master/examples/multi-tenancy-strapi", @@ -389,6 +400,7 @@ The source code of this multitenancy app is open-source, allowing you to use or "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-ant-design-template.png", ], runOnYourLocalPath: "auth-antd", + edition: "Community", liveDemo: "https://codesandbox.io/p/sandbox/github/refinedev/refine/tree/master/examples/auth-antd", github: @@ -409,6 +421,7 @@ Complete internal tool template built with Material UI. Features authentication "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-material-ui-template.png", ], runOnYourLocalPath: "auth-material-ui", + edition: "Community", liveDemo: "https://codesandbox.io/embed/github/refinedev/refine/tree/master/examples/auth-material-ui", github: @@ -429,6 +442,7 @@ Complete internal tool template built with Material UI. Features authentication "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-mantine-template.png", ], runOnYourLocalPath: "auth-mantine", + edition: "Community", liveDemo: "https://codesandbox.io/embed/github/refinedev/refine/tree/master/examples/auth-mantine", github: @@ -449,6 +463,7 @@ Complete internal tool template built with Mantine. Features authentication and "https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-chakra-ui-template.png", ], runOnYourLocalPath: "auth-chakra-ui", + edition: "Community", liveDemo: "https://codesandbox.io/embed/github/refinedev/refine/tree/master/examples/auth-chakra-ui", github: diff --git a/documentation/src/pages/templates/index.tsx b/documentation/src/pages/templates/index.tsx index cf3398164b6d..a9b9d96337c7 100644 --- a/documentation/src/pages/templates/index.tsx +++ b/documentation/src/pages/templates/index.tsx @@ -19,29 +19,36 @@ import { Supabase, } from "@site/src/assets/integration-icons"; import { TemplatesList } from "@site/src/refine-theme/templates-list"; -import { TemplatesFilters } from "@site/src/refine-theme/templates-filters"; +import { + type Filters, + TemplatesFilters, +} from "@site/src/refine-theme/templates-filters"; import { TemplatesFilterButton } from "@site/src/refine-theme/templates-filter-button"; import { CommonDrawer } from "@site/src/refine-theme/common-drawer"; +import { TemplateEdition } from "@site/src/types/integrations"; const Templates: React.FC = () => { const title = "Refine | Open-source Retool for Enterprise"; const [isFilterDrawerOpen, setIsFilterDrawerOpen] = React.useState(false); - const [filters, setFilters] = React.useState<{ - uiFramework: string[]; - backend: string[]; - }>({ + const [filters, setFilters] = React.useState({ + edition: TemplateEdition.All, uiFramework: [], backend: [], }); const dataFiltered = React.useMemo(() => { + const byEdition = + filters.edition === "All" + ? dataTemplates + : dataTemplates.filter((item) => item.edition === filters.edition); + if (!filters.uiFramework.length && !filters.backend.length) { - return dataTemplates; + return byEdition; } - return dataTemplates.filter((item) => { + const byTechStack = byEdition.filter((item) => { return item.integrations.some((integration) => { return ( filters.uiFramework.includes(integration.label) || @@ -49,10 +56,22 @@ const Templates: React.FC = () => { ); }); }); + + return byTechStack; }, [filters]); - const handleFilterChange = (filter: string, field: keyof typeof filters) => { + const handleFilterChange = ( + filter: string | TemplateEdition, + field: keyof typeof filters, + ) => { setFilters((prev) => { + if (field === "edition") { + return { + ...prev, + [field]: filter as TemplateEdition, + }; + } + const hasFilter = prev[field].includes(filter); if (hasFilter) { return { @@ -141,6 +160,9 @@ const Templates: React.FC = () => { { + handleFilterChange(edition, "edition"); + }} onBackendChange={(backend) => { handleFilterChange(backend, "backend"); }} @@ -166,6 +188,9 @@ const Templates: React.FC = () => {
{ + handleFilterChange(edition, "edition"); + }} onBackendChange={(backend) => { handleFilterChange(backend, "backend"); }} @@ -201,6 +226,20 @@ type Integration = { }; const dataFilters = { + editions: [ + { + label: "All", + icon: null, + }, + { + label: "Enterprise", + icon: null, + }, + { + label: TemplateEdition.Community, + icon: null, + }, + ], uiFrameworks: [ { label: "Ant Design", @@ -278,6 +317,7 @@ const dataTemplates: { description: string; image: string; to: string; + edition: TemplateEdition; integrations: (Integration["uiFrameworks"] | Integration["backends"])[]; }[] = [ { @@ -287,6 +327,7 @@ const dataTemplates: { "A comprehensive CRM app built using Refine, Ant Design, and GraphQL. It includes authentication, a dashboard, and over 10 CRUD interfaces, ranging from charts and sales kanban boards to user administration.", image: "https://refine.ams3.cdn.digitaloceanspaces.com/templates/refine-crm.jpg", + edition: TemplateEdition.Enterprise, integrations: [ { label: "Ant Design", @@ -309,6 +350,7 @@ const dataTemplates: { "A Headless storefront example built with Refine and Tailwind CSS. Features product listings and a simple shopping cart. Supports SSR with NextJS.", image: "https://refine.ams3.cdn.digitaloceanspaces.com/templates/finefoods-storefront.jpg", + edition: TemplateEdition.Community, integrations: [ { label: "Headless", @@ -331,6 +373,7 @@ const dataTemplates: { "A comprehensive Admin panel template built using Refine and Material UI demonstrating a food ordering system. It includes features such as authentication, a dashboard, and over 10 CRUD interfaces, ranging from orders to user management.", image: "https://refine.ams3.cdn.digitaloceanspaces.com/templates/finefoods-material-ui.jpg", + edition: TemplateEdition.Community, integrations: [ { label: "Material UI", @@ -353,6 +396,7 @@ const dataTemplates: { "A comprehensive Admin panel template built using Refine and Ant design demonstrating a food ordering system. It includes features such as authentication, a dashboard, and over 10 CRUD interfaces, ranging from orders to user management.", image: "https://refine.ams3.cdn.digitaloceanspaces.com/templates/finefoods-ant-design.jpg", + edition: TemplateEdition.Community, integrations: [ { label: "Ant Design", @@ -375,6 +419,7 @@ const dataTemplates: { "A complete headless e-commerce template was built on top of Medusa with Refine. Features a fully working solution with product listings, a shopping cart, and checkout. Supports SSR with NextJS.", image: "https://refine.ams3.cdn.digitaloceanspaces.com/templates/swag-store.jpg", + edition: TemplateEdition.Community, integrations: [ { label: "Headless", @@ -397,6 +442,7 @@ const dataTemplates: { "It is a funny app built with Refine and Supabase, along with a Realtime feature.", image: "https://refine.ams3.cdn.digitaloceanspaces.com/templates/pixels.jpg", + edition: TemplateEdition.Community, integrations: [ { label: "Ant Design", @@ -419,6 +465,7 @@ const dataTemplates: { "The Internal Tool template features a PDF Invoice Generator along with CRUD functionalities.", image: "https://refine.ams3.cdn.digitaloceanspaces.com/templates/invoicer.jpg", + edition: TemplateEdition.Community, integrations: [ { label: "Ant Design", @@ -441,6 +488,7 @@ const dataTemplates: { "With the headless architecture of Refine, you have the flexibility to implement any custom design!", image: "https://refine.ams3.cdn.digitaloceanspaces.com/templates/video-club.png", + edition: TemplateEdition.Community, integrations: [ { label: "Headless", @@ -462,6 +510,7 @@ const dataTemplates: { description: `"The mother of all demo apps" - Exemplary fullstack Medium.com clone powered by Refine!`, image: "https://refine.ams3.cdn.digitaloceanspaces.com/templates/realworld.jpg", + edition: TemplateEdition.Community, integrations: [ { label: "Headless", @@ -484,6 +533,7 @@ const dataTemplates: { "Implementing multitenancy architecture in Refine applications.", image: "https://refine.ams3.cdn.digitaloceanspaces.com/templates/multitenancy-strapi.jpg", + edition: TemplateEdition.Community, integrations: [ { label: "Ant Design", @@ -506,6 +556,7 @@ const dataTemplates: { "Complete internal tool template built with Ant Design. Features authentication and CRUD screens.", image: "https://refine.ams3.cdn.digitaloceanspaces.com/templates/ant-design-template.jpg", + edition: TemplateEdition.Community, integrations: [ { label: "Ant Design", @@ -528,6 +579,7 @@ const dataTemplates: { "Complete internal tool template built with Material UI. Features authentication and CRUD screens.", image: "https://refine.ams3.cdn.digitaloceanspaces.com/templates/material-ui-template.jpg", + edition: TemplateEdition.Community, integrations: [ { label: "Material UI", @@ -550,6 +602,7 @@ const dataTemplates: { "Complete internal tool template built with Chakra UI. Features authentication and CRUD screens.", image: "https://refine.ams3.cdn.digitaloceanspaces.com/templates/chakra-ui-template.jpg", + edition: TemplateEdition.Community, integrations: [ { label: "Chakra UI", @@ -572,6 +625,7 @@ const dataTemplates: { "Complete internal tool template built with Mantine. Features authentication and CRUD screens.", image: "https://refine.ams3.cdn.digitaloceanspaces.com/templates/mantine-template.jpg", + edition: TemplateEdition.Community, integrations: [ { label: "Mantine", diff --git a/documentation/src/refine-theme/enterprise-get-in-touch-button.tsx b/documentation/src/refine-theme/enterprise-get-in-touch-button.tsx index 82b1e08e054f..20d29aaa35af 100644 --- a/documentation/src/refine-theme/enterprise-get-in-touch-button.tsx +++ b/documentation/src/refine-theme/enterprise-get-in-touch-button.tsx @@ -6,12 +6,16 @@ type Props = { className?: string; linkClassName?: string; variant?: "plain" | "default"; + utmMedium?: string; }; export const EnterpriseGetInTouchButton: FC = ({ variant = "default", ...props }) => { + let href = "https://s.refine.dev/enterprise"; + href += props.utmMedium ? `?utm_medium=${props.utmMedium}` : ""; + return (
= ({ )} > void; +}; + +export const EnterpriseTemplateContactUsModal = ({ + title, + utmMedium, + open, + onClose, +}: Props) => { + return ( + + + +
+ + +
+
+ + +
+ +
+

+ {title} +

+

+ The source code of this project is exclusive to Refine's{" "} + + Enterprise Edition + + . For more information please: +

+ +
+ +
+
+
+
+
+
+
+ ); +}; diff --git a/documentation/src/refine-theme/icons/close-circle.tsx b/documentation/src/refine-theme/icons/close-circle.tsx new file mode 100644 index 000000000000..a695cbfb5366 --- /dev/null +++ b/documentation/src/refine-theme/icons/close-circle.tsx @@ -0,0 +1,30 @@ +import * as React from "react"; +import type { SVGProps } from "react"; + +export const CloseCircleIcon = (props: SVGProps) => ( + + + + +); diff --git a/documentation/src/refine-theme/icons/locked.tsx b/documentation/src/refine-theme/icons/locked.tsx new file mode 100644 index 000000000000..24823a9d8322 --- /dev/null +++ b/documentation/src/refine-theme/icons/locked.tsx @@ -0,0 +1,19 @@ +import React from "react"; + +export const LockedIcon = (props: React.SVGProps) => ( + + + +); diff --git a/documentation/src/refine-theme/template-detail.tsx b/documentation/src/refine-theme/template-detail.tsx index 3543983446a8..23841f6d955e 100644 --- a/documentation/src/refine-theme/template-detail.tsx +++ b/documentation/src/refine-theme/template-detail.tsx @@ -1,18 +1,27 @@ -import React, { type FC, type PropsWithChildren, type SVGProps } from "react"; +import React, { + Fragment, + useState, + type FC, + type PropsWithChildren, + type SVGProps, +} from "react"; import clsx from "clsx"; import Link from "@docusaurus/Link"; +import ReactMarkdown from "react-markdown"; +import remarkGfm from "remark-gfm"; +import remarkRehype from "remark-rehype"; import { CommonLayout } from "@site/src/refine-theme/common-layout"; import { CommonHeader } from "@site/src/refine-theme/common-header"; import { LandingFooter } from "@site/src/refine-theme/landing-footer"; +import { EnterpriseTemplateContactUsModal } from "./enterprise-template-contact-us-modal"; import { CommonCircleChevronLeft } from "./common-circle-chevron-left"; import { ShareIcon } from "./icons/share"; import * as Icons from "@site/src/assets/integration-icons"; -import ReactMarkdown from "react-markdown"; -import remarkGfm from "remark-gfm"; -import remarkRehype from "remark-rehype"; import { CommonRunLocalPrompt } from "./common-run-local-prompt"; import { GithubIcon } from "./icons/github"; import { TutorialIcon } from "./icons/tutorial"; +import { LockedIcon } from "./icons/locked"; +import { TemplateEdition } from "../types/integrations"; type Props = { data: { @@ -20,9 +29,10 @@ type Props = { title: string; description: string; images: string[]; - runOnYourLocalPath: string; + runOnYourLocalPath: string | null; + edition: TemplateEdition; liveDemo: string; - github: string; + github: string | null; tutorial: string; reactPlatform: string; uiFramework: string; @@ -81,6 +91,7 @@ export const TemplatesDetail: FC = ({ data }) => {
= ({ data }) => { src={data.images[0]} alt={data.title} /> + {data.edition === TemplateEdition.Enterprise && ( +
+ enterprise badge +
+ )}
= ({ data }) => { )} - {data.github && ( - - - - Source code - - - )} + {data.github ? ( + + ) : data.edition === TemplateEdition.Enterprise ? ( + + ) : null}
= ({ data }) => { "landing-lg:pl-4 landing-lg:pr-6", )} > -
- -
+ {data.runOnYourLocalPath && ( +
+ +
+ )}

= ({ data }) => { ); }; -const ProjectLink: FC> = ({ - to, - children, -}) => { +const ProjectLink: FC< + PropsWithChildren<{ to?: string; onClick?: () => void }> +> = ({ to, onClick, children }) => { + const Component = to ? Link : "button"; + const props = to + ? { + to, + target: "_blank", + rel: "noopener noreferrer", + } + : { + onClick, + }; + return ( - > = ({ "pl-2 py-2 pr-3", "landing-md:pl-3 landing-md:py-3 landing-md:pr-6", "rounded-full", - "dark:bg-refine-cyan-alt/10 bg-refine-blue/10", - "dark:text-refine-cyan-alt text-refine-blue", + to && "dark:bg-refine-cyan-alt/10 bg-refine-blue/10", + to && "dark:text-refine-cyan-alt text-refine-blue", + !to && + "dark:text-refine-enterprise-purple-5 text-refine-enterprise-purple-2", + !to && + "dark:bg-refine-enterprise-purple-5/10 bg-refine-enterprise-purple-2/10", "hover:no-underline", "overflow-hidden", )} @@ -280,10 +312,13 @@ const ProjectLink: FC> = ({ "group-hover/project-link-button:opacity-100", "group-hover/project-link-button:scale-100", "pointer-events-none", - "bg-landing-copy-command-hover-bg-light dark:bg-landing-copy-command-hover-bg-dark", + to && + "bg-landing-copy-command-hover-bg-light dark:bg-landing-copy-command-hover-bg-dark", + !to && + "bg-enterprise-copy-command-hover-bg-light dark:bg-enterprise-copy-command-hover-bg-dark", )} /> - + ); }; @@ -363,6 +398,39 @@ const IntegrationBadge = (props: { ); }; +const SourceCode = (props: { url: string }) => { + return ( + + + + Source code + + + ); +}; + +const SourceCodeLocked = (props: { data: Props["data"] }) => { + const [isModalOpen, setIsModalOpen] = useState(false); + + return ( + <> + setIsModalOpen(true)}> + + + Source code + + + + setIsModalOpen(false)} + title={props.data.title} + utmMedium={props.data.slug} + /> + + ); +}; + const integrationToIconMap = { Ably: (props: SVGProps) => , Airtable: (props: SVGProps) => , @@ -440,6 +508,9 @@ const integrationToIconMap = { "Nestjs-query": (props: SVGProps) => ( ), + "Nestjsx-CRUD": (props: SVGProps) => ( + + ), }; export type Integration = keyof typeof integrationToIconMap; diff --git a/documentation/src/refine-theme/templates-filters.tsx b/documentation/src/refine-theme/templates-filters.tsx index 3556dc654a9a..a80922933801 100644 --- a/documentation/src/refine-theme/templates-filters.tsx +++ b/documentation/src/refine-theme/templates-filters.tsx @@ -1,14 +1,27 @@ import clsx from "clsx"; -import React, { type FC, type SVGProps } from "react"; +import React, { + type PropsWithChildren, + type FC, + type SVGProps, + type ReactNode, +} from "react"; +import type { TemplateEdition } from "../types/integrations"; + +export type Filters = { + edition: TemplateEdition; + uiFramework: string[]; + backend: string[]; +}; type Props = { svgId?: string; className?: string; - selected: { - uiFramework: string[]; - backend: string[]; - }; + selected: Filters; data: { + editions: { + label: string; + icon: null; + }[]; uiFrameworks: { label: string; icon: (props: SVGProps) => JSX.Element; @@ -18,8 +31,9 @@ type Props = { icon: (props: SVGProps) => JSX.Element; }[]; }; - onBackendChange: (backend: string) => void; - onUIFrameworkChange: (framework: string) => void; + onEditionChange: (edition: string) => void; + onBackendChange: (backend: Filters["backend"][number]) => void; + onUIFrameworkChange: (framework: Filters["uiFramework"][number]) => void; }; export const TemplatesFilters: FC = ({ @@ -27,97 +41,122 @@ export const TemplatesFilters: FC = ({ className, data, selected, + onEditionChange, onBackendChange, onUIFrameworkChange, }) => { return (
-

- UI Frameworks -

-
+ + {data.editions.map((item) => { + const isSelected = selected.edition === item.label; + + return ( + onEditionChange(item.label)} + /> + ); + })} + + {data.uiFrameworks.map((item) => { const isSelected = selected.uiFramework.includes(item.label); const Icon = item.icon; return ( -
} + label={item.label} + isSelected={isSelected} onClick={() => onUIFrameworkChange(item.label)} - role="button" - className={clsx( - "flex", - "items-center", - "py-2 pl-2 pr-4", - "gap-2", - "rounded-full", - "cursor-pointer", - "border dark:border-gray-700 border-gray-200", - isSelected && "dark:bg-gray-700 bg-gray-50", - "transition-colors duration-200 ease-in-out", - )} - > - - - {item.label} - -
+ /> ); })} -
-

- Backends -

-
+ + {data.backends.map((item) => { const isSelected = selected.backend.includes(item.label); const Icon = item.icon; return ( -
} + label={item.label} + isSelected={isSelected} onClick={() => onBackendChange(item.label)} - role="button" - className={clsx( - "flex", - "items-center", - "py-2 pl-2 pr-4", - "gap-2", - "rounded-full", - "cursor-pointer", - "border dark:border-gray-700 border-gray-200", - isSelected && "dark:bg-gray-700 bg-gray-50", - )} - > - - - {item.label} - -
+ /> ); })} -
+
); }; + +const List = ( + props: PropsWithChildren<{ + label: string; + className?: string; + }>, +) => { + return ( + <> +

+ {props.label} +

+
+ {props.children} +
+ + ); +}; + +const ListItem = (props: { + icon?: ReactNode; + label: string; + isSelected: boolean; + onClick: () => void; +}) => { + return ( + + ); +}; diff --git a/documentation/src/refine-theme/templates-list.tsx b/documentation/src/refine-theme/templates-list.tsx index 9b2050dc6975..610abd9c62cc 100644 --- a/documentation/src/refine-theme/templates-list.tsx +++ b/documentation/src/refine-theme/templates-list.tsx @@ -1,6 +1,7 @@ import Link from "@docusaurus/Link"; import clsx from "clsx"; import React, { type FC, type SVGProps } from "react"; +import type { TemplateEdition } from "../types/integrations"; type Props = { className?: string; @@ -9,6 +10,7 @@ type Props = { description: string; image: string; to: string; + edition: TemplateEdition; integrations: { label: string; icon: (props: SVGProps) => JSX.Element; @@ -35,6 +37,7 @@ export const TemplatesList: FC = ({ className, data }) => { key={item.title} className={clsx( "h-full", + "relative", "flex", "flex-col", "dark:bg-gray-800 bg-gray-50", @@ -115,6 +118,15 @@ export const TemplatesList: FC = ({ className, data }) => { })}

+ {item.edition === "Enterprise" && ( +
+ enterprise badge +
+ )} ); })} diff --git a/documentation/src/types/integrations/index.ts b/documentation/src/types/integrations/index.ts index f730702cdbeb..317b551d7399 100644 --- a/documentation/src/types/integrations/index.ts +++ b/documentation/src/types/integrations/index.ts @@ -15,3 +15,9 @@ export type contributor = { name: string; url: string; }; + +export enum TemplateEdition { + All = "All", + Enterprise = "Enterprise", + Community = "Community", +} diff --git a/documentation/static/assets/badge-enterprise.png b/documentation/static/assets/badge-enterprise.png new file mode 100644 index 0000000000000000000000000000000000000000..28f93c58f49edda51a3f548f1ebaf4213ae87b3d GIT binary patch literal 24337 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