fork refine

This commit is contained in:
Stefan Pejcic
2024-02-05 10:23:04 +01:00
parent 3fffde9a8f
commit 8496a83edb
3634 changed files with 715528 additions and 2 deletions

View File

@@ -0,0 +1,367 @@
const blogPluginExports = require("@docusaurus/plugin-content-blog");
const utils = require("@docusaurus/utils");
const path = require("path");
const defaultBlogPlugin = blogPluginExports.default;
const pluginDataDirRoot = path.join(
".docusaurus",
"docusaurus-plugin-content-blog",
);
const aliasedSource = (source) =>
`~blog/${utils.posixPath(path.relative(pluginDataDirRoot, source))}`;
function paginateBlogPosts({
blogPosts,
basePageUrl,
blogTitle,
blogDescription,
postsPerPageOption,
}) {
const totalCount = blogPosts.length;
const postsPerPage =
postsPerPageOption === "ALL" ? totalCount : postsPerPageOption;
const numberOfPages = Math.ceil(totalCount / postsPerPage);
const pages = [];
function permalink(page) {
return page > 0
? utils.normalizeUrl([basePageUrl, `page/${page + 1}`])
: basePageUrl;
}
for (let page = 0; page < numberOfPages; page += 1) {
pages.push({
items: blogPosts
.slice(page * postsPerPage, (page + 1) * postsPerPage)
.map((item) => item.id),
metadata: {
permalink: permalink(page),
page: page + 1,
postsPerPage,
totalPages: numberOfPages,
totalCount,
previousPage: page !== 0 ? permalink(page - 1) : undefined,
nextPage:
page < numberOfPages - 1 ? permalink(page + 1) : undefined,
blogDescription,
blogTitle,
},
});
}
return pages;
}
function getMultipleRandomElement(arr, num) {
const shuffled = [...arr].sort(() => 0.5 - Math.random());
return shuffled.slice(0, num);
}
function getReletadPosts(allBlogPosts, metadata) {
const relatedPosts = allBlogPosts.filter(
(post) =>
post.metadata.frontMatter.tags?.some((tag) =>
metadata.frontMatter.tags?.includes(tag),
) && post.metadata.title !== metadata.title,
);
const randomThreeRelatedPosts = getMultipleRandomElement(relatedPosts, 3);
const filteredPostInfos = randomThreeRelatedPosts.map((post) => {
return {
title: post.metadata.title,
description: post.metadata.description,
permalink: post.metadata.permalink,
formattedDate: post.metadata.formattedDate,
authors: post.metadata.authors,
readingTime: post.metadata.readingTime,
date: post.metadata.date,
};
});
return filteredPostInfos;
}
function getAuthorPosts(allBlogPosts, metadata) {
const authorPosts = allBlogPosts.filter(
(post) =>
post.metadata.frontMatter.authors ===
metadata.frontMatter.authors &&
post.metadata.title !== metadata.title,
);
const randomThreeAuthorPosts = getMultipleRandomElement(authorPosts, 3);
const filteredPostInfos = randomThreeAuthorPosts.map((post) => {
return {
title: post.metadata.title,
description: post.metadata.description,
permalink: post.metadata.permalink,
formattedDate: post.metadata.formattedDate,
authors: post.metadata.authors,
readingTime: post.metadata.readingTime,
date: post.metadata.date,
};
});
return filteredPostInfos;
}
async function blogPluginExtended(...pluginArgs) {
const blogPluginInstance = await defaultBlogPlugin(...pluginArgs);
const { blogTitle, blogDescription, postsPerPage } = pluginArgs[1];
return {
// Add all properties of the default blog plugin so existing functionality is preserved
...blogPluginInstance,
/**
* Override the default `contentLoaded` hook to access blog posts data
*/
contentLoaded: async function (data) {
const { content: blogContents, actions } = data;
const { addRoute, createData } = actions;
const {
blogPosts: allBlogPosts,
blogTags,
blogTagsListPath,
} = blogContents;
const blogItemsToMetadata = {};
function blogPostItemsModule(items) {
return items.map((postId) => {
const blogPostMetadata = blogItemsToMetadata[postId];
return {
content: {
__import: true,
path: blogPostMetadata.source,
query: {
truncated: true,
},
},
};
});
}
const featuredBlogPosts = allBlogPosts.filter(
(post) => post.metadata.frontMatter.is_featured === true,
);
const blogPosts = allBlogPosts.filter(
(post) => post.metadata.frontMatter.is_featured !== true,
);
const blogListPaginated = paginateBlogPosts({
blogPosts,
basePageUrl: "/blog",
blogTitle,
blogDescription,
postsPerPageOption: postsPerPage,
});
// Create routes for blog entries.
await Promise.all(
allBlogPosts.map(async (blogPost) => {
const { id, metadata } = blogPost;
const relatedPosts = getReletadPosts(
allBlogPosts,
metadata,
);
const authorPosts = getAuthorPosts(allBlogPosts, metadata);
await createData(
// Note that this created data path must be in sync with
// metadataPath provided to mdx-loader.
`${utils.docuHash(metadata.source)}.json`,
JSON.stringify(
{ ...metadata, relatedPosts, authorPosts },
null,
2,
),
);
addRoute({
path: metadata.permalink,
component: "@theme/BlogPostPage",
exact: true,
modules: {
content: metadata.source,
},
});
blogItemsToMetadata[id] = metadata;
}),
);
// Create routes for blog's paginated list entries.
await Promise.all(
blogListPaginated.map(async (listPage) => {
const { metadata, items } = listPage;
const { permalink } = metadata;
const pageMetadataPath = await createData(
`${utils.docuHash(permalink)}.json`,
JSON.stringify(metadata, null, 2),
);
const tagsProp = Object.values(blogTags).map((tag) => ({
label: tag.label,
permalink: tag.permalink,
count: tag.items.length,
}));
const tagsPropPath = await createData(
`${utils.docuHash(`${blogTagsListPath}-tags`)}.json`,
JSON.stringify(tagsProp, null, 2),
);
addRoute({
path: permalink,
component: "@theme/BlogListPage",
exact: true,
modules: {
items: blogPostItemsModule(
permalink === "/blog"
? [
...items,
...featuredBlogPosts.map(
(post) => post.id,
),
]
: items,
),
metadata: aliasedSource(pageMetadataPath),
tags: aliasedSource(tagsPropPath),
},
});
}),
);
const authorsArray = allBlogPosts
.map((post) => post.metadata.frontMatter.authors)
.filter((authorName) => authorName !== undefined);
const uniqueAuthors = [...new Set(authorsArray)];
uniqueAuthors.map(async (author) => {
const authorPosts = allBlogPosts.filter(
(post) => post.metadata.frontMatter.authors === author,
);
const authorListPaginated = paginateBlogPosts({
blogPosts: authorPosts,
basePageUrl: "/blog/author/" + author,
blogTitle,
blogDescription,
postsPerPageOption: "ALL",
});
authorListPaginated.map((authorListPage) => {
const { metadata, items } = authorListPage;
const { permalink } = metadata;
addRoute({
path: permalink,
component: "@site/src/components/blog/author-page",
exact: true,
modules: {
items: blogPostItemsModule(items),
},
});
});
});
// Tags. This is the last part so we early-return if there are no tags.
if (Object.keys(blogTags).length === 0) {
return;
}
async function createTagsListPage() {
const tagsProp = Object.values(blogTags).map((tag) => ({
label: tag.label,
permalink: tag.permalink,
count: tag.items.length,
}));
const tagsPropPath = await createData(
`${utils.docuHash(`${blogTagsListPath}-tags`)}.json`,
JSON.stringify(tagsProp, null, 2),
);
addRoute({
path: blogTagsListPath,
component: "@theme/BlogTagsListPage",
exact: true,
modules: {
tags: aliasedSource(tagsPropPath),
},
});
}
async function createTagPostsListPage(tag) {
await Promise.all(
tag.pages.map(async (blogPaginated) => {
const { metadata, items } = blogPaginated;
const tagProp = {
label: tag.label,
permalink: tag.permalink,
allTagsPath: blogTagsListPath,
count: tag.items.length,
};
const tagPropPath = await createData(
`${utils.docuHash(metadata.permalink)}.json`,
JSON.stringify(tagProp, null, 2),
);
const listMetadataPath = await createData(
`${utils.docuHash(metadata.permalink)}-list.json`,
JSON.stringify(metadata, null, 2),
);
const tagsProp = Object.values(blogTags).map((tag) => ({
label: tag.label,
permalink: tag.permalink,
count: tag.items.length,
}));
const tagsPropPath = await createData(
`${utils.docuHash(
`${blogTagsListPath}-tags`,
)}.json`,
JSON.stringify(tagsProp, null, 2),
);
addRoute({
path: metadata.permalink,
component: "@theme/BlogTagsPostsPage",
exact: true,
modules: {
items: blogPostItemsModule(items),
tag: aliasedSource(tagPropPath),
tags: aliasedSource(tagsPropPath),
listMetadata: aliasedSource(listMetadataPath),
},
});
}),
);
}
await createTagsListPage();
await Promise.all(
Object.values(blogTags).map(createTagPostsListPage),
);
},
};
}
module.exports = {
...blogPluginExports,
default: blogPluginExtended,
};

View File

@@ -0,0 +1,157 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
function _nullishCoalesce(lhs, rhsFn) {
if (lhs != null) {
return lhs;
} else {
return rhsFn();
}
}
function _optionalChain(ops) {
let lastAccessLHS = undefined;
let value = ops[0];
let i = 1;
while (i < ops.length) {
const op = ops[i];
const fn = ops[i + 1];
i += 2;
if (
(op === "optionalAccess" || op === "optionalCall") &&
value == null
) {
return undefined;
}
if (op === "access" || op === "optionalAccess") {
lastAccessLHS = value;
value = fn(value);
} else if (op === "call" || op === "optionalCall") {
value = fn((...args) => value.call(lastAccessLHS, ...args));
lastAccessLHS = undefined;
}
}
return value;
}
var _fsextra = require("fs-extra");
var _fsextra2 = _interopRequireDefault(_fsextra);
var _path = require("path");
var _path2 = _interopRequireDefault(_path);
const getDocContent = async (docPath) => {
const accessPath = docPath.startsWith("@site/")
? docPath.replace("@site/", "./")
: docPath;
return new Promise((resolve) => {
_fsextra2.default.readFile(
_path2.default.resolve(accessPath),
(err, data) => {
if (err) {
resolve(undefined);
}
return resolve(data.toString());
},
);
});
};
const getChecklistItems = (docContent) => {
const regex = /<ChecklistItem[\s\n\r\t]+id="((?:\w|\d|-|_)+)"[\s\n\r\t]*>/g;
const matches = docContent.matchAll(regex);
const itemIds = Array.from(matches).map((match) => match[1]);
return itemIds;
};
const getUnitById = (id) => {
// tutorial/<unit-name>/<ui-scope(optional)>/<tutorial-slug>
const unitId = id.split("/")[1];
return unitId;
};
function plugin() {
return {
name: "docusaurus-plugin-refine-checklist",
configureWebpack(config) {
return {
resolve: {
alias: {
"@checklists": _path2.default.join(
_optionalChain([
config,
"access",
(_) => _.resolve,
"optionalAccess",
(_2) => _2.alias,
"optionalAccess",
(_3) => _3["@generated"],
]),
"docusaurus-plugin-refine-checklist",
"default",
),
},
},
};
},
async contentLoaded({ allContent, actions }) {
if (!process.env.DISABLE_CHECKLISTS) {
console.log("Composing Refine tutorial checklists...");
const { createData } = actions;
const currentVersion =
allContent["docusaurus-plugin-content-docs"].default
.loadedVersions[0];
const allDocs = currentVersion.docs;
const allTutorials = allDocs.filter(
(doc) =>
doc.id.startsWith("tutorial/") &&
doc.id !== "tutorial/tutorial",
);
const tutorialsWithChecklist = await Promise.all(
allTutorials.map(async (tutorial) => {
const docContent = await getDocContent(tutorial.source);
const checklistItemIds = getChecklistItems(
_nullishCoalesce(docContent, () => ""),
);
return {
id: tutorial.id,
unit: getUnitById(tutorial.id),
title: tutorial.title,
checklist: checklistItemIds.map((id, index) => ({
id,
index,
})),
};
}),
);
const data = {
items: tutorialsWithChecklist,
};
await createData(
`tutorial-checklist-data.json`,
JSON.stringify(data),
);
} else {
const { createData } = actions;
await createData(
`tutorial-checklist-data.json`,
JSON.stringify({ items: [] }),
);
}
},
};
}
exports.default = plugin;

View File

@@ -0,0 +1,149 @@
import { Plugin } from "@docusaurus/types";
import fs from "fs-extra";
import path from "path";
type DocusaurusDoc = {
unversionedId: string;
id: string;
title: string;
description: string;
source: string;
sourceDirName: string;
slug: string;
permalink: string;
draft: boolean;
editUrl: string;
tags: string[];
version: string;
lastUpdatedBy: string;
lastUpdatedAt: number;
formattedLastUpdatedAt: string;
frontMatter: {
id: string;
title: string;
description?: string;
tags?: string[];
};
sidebar: string;
previous?: {
title: string;
permalink: string;
};
next?: {
title: string;
permalink: string;
};
};
type ContentPluginType = {
default: {
loadedVersions: Array<{ docs: DocusaurusDoc[] }>;
};
};
const getDocContent = async (docPath: string) => {
const accessPath = docPath.startsWith("@site/")
? docPath.replace("@site/", "./")
: docPath;
return new Promise<string | undefined>((resolve) => {
fs.readFile(path.resolve(accessPath), (err, data) => {
if (err) {
resolve(undefined);
}
return resolve(data.toString());
});
});
};
const getChecklistItems = (docContent: string) => {
const regex = /<ChecklistItem[\s\n\r\t]+id="((?:\w|\d|-|_)+)"[\s\n\r\t]*>/g;
const matches = docContent.matchAll(regex);
const itemIds = Array.from(matches).map((match) => match[1]);
return itemIds;
};
const getUnitById = (id: string) => {
// tutorial/<unit-name>/<ui-scope(optional)>/<tutorial-slug>
const unitId = id.split("/")[1];
return unitId;
};
export default function plugin(): Plugin {
return {
name: "docusaurus-plugin-refine-checklist",
configureWebpack(config) {
return {
resolve: {
alias: {
"@checklists": path.join(
config.resolve?.alias?.["@generated"],
"docusaurus-plugin-refine-checklist",
"default",
),
},
},
};
},
async contentLoaded({ allContent, actions }): Promise<void> {
if (!process.env.DISABLE_CHECKLISTS) {
console.log("Composing Refine tutorial checklists...");
const { createData } = actions;
const currentVersion = (
allContent[
"docusaurus-plugin-content-docs"
] as ContentPluginType
).default.loadedVersions[0];
const allDocs = currentVersion.docs as DocusaurusDoc[];
const allTutorials: DocusaurusDoc[] = allDocs.filter(
(doc) =>
doc.id.startsWith("tutorial/") &&
doc.id !== "tutorial/tutorial",
);
const tutorialsWithChecklist = await Promise.all(
allTutorials.map(async (tutorial) => {
const docContent = await getDocContent(tutorial.source);
const checklistItemIds = getChecklistItems(
docContent ?? "",
);
return {
id: tutorial.id,
unit: getUnitById(tutorial.id),
title: tutorial.title,
checklist: checklistItemIds.map((id, index) => ({
id,
index,
})),
};
}),
);
const data = {
items: tutorialsWithChecklist,
};
await createData(
`tutorial-checklist-data.json`,
JSON.stringify(data),
);
} else {
const { createData } = actions;
await createData(
`tutorial-checklist-data.json`,
JSON.stringify({ items: [] }),
);
}
},
};
}

View File

@@ -0,0 +1,31 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
async function microsoftClarity() {
return {
name: "docusaurus-plugin-refine-clarity",
injectHtmlTags() {
return {
headTags: [
{
tagName: "link",
attributes: {
rel: "preconnect",
href: "https://www.clarity.ms",
},
},
{
tagName: "script",
innerHTML: `
(function(c,l,a,r,i,t,y){
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
})(window, document, "clarity", "script", "kv1shigbgy");`,
},
],
};
},
};
}
exports.default = microsoftClarity;

View File

@@ -0,0 +1,29 @@
import type { Plugin } from "@docusaurus/types";
export default async function microsoftClarity(): Promise<Plugin> {
return {
name: "docusaurus-plugin-refine-clarity",
injectHtmlTags() {
return {
headTags: [
{
tagName: "link",
attributes: {
rel: "preconnect",
href: "https://www.clarity.ms",
},
},
{
tagName: "script",
innerHTML: `
(function(c,l,a,r,i,t,y){
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
})(window, document, "clarity", "script", "jquujqps85");`,
},
],
};
},
};
}

View File

@@ -0,0 +1,453 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
function _optionalChain(ops) {
let lastAccessLHS = undefined;
let value = ops[0];
let i = 1;
while (i < ops.length) {
const op = ops[i];
const fn = ops[i + 1];
i += 2;
if (
(op === "optionalAccess" || op === "optionalCall") &&
value == null
) {
return undefined;
}
if (op === "access" || op === "optionalAccess") {
lastAccessLHS = value;
value = fn(value);
} else if (op === "call" || op === "optionalCall") {
value = fn((...args) => value.call(lastAccessLHS, ...args));
lastAccessLHS = undefined;
}
}
return value;
}
//
var _fsextra = require("fs-extra");
var _fsextra2 = _interopRequireDefault(_fsextra);
var _ora = require("ora");
var _ora2 = _interopRequireDefault(_ora);
var _path = require("path");
var _path2 = _interopRequireDefault(_path);
var _reactdocgentypescript = require("react-docgen-typescript");
/** TYPES */
/** CONSTANTS */
const packagesDir = _path2.default.join(__dirname, "./../..", "./packages");
const sourceDir = "./src";
const excludedFilePatterns = [
"node_modules",
"tsup.config.ts",
".test.",
".spec.",
];
const excludedValueDeclarationPatterns = ["node_modules/antd/lib/list/"];
const excludePropPatterns = [/^__.*/];
const excludedProps = [
"className",
"classNames",
"styles",
"unstyled",
"component",
"key",
"ref",
"style",
"sx",
"m",
"mx",
"my",
"mt",
"ml",
"mr",
"mb",
"p",
"px",
"py",
"pt",
"pl",
"pr",
"pb",
];
const replacementProps = {
// "null | string | number | false | true | ReactElement<any, string | JSXElementConstructor<any>> | ReactFragment | ReactPortal": "ReactNode",
ReactElement:
"ReactElement<any, string | ((props: any) => ReactElement<any, any>) | (new (props: any) => Component<any, any, any>)>",
"ReactNode | (value: number) => ReactNode":
"string | number | boolean | {} | ReactElement<any, string | ((props: any) => ReactElement<any, any>) | (new (props: any) => Component<any, any, any>)> | ReactNodeArray | ReactPortal | ((value: number) => ReactNode)",
ActionButtonRenderer:
"ReactNode | ({ defaultButtons: ReactNode }) => ReactNode",
"DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>":
"DetailedHTMLProps<HTMLDivElement>",
"false | OpenNotificationParams | ((data?: unknown, values?: unknown, resource?: string) => OpenNotificationParams)":
"false | OpenNotificationParams | (data, values, resource) => OpenNotificationParams",
"false | OpenNotificationParams | ((error?: unknown, values?: unknown, resource?: string) => OpenNotificationParams)":
"false | OpenNotificationParams | (error, values, resource) => OpenNotificationParams",
'SvgIconProps<"svg", {}>': "SvgIconProps",
SpaceProps: "[`SpaceProps`](https://styled-system.com/api#space)",
"((value: DeleteOneResponse<BaseRecord>) => void)":
"(value: DeleteOneResponse) => void",
"{ [key: string]: any; ids?: BaseKey[]; }":
"{ [key]: any; ids?: BaseKey[]; }",
"BaseKey | BaseKey[]":
"[BaseKey](/docs/core/interface-references/#basekey) | [BaseKey[]](/docs/core/interface-references/#basekey)",
BaseKey: "[BaseKey](/docs/core/interface-references/#basekey)",
MetaDataQuery:
"[MetaDataQuery](/docs/core/interface-references/#metadataquery)",
CrudFilters: "[CrudFilters](/docs/core/interface-references/#crudfilters)",
CrudSorting: "[CrudSorting](/docs/core/interface-references/#crudsorting)",
};
const spinner = _ora2.default.call(void 0, "Generating Refine declarations...");
/** HELPERS */
const getPackageNamePathMap = async (directory) => {
const packages = await _fsextra2.default.readdir(directory);
const packageNamePathMap = {};
const includedPackages =
_optionalChain([
process,
"access",
(_2) => _2.env,
"access",
(_3) => _3.INCLUDED_PACKAGES,
"optionalAccess",
(_4) => _4.split,
"call",
(_5) => _5(","),
]) || [];
await Promise.all(
packages.map(async (packageName) => {
const packagePath = _path2.default.join(
directory,
packageName,
"package.json",
);
if (_fsextra2.default.existsSync(packagePath)) {
const packageJson = await _fsextra2.default.readJSON(
packagePath,
);
if (
includedPackages.length == 0 ||
includedPackages.some((p) => packageName.includes(p))
) {
packageNamePathMap[packageJson.name] = _path2.default.join(
packagePath,
"..",
);
}
}
return packageName;
}),
);
return packageNamePathMap;
};
const getPaths = async (packageDir, excludedPatterns) => {
const dir = await _fsextra2.default.readdir(packageDir);
const filtered = [];
await Promise.all(
dir.map(async (file) => {
const result = await _fsextra2.default.pathExists(
_path2.default.join(packageDir, file),
);
if (result) {
filtered.push(file);
}
}),
);
return filtered
.map((p) => _path2.default.join(packageDir, p))
.filter(
(p) => !excludedPatterns.some((pattern) => p.includes(pattern)),
);
};
const _getPrefixFromDeclarationPath = async (path) => {
const map = await getPackageNamePathMap(packagesDir);
const packageName = Object.keys(map).find((key) => path.includes(map[key]));
return packageName;
};
const getComponentName = (name, _fileName) => {
return name;
// return `${getPrefixFromDeclarationPath(fileName)}#${name}`;
};
const getOutputName = (packageName) => {
return packageName;
};
const declarationFilter = (declaration) => {
return (
!declaration.fileName.includes("node_modules") ||
declaration.fileName.includes("@refinedev")
);
};
const valueDeclarationFilter = (tsDeclaration) => {
// excludedValueDeclarationPatterns includes fileNames of source files to be ignored (partially)
const sourceFileName = _optionalChain([
tsDeclaration,
"optionalAccess",
(_6) => _6.getSourceFile,
"call",
(_7) => _7(),
"access",
(_8) => _8.fileName,
]);
// if sourceFileName includes any of the excludedValueDeclarationPatterns then ignore it
const isIgnored = excludedValueDeclarationPatterns.some((pattern) =>
_optionalChain([
sourceFileName,
"optionalAccess",
(_9) => _9.includes,
"call",
(_10) => _10(pattern),
]),
);
return !isIgnored;
};
const createParser = (configPath) => {
const docgenParser = _reactdocgentypescript.withCustomConfig.call(
void 0,
_path2.default.join(configPath),
{
savePropValueAsString: true,
shouldExtractLiteralValuesFromEnum: true,
shouldRemoveUndefinedFromOptional: true,
shouldIncludePropTagMap: true,
componentNameResolver: (exp, source) => {
const name = getComponentName(exp.getName(), source.fileName);
if (valueDeclarationFilter(exp.valueDeclaration)) {
return name;
}
return `IGNORED_${name}`;
},
propFilter: (prop) => {
const isExcluded =
excludedProps.includes(prop.name) ||
excludePropPatterns.some((pattern) =>
pattern.test(prop.name),
);
const isExternal =
prop.declarations &&
prop.declarations.length > 0 &&
!Boolean(prop.declarations.find(declarationFilter));
const isUnknown = typeof prop.declarations === "undefined";
if (isExcluded || isExternal || isUnknown) {
return false;
}
return true;
},
},
);
return docgenParser;
};
const normalizeMarkdownLinks = (value) => {
return value.replace(/\[(.*?)\]\s{1}\((.*?)\)/g, (_, p1, p2) => {
return `[${p1}](${p2})`;
});
};
const prepareDeclaration = (declaration) => {
const data = { ...declaration };
delete data.methods;
delete data.tags;
data.generatedAt = Date.now();
Object.keys(data.props).forEach((prop) => {
data.props[prop].type.name = normalizeMarkdownLinks(
data.props[prop].type.name,
);
delete data.props[prop].parent;
delete data.props[prop].declarations;
if (data.props[prop].type.raw === "ReactNode") {
data.props[prop].type.name = "ReactNode";
}
if (data.props[prop].type.name in replacementProps) {
data.props[prop].type.name =
replacementProps[data.props[prop].type.name];
}
if (data.props[prop].type.name === "enum") {
data.props[prop].type.name = data.props[prop].type.value
.map((val) => val.value)
.join(" | ");
}
});
const ordered = Object.keys(data.props)
// .sort()
.reduce((obj, key) => {
obj[key] = data.props[key];
return obj;
}, {});
data.props = ordered;
return data;
};
const transposeDeclarations = (declarations) => {
const transposed = {};
declarations.forEach((declaration) => {
transposed[declaration.displayName] = declaration;
});
return transposed;
};
const generateDeclarations = async (packagePaths) => {
const generated = {};
await Promise.all(
packagePaths.map(async ([packageName, packagePath]) => {
const parser = createParser(
_path2.default.join(packagePath, "./tsconfig.json"),
);
const sourcePath = _path2.default.join(packagePath, sourceDir);
if (!(await _fsextra2.default.pathExists(sourcePath))) {
spinner.fail("Component path does not exist", sourcePath);
process.exit(1);
}
const declarationPaths = await getPaths(
sourcePath,
excludedFilePatterns,
);
const parsed = parser
.parse(declarationPaths)
.map(prepareDeclaration);
const transposed = transposeDeclarations(parsed);
const outputName = getOutputName(packageName);
generated[outputName] = transposed;
spinner.stop();
spinner.start(`- Generated declarations - ${packageName}`);
return [packageName, packagePath];
}),
);
return generated;
};
/** DOCGEN */
const handleDocgen = async () => {
const packagePathMap = await getPackageNamePathMap(packagesDir);
const packagePathMapArray = Object.entries(packagePathMap);
spinner.stop();
spinner.start(`- Found ${packagePathMapArray.length} packages`);
const res = await generateDeclarations(packagePathMapArray);
spinner.succeed("Generated declarations");
return res;
};
function plugin() {
return {
name: "docusaurus-plugin-refine-docgen",
getPathsToWatch: function () {
return [packagesDir];
},
async loadContent() {
if (!process.env.DISABLE_DOCGEN) {
spinner.start();
return await handleDocgen();
}
return {};
},
configureWebpack(config) {
return {
resolve: {
alias: {
"@docgen": _path2.default.join(
_optionalChain([
config,
"access",
(_11) => _11.resolve,
"optionalAccess",
(_12) => _12.alias,
"optionalAccess",
(_13) => _13["@generated"],
]),
"docusaurus-plugin-refine-docgen",
"default",
),
},
},
};
},
async contentLoaded({ content, actions }) {
if (!process.env.DISABLE_DOCGEN) {
_ora2.default
.call(void 0, "Creating Refine declaration files...")
.succeed();
const { createData } = actions;
const data = [];
Object.entries(content).forEach(
([packageName, packageDeclarations]) => {
Object.entries(packageDeclarations).forEach(
([componentName, declaration]) => {
data.push(
createData(
`${packageName}/${componentName}.json`,
JSON.stringify(declaration),
),
);
},
);
},
);
await Promise.all(data);
}
},
};
}
exports.default = plugin;

View File

@@ -0,0 +1,387 @@
import { Plugin } from "@docusaurus/types";
//
import fs from "fs-extra";
import ora from "ora";
import path from "path";
import {
ComponentDoc,
PropItem,
withCustomConfig,
} from "react-docgen-typescript";
import { ParentType, Props } from "react-docgen-typescript/lib/parser";
import ts from "typescript";
/** TYPES */
type DeclarationType = Omit<ComponentDoc, "methods"> &
Partial<Pick<ComponentDoc, "methods">> & {
generatedAt?: number;
};
type DocgenContent = Record<string, Record<string, DeclarationType>>;
/** CONSTANTS */
const packagesDir = path.join(__dirname, "./../..", "./packages");
const sourceDir = "./src";
const excludedFilePatterns = [
"node_modules",
"tsup.config.ts",
".test.",
".spec.",
];
const excludedValueDeclarationPatterns = ["node_modules/antd/lib/list/"];
const excludePropPatterns = [/^__.*/];
const excludedProps = [
"className",
"classNames",
"styles",
"unstyled",
"component",
"key",
"ref",
"style",
"sx",
"m",
"mx",
"my",
"mt",
"ml",
"mr",
"mb",
"p",
"px",
"py",
"pt",
"pl",
"pr",
"pb",
];
const replacementProps: Record<string, string> = {
// "null | string | number | false | true | ReactElement<any, string | JSXElementConstructor<any>> | ReactFragment | ReactPortal": "ReactNode",
ReactElement:
"ReactElement<any, string | ((props: any) => ReactElement<any, any>) | (new (props: any) => Component<any, any, any>)>",
"ReactNode | (value: number) => ReactNode":
"string | number | boolean | {} | ReactElement<any, string | ((props: any) => ReactElement<any, any>) | (new (props: any) => Component<any, any, any>)> | ReactNodeArray | ReactPortal | ((value: number) => ReactNode)",
ActionButtonRenderer:
"ReactNode | ({ defaultButtons: ReactNode }) => ReactNode",
"DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>":
"DetailedHTMLProps<HTMLDivElement>",
"false | OpenNotificationParams | ((data?: unknown, values?: unknown, resource?: string) => OpenNotificationParams)":
"false | OpenNotificationParams | (data, values, resource) => OpenNotificationParams",
"false | OpenNotificationParams | ((error?: unknown, values?: unknown, resource?: string) => OpenNotificationParams)":
"false | OpenNotificationParams | (error, values, resource) => OpenNotificationParams",
'SvgIconProps<"svg", {}>': "SvgIconProps",
SpaceProps: "[`SpaceProps`](https://styled-system.com/api#space)",
"((value: DeleteOneResponse<BaseRecord>) => void)":
"(value: DeleteOneResponse) => void",
"{ [key: string]: any; ids?: BaseKey[]; }":
"{ [key]: any; ids?: BaseKey[]; }",
"BaseKey | BaseKey[]":
"[BaseKey](/docs/core/interface-references/#basekey) | [BaseKey[]](/docs/core/interface-references/#basekey)",
BaseKey: "[BaseKey](/docs/core/interface-references/#basekey)",
MetaDataQuery:
"[MetaDataQuery](/docs/core/interface-references/#metadataquery)",
CrudFilters: "[CrudFilters](/docs/core/interface-references/#crudfilters)",
CrudSorting: "[CrudSorting](/docs/core/interface-references/#crudsorting)",
};
const spinner = ora("Generating Refine declarations...");
/** HELPERS */
const getPackageNamePathMap = async (directory: string) => {
const packages = await fs.readdir(directory);
const packageNamePathMap: Record<string, string> = {};
const includedPackages = process.env.INCLUDED_PACKAGES?.split(",") || [];
await Promise.all(
packages.map(async (packageName) => {
const packagePath = path.join(
directory,
packageName,
"package.json",
);
if (fs.existsSync(packagePath)) {
const packageJson = await fs.readJSON(packagePath);
if (
includedPackages.length == 0 ||
includedPackages.some((p) => packageName.includes(p))
) {
packageNamePathMap[packageJson.name] = path.join(
packagePath,
"..",
);
}
}
return packageName;
}),
);
return packageNamePathMap;
};
const getPaths = async (packageDir: string, excludedPatterns: string[]) => {
const dir = await fs.readdir(packageDir);
const filtered: string[] = [];
await Promise.all(
dir.map(async (file) => {
const result = await fs.pathExists(path.join(packageDir, file));
if (result) {
filtered.push(file);
}
}),
);
return filtered
.map((p) => path.join(packageDir, p))
.filter(
(p) => !excludedPatterns.some((pattern) => p.includes(pattern)),
);
};
const _getPrefixFromDeclarationPath = async (path: string) => {
const map = await getPackageNamePathMap(packagesDir);
const packageName = Object.keys(map).find((key) => path.includes(map[key]));
return packageName;
};
const getComponentName = (name: string, _fileName: string) => {
return name;
// return `${getPrefixFromDeclarationPath(fileName)}#${name}`;
};
const getOutputName = (packageName: string) => {
return packageName;
};
const declarationFilter = (declaration: ParentType) => {
return (
!declaration.fileName.includes("node_modules") ||
declaration.fileName.includes("@refinedev")
);
};
const valueDeclarationFilter = (tsDeclaration?: ts.Declaration) => {
// excludedValueDeclarationPatterns includes fileNames of source files to be ignored (partially)
const sourceFileName = tsDeclaration?.getSourceFile().fileName;
// if sourceFileName includes any of the excludedValueDeclarationPatterns then ignore it
const isIgnored = excludedValueDeclarationPatterns.some((pattern) =>
sourceFileName?.includes(pattern),
);
return !isIgnored;
};
const createParser = (configPath: string) => {
const docgenParser = withCustomConfig(path.join(configPath), {
savePropValueAsString: true,
shouldExtractLiteralValuesFromEnum: true,
shouldRemoveUndefinedFromOptional: true,
shouldIncludePropTagMap: true,
componentNameResolver: (exp, source) => {
const name = getComponentName(exp.getName(), source.fileName);
if (valueDeclarationFilter(exp.valueDeclaration)) {
return name;
}
return `IGNORED_${name}`;
},
propFilter: (prop: PropItem) => {
const isExcluded =
excludedProps.includes(prop.name) ||
excludePropPatterns.some((pattern) => pattern.test(prop.name));
const isExternal =
prop.declarations &&
prop.declarations.length > 0 &&
!Boolean(prop.declarations.find(declarationFilter));
const isUnknown = typeof prop.declarations === "undefined";
if (isExcluded || isExternal || isUnknown) {
return false;
}
return true;
},
});
return docgenParser;
};
const normalizeMarkdownLinks = (value: string) => {
return value.replace(/\[(.*?)\]\s{1}\((.*?)\)/g, (_, p1, p2) => {
return `[${p1}](${p2})`;
});
};
const prepareDeclaration = (declaration: ComponentDoc) => {
const data: DeclarationType = { ...declaration };
delete data.methods;
delete data.tags;
data.generatedAt = Date.now();
Object.keys(data.props).forEach((prop) => {
data.props[prop].type.name = normalizeMarkdownLinks(
data.props[prop].type.name,
);
delete data.props[prop].parent;
delete data.props[prop].declarations;
if (data.props[prop].type.raw === "ReactNode") {
data.props[prop].type.name = "ReactNode";
}
if (data.props[prop].type.name in replacementProps) {
data.props[prop].type.name =
replacementProps[data.props[prop].type.name];
}
if (data.props[prop].type.name === "enum") {
data.props[prop].type.name = data.props[prop].type.value
.map((val: { value: string }) => val.value)
.join(" | ");
}
});
const ordered = Object.keys(data.props)
// .sort()
.reduce((obj, key) => {
obj[key] = data.props[key];
return obj;
}, {} as Props);
data.props = ordered;
return data;
};
const transposeDeclarations = (declarations: DeclarationType[]) => {
const transposed: Record<string, DeclarationType> = {};
declarations.forEach((declaration) => {
transposed[declaration.displayName] = declaration;
});
return transposed;
};
const generateDeclarations = async (packagePaths: [string, string][]) => {
const generated: Record<string, Record<string, DeclarationType>> = {};
await Promise.all(
packagePaths.map(async ([packageName, packagePath]) => {
const parser = createParser(
path.join(packagePath, "./tsconfig.json"),
);
const sourcePath = path.join(packagePath, sourceDir);
if (!(await fs.pathExists(sourcePath))) {
spinner.fail("Component path does not exist", sourcePath);
process.exit(1);
}
const declarationPaths = await getPaths(
sourcePath,
excludedFilePatterns,
);
const parsed = parser
.parse(declarationPaths)
.map(prepareDeclaration);
const transposed = transposeDeclarations(parsed);
const outputName = getOutputName(packageName);
generated[outputName] = transposed;
spinner.stop();
spinner.start(`- Generated declarations - ${packageName}`);
return [packageName, packagePath];
}),
);
return generated;
};
/** DOCGEN */
const handleDocgen = async () => {
const packagePathMap = await getPackageNamePathMap(packagesDir);
const packagePathMapArray = Object.entries(packagePathMap);
spinner.stop();
spinner.start(`- Found ${packagePathMapArray.length} packages`);
const res = await generateDeclarations(packagePathMapArray);
spinner.succeed("Generated declarations");
return res;
};
export default function plugin(): Plugin<DocgenContent> {
return {
name: "docusaurus-plugin-refine-docgen",
getPathsToWatch: function () {
return [packagesDir];
},
async loadContent() {
if (!process.env.DISABLE_DOCGEN) {
spinner.start();
return await handleDocgen();
}
return {};
},
configureWebpack(config) {
return {
resolve: {
alias: {
"@docgen": path.join(
config.resolve?.alias?.["@generated"],
"docusaurus-plugin-refine-docgen",
"default",
),
},
},
};
},
async contentLoaded({ content, actions }): Promise<void> {
if (!process.env.DISABLE_DOCGEN) {
ora("Creating Refine declaration files...").succeed();
const { createData } = actions;
const data: Promise<string>[] = [];
Object.entries(content).forEach(
([packageName, packageDeclarations]) => {
Object.entries(packageDeclarations).forEach(
([componentName, declaration]) => {
data.push(
createData(
`${packageName}/${componentName}.json`,
JSON.stringify(declaration),
),
);
},
);
},
);
await Promise.all(data);
}
},
};
}

View File

@@ -0,0 +1,110 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
function _nullishCoalesce(lhs, rhsFn) {
if (lhs != null) {
return lhs;
} else {
return rhsFn();
}
}
function _optionalChain(ops) {
let lastAccessLHS = undefined;
let value = ops[0];
let i = 1;
while (i < ops.length) {
const op = ops[i];
const fn = ops[i + 1];
i += 2;
if (
(op === "optionalAccess" || op === "optionalCall") &&
value == null
) {
return undefined;
}
if (op === "access" || op === "optionalAccess") {
lastAccessLHS = value;
value = fn(value);
} else if (op === "call" || op === "optionalCall") {
value = fn((...args) => value.call(lastAccessLHS, ...args));
lastAccessLHS = undefined;
}
}
return value;
}
var _fsextra = require("fs-extra");
var _fsextra2 = _interopRequireDefault(_fsextra);
function pluginExampleRedirectsPages() {
return {
name: "refine-plugin-handle-example-redirects",
async postBuild() {
const redirects = collectRedirects();
const redirectFiles = generateRedirectFiles(redirects);
// Write files only at the end: make code more easy to test without IO
await Promise.all(
redirectFiles.map((file) => writeRedirectFile(file)),
);
},
};
}
exports.default = pluginExampleRedirectsPages;
async function writeRedirectFile(file) {
try {
// User-friendly security to prevent file overrides
if (await _fsextra2.default.pathExists(file.fileAbsolutePath)) {
throw new Error(
"The redirect plugin is not supposed to override existing files.",
);
}
await _fsextra2.default.outputFile(
file.fileAbsolutePath,
file.fileContent,
// Hard security to prevent file overrides
// See https://stackoverflow.com/a/34187712/82609
{ flag: "wx" },
);
} catch (err) {
// logger.error`Redirect file creation error for path=${file.fileAbsolutePath}.`;
throw err;
}
}
const htmlTemplate = (to) => `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<script>
window.location.href = '${to}';
</script>
</html>
`;
const collectRedirects = () => {
const redirects = _fsextra2.default.readJSONSync(
"./example-redirects.json",
);
return _nullishCoalesce(
_optionalChain([redirects, "optionalAccess", (_) => _.redirects]),
() => [],
);
};
const generateRedirectFiles = (redirects) => {
return redirects.map((redirect) => {
const path = `${redirect.from}/index.html`;
return {
fileAbsolutePath: `./build/${path}`,
fileContent: htmlTemplate(redirect.to),
};
});
};

View File

@@ -0,0 +1,78 @@
import fs from "fs-extra";
import type { Plugin } from "@docusaurus/types";
export default function pluginExampleRedirectsPages(): Plugin<void> {
return {
name: "refine-plugin-handle-example-redirects",
async postBuild() {
const redirects: RedirectItem[] = collectRedirects();
const redirectFiles = generateRedirectFiles(redirects);
// Write files only at the end: make code more easy to test without IO
await Promise.all(
redirectFiles.map((file) => writeRedirectFile(file)),
);
},
};
}
async function writeRedirectFile(file: RedirectFile): Promise<void> {
try {
// User-friendly security to prevent file overrides
if (await fs.pathExists(file.fileAbsolutePath)) {
throw new Error(
"The redirect plugin is not supposed to override existing files.",
);
}
await fs.outputFile(
file.fileAbsolutePath,
file.fileContent,
// Hard security to prevent file overrides
// See https://stackoverflow.com/a/34187712/82609
{ flag: "wx" },
);
} catch (err) {
// logger.error`Redirect file creation error for path=${file.fileAbsolutePath}.`;
throw err;
}
}
const htmlTemplate = (to: string) => `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<script>
window.location.href = '${to}';
</script>
</html>
`;
const collectRedirects = () => {
const redirects = fs.readJSONSync("./example-redirects.json");
return redirects?.redirects ?? [];
};
const generateRedirectFiles = (redirects: RedirectItem[]) => {
return redirects.map((redirect) => {
const path = `${redirect.from}/index.html`;
return {
fileAbsolutePath: `./build/${path}`,
fileContent: htmlTemplate(redirect.to),
};
});
};
type RedirectFile = {
fileAbsolutePath: string;
fileContent: string;
};
type RedirectItem = {
from: string;
to: string;
};

View File

@@ -0,0 +1,205 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
function _nullishCoalesce(lhs, rhsFn) {
if (lhs != null) {
return lhs;
} else {
return rhsFn();
}
}
function _optionalChain(ops) {
let lastAccessLHS = undefined;
let value = ops[0];
let i = 1;
while (i < ops.length) {
const op = ops[i];
const fn = ops[i + 1];
i += 2;
if (
(op === "optionalAccess" || op === "optionalCall") &&
value == null
) {
return undefined;
}
if (op === "access" || op === "optionalAccess") {
lastAccessLHS = value;
value = fn(value);
} else if (op === "call" || op === "optionalCall") {
value = fn((...args) => value.call(lastAccessLHS, ...args));
lastAccessLHS = undefined;
}
}
return value;
}
var _path = require("path");
var _path2 = _interopRequireDefault(_path);
const colorByHash = (input) => {
let hash = 0;
let color = "#";
input.split("").forEach((char) => {
hash = char.charCodeAt(0) + ((hash << 5) - hash);
});
for (let i = 0; i < 3; i++) {
const value = (hash >> (i * 8)) & 0xff;
color += ("00" + value.toString(16)).slice(-2);
}
return color;
};
const addColorToTags = (tags) => {
let colors = [
"#ef4444",
"#f97316",
"#f59e0b",
"#eab308",
"#84cc16",
"#22c55e",
"#10b981",
"#14b8a6",
"#06b6d4",
"#0ea5e9",
"#3b82f6",
"#6366f1",
"#8b5cf6",
"#a855f7",
"#d946ef",
"#ec4899",
"#f43f5e",
];
// if there are more tags than colors, we will reuse colors.
// multiply the colors array until it is bigger than the tags array
while (colors.length < tags.length) {
colors = [...colors, ...colors];
}
const selectedColorIndexes = [];
const tagsWithColor = tags.map((tag) => {
// pick a random color
let randomColorIndex = Math.floor(Math.random() * colors.length);
// if the color is already used, pick another one
while (selectedColorIndexes.includes(randomColorIndex)) {
randomColorIndex = Math.floor(Math.random() * colors.length);
}
const color = colors[randomColorIndex];
selectedColorIndexes.push(randomColorIndex);
return {
name: tag,
color: color,
};
});
return tagsWithColor;
};
function plugin() {
return {
name: "docusaurus-plugin-refine-examples",
configureWebpack(config) {
return {
resolve: {
alias: {
"@examples": _path2.default.join(
_optionalChain([
config,
"access",
(_) => _.resolve,
"optionalAccess",
(_2) => _2.alias,
"optionalAccess",
(_3) => _3["@generated"],
]),
"docusaurus-plugin-refine-examples",
"default",
),
},
},
};
},
async contentLoaded({ allContent, actions }) {
if (!process.env.DISABLE_EXAMPLES) {
console.log("Composing Refine examples...");
const { createData } = actions;
const currentVersion =
allContent["docusaurus-plugin-content-docs"].default
.loadedVersions[0];
const allDocs = currentVersion.docs;
const allExamples = allDocs
.filter(
(doc) =>
doc.id.startsWith("examples/") &&
doc.id !== "examples/examples",
)
.map((doc) => {
const titleFromId =
doc.id
.replace("examples/", "")
.split("/")
.slice(0, -1)
.join("-") +
" " +
doc.title
.replace("antd", "Ant Design")
.replace("mui", "Material UI")
.replace("chakra-ui", "Chakra UI");
return {
// ...doc,
id: doc.id,
baseTitle: doc.title,
title: doc.title
.replace("antd", "Ant Design")
.replace("mui", "Material UI")
.replace("chakra-ui", "Chakra UI"),
displayTitle: _nullishCoalesce(
_nullishCoalesce(
doc.frontMatter["example-title"],
() => titleFromId,
),
() =>
doc.title
.replace("antd", "Ant Design")
.replace("mui", "Material UI")
.replace("chakra-ui", "Chakra UI"),
),
description: doc.description,
permalink: doc.permalink,
tags: doc.frontMatter["example-tags"] || [],
};
});
const allTags = allExamples
.reduce((acc, example) => [...acc, ...example.tags], [])
.filter((tag, index, self) => self.indexOf(tag) === index);
const data = {
examples: allExamples,
tags: addColorToTags(allTags),
};
await createData(`examples-data.json`, JSON.stringify(data));
} else {
const { createData } = actions;
await createData(
`examples-data.json`,
JSON.stringify({ examples: [], tags: [] }),
);
}
},
};
}
exports.default = plugin;

View File

@@ -0,0 +1,205 @@
import { Plugin } from "@docusaurus/types";
import path from "path";
type DocusaurusDoc = {
unversionedId: string;
id: string;
title: string;
description: string;
source: string;
sourceDirName: string;
slug: string;
permalink: string;
draft: boolean;
editUrl: string;
tags: string[];
version: string;
lastUpdatedBy: string;
lastUpdatedAt: number;
formattedLastUpdatedAt: string;
frontMatter: {
id: string;
title: string;
description?: string;
tags?: string[];
};
sidebar: string;
previous?: {
title: string;
permalink: string;
};
next?: {
title: string;
permalink: string;
};
};
type ContentPluginType = {
default: {
loadedVersions: Array<{ docs: DocusaurusDoc[] }>;
};
};
type ExampleDoc = Pick<
DocusaurusDoc,
"id" | "title" | "description" | "permalink"
> & { tags: string[] };
const colorByHash = (input: string) => {
let hash = 0;
let color = "#";
input.split("").forEach((char) => {
hash = char.charCodeAt(0) + ((hash << 5) - hash);
});
for (let i = 0; i < 3; i++) {
const value = (hash >> (i * 8)) & 0xff;
color += ("00" + value.toString(16)).slice(-2);
}
return color;
};
const addColorToTags = (tags: string[]) => {
let colors = [
"#ef4444",
"#f97316",
"#f59e0b",
"#eab308",
"#84cc16",
"#22c55e",
"#10b981",
"#14b8a6",
"#06b6d4",
"#0ea5e9",
"#3b82f6",
"#6366f1",
"#8b5cf6",
"#a855f7",
"#d946ef",
"#ec4899",
"#f43f5e",
];
// if there are more tags than colors, we will reuse colors.
// multiply the colors array until it is bigger than the tags array
while (colors.length < tags.length) {
colors = [...colors, ...colors];
}
const selectedColorIndexes: number[] = [];
const tagsWithColor = tags.map((tag) => {
// pick a random color
let randomColorIndex = Math.floor(Math.random() * colors.length);
// if the color is already used, pick another one
while (selectedColorIndexes.includes(randomColorIndex)) {
randomColorIndex = Math.floor(Math.random() * colors.length);
}
const color = colors[randomColorIndex];
selectedColorIndexes.push(randomColorIndex);
return {
name: tag,
color: color,
};
});
return tagsWithColor;
};
export default function plugin(): Plugin {
return {
name: "docusaurus-plugin-refine-examples",
configureWebpack(config) {
return {
resolve: {
alias: {
"@examples": path.join(
config.resolve?.alias?.["@generated"],
"docusaurus-plugin-refine-examples",
"default",
),
},
},
};
},
async contentLoaded({ allContent, actions }): Promise<void> {
if (!process.env.DISABLE_EXAMPLES) {
console.log("Composing Refine examples...");
const { createData } = actions;
const currentVersion = (
allContent[
"docusaurus-plugin-content-docs"
] as ContentPluginType
).default.loadedVersions[0];
const allDocs = currentVersion.docs as DocusaurusDoc[];
const allExamples: ExampleDoc[] = allDocs
.filter(
(doc) =>
doc.id.startsWith("examples/") &&
doc.id !== "examples/examples",
)
.map((doc) => {
const titleFromId =
doc.id
.replace("examples/", "")
.split("/")
.slice(0, -1)
.join("-") +
" " +
doc.title
.replace("antd", "Ant Design")
.replace("mui", "Material UI")
.replace("chakra-ui", "Chakra UI");
return {
// ...doc,
id: doc.id,
baseTitle: doc.title,
title: doc.title
.replace("antd", "Ant Design")
.replace("mui", "Material UI")
.replace("chakra-ui", "Chakra UI"),
displayTitle:
doc.frontMatter["example-title"] ??
titleFromId ??
doc.title
.replace("antd", "Ant Design")
.replace("mui", "Material UI")
.replace("chakra-ui", "Chakra UI"),
description: doc.description,
permalink: doc.permalink,
tags: doc.frontMatter["example-tags"] || [],
};
});
const allTags = allExamples
.reduce(
(acc, example) => [...acc, ...example.tags],
[] as string[],
)
.filter((tag, index, self) => self.indexOf(tag) === index);
const data = {
examples: allExamples,
tags: addColorToTags(allTags),
};
await createData(`examples-data.json`, JSON.stringify(data));
} else {
const { createData } = actions;
await createData(
`examples-data.json`,
JSON.stringify({ examples: [], tags: [] }),
);
}
},
};
}

View File

@@ -0,0 +1,29 @@
const path = require("path");
module.exports = function (context) {
const { siteConfig } = context;
const { themeConfig } = siteConfig;
return {
name: "@pankod/docusaurus-plugin-intercom",
injectHtmlTags() {
return {
postBodyTags: [
{
tagName: "script",
innerHTML: `
window.intercomSettings = {
api_base: "https://api-iam.intercom.io",
app_id: "m6xbwbzo"
};
// We pre-filled your app ID in the widget URL: 'https://widget.intercom.io/widget/m6xbwbzo'
(function(){var w=window;var ic=w.Intercom;if(typeof ic==="function"){ic('reattach_activator');ic('update',w.intercomSettings);}else{var d=document;var i=function(){i.c(arguments);};i.q=[];i.c=function(args){i.q.push(args);};w.Intercom=i;var l=function(){var s=d.createElement('script');s.type='text/javascript';s.async=true;s.src='https://widget.intercom.io/widget/m6xbwbzo';var x=d.getElementsByTagName('script')[0];x.parentNode.insertBefore(s,x);};if(document.readyState==='complete'){l();}else if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}}})();
`,
},
],
};
},
};
};

View File

@@ -0,0 +1,455 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
async function RefineTemplates() {
return {
name: "docusaurus-plugin-refine-templates",
contentLoaded: async (args) => {
const { content, actions } = args;
const { addRoute, createData } = actions;
await Promise.all(
content.map(async (data) => {
const json = await createData(
`templates-${data.slug}.json`,
JSON.stringify(data, null, 2),
);
addRoute({
path: `/templates/${data.slug}`,
component:
"@site/src/components/templates-detail-page/index",
exact: true,
modules: {
content: json,
},
});
}),
);
},
loadContent: async () => {
return templates;
},
};
}
exports.default = RefineTemplates;
const templates = [
{
slug: "crm-application",
title: "CRM Application",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-refine-crm.jpg",
],
runOnYourLocalPath: "app-crm",
liveDemo: "https://example.crm.refine.dev/",
github: "https://github.com/refinedev/refine/tree/master/examples/app-crm",
reactPlatform: "Vite",
uiFramework: "Ant Design",
dataProvider: "Nestjs-query",
authProvider: "Custom",
description: `
This CRM app example, built with Refine, demonstrates a complete solution for enterprise-level CRM internal tool needs. It has a wide range of functionalities for real-use cases, which are extensively utilized by enterprise companies.
The app connected to GraphQL API through Refine's Nestjs-query data provider, and its user interface is developed using Ant Design, which Refine offers built-in UI framework support.
We built this template to demonstrate how the Refine framework simplifies and speeds up development. It is a valuable resource, offering insights into Refine's best practices and modern development techniques.
The source code of the CRM app is also open-source; feel free to use or inspect it to discover how Refine works. Being production-ready, you can either build your own CRM internal tool using it as a code base or directly implement it as is.
### Key Features:
- **Dashboard**: Overview of CRM activities, key metrics, and customer interactions.
- **Calendar**: Manage appointments and events.
- **Scrumboard-Project Kanban**: Streamline project management and task tracking.
- **Sales Pipeline**: Visualize sales stages and track lead conversions.
- **Companies**: Organize business contacts with detailed profiles.
- **Contacts**: Efficient management of individual customer interactions.
- **Quotes**: Create, send, and manage customer quotes.
- **Administration**: Customize CRM settings, user roles, and permissions.
This CRM app template can be used in for various app requirements like B2B applications, internal tools, admin panel, dashboard and all CRUD applications, providing a comprehensive platform for:
- Human Resource Management (HRM) Tools
- IT Service Management (ITSM) Tools
- Network Monitoring Tools
- Risk Management Tools
- Customer Support Tools
- Financial Planning Systems
- Customer Analytics Tools
- Inventory Management Systems
- Supply Chain Management Tools
- Retail Management Systems
- Business Intelligence Tools
- Electronic Health Record (EHR) Systems
- Patient Management Systems
- Health Information Exchange (HIE) Systems
- Pharmacy Management Systems
`,
},
{
slug: "next-js-tailwind",
title: "E-Commerce Application Storefront",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-storefront.jpg",
],
runOnYourLocalPath: "finefoods-client",
liveDemo: "https://example.refine.dev/",
github: "https://github.com/refinedev/refine/tree/master/examples/finefoods-client",
reactPlatform: "Next.js",
uiFramework: "Headless",
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:
- With Refine's headless approach, we demonstrated how to apply a style of your choice, such as Tailwind CSS.
- SSR support with Next.js. Refine supports SSR with Next.js and Remix. You can use this template as a starter point for Next.js-powered Refine apps.
- REST API implementation
The source code is also open-source; feel free to use or inspect it to discover how to Refine works with any custom designs & UI library and supports SSR with Next.js.`,
},
{
slug: "react-admin-panel",
title: "B2B Admin Panel with Material UI",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-finefoods-material-ui.jpg",
],
runOnYourLocalPath: "finefoods-material-ui",
liveDemo: "https://example.mui.admin.refine.dev",
github: "https://github.com/refinedev/refine/tree/master/examples/finefoods-material-ui",
reactPlatform: "Vite",
uiFramework: "Material UI",
dataProvider: "Rest API",
authProvider: "Custom",
description: `
This example of a React admin panel, built with Refine, provides a comprehensive solution for the needs of enterprise-level admin panels. It features a full range of functionalities typical in products used by enterprise companies.
The admin panel connects to a REST API using a Simple REST data provider. Its user interface is developed with Material UI, which Refine offers built-in UI framework support.
We built this template to showcase the efficiency and ease of using the Refine framework in development. It is a valuable resource, offering insights into Refine's best practices and modern development techniques. As it's ready for production, you can use this template as a foundation to build your own React admin panel or implement it as it is.
The template is open-source, so you can freely use or examine it to understand how Refine works.
### Key Features:
- **Dashboard**: Get an overview of food ordering activities, track product performance, and view insightful charts.
- **Orders**: Manage, track, and filter all customer orders.
- **Users**: Administer customer and courier accounts and data.
- **Stores**: View and manage the list of participating stores.
- **Categories**: Categorize and organize menu items and store types.
- **Couriers**: Monitor and manage courier activity and interactions.
- **Reviews**: Handle customer feedback, review ratings, and respond to comments.
This admin panel template can be used for various app requirements like B2B applications, internal tools, admin panel, dashboard, and all CRUD applications, providing a comprehensive platform for managing order interactions, restaurant management, and sales processes.`,
},
{
slug: "react-admin-panel-ant-design",
title: "B2B Internal tool with Ant Design",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-finefoods-ant-design.jpg",
],
runOnYourLocalPath: "finefoods-antd",
liveDemo: "https://example.admin.refine.dev",
github: "https://github.com/refinedev/refine/tree/master/examples/finefoods-antd",
reactPlatform: "Vite",
uiFramework: "Ant Design",
dataProvider: "Rest API",
authProvider: "Custom",
description: `
This example of a B2B React admin panel, built with Refine, provides a comprehensive solution for the needs of enterprise-level internal tools. It features a full range of functionalities typical in products used by enterprise companies.
The admin panel connects to a REST API using a Simple REST data provider. Its user interface is developed with Ant Design, which Refine offers built-in UI framework support.
We built this template to showcase the efficiency and ease of using the Refine framework in development. It is a valuable resource, offering insights into Refine's best practices and modern development techniques. As it's ready for production, you can use this template as a foundation to build your own React admin panel or implement it as it is.
The template is open-source, so you can freely use or examine it to understand how Refine works.
### Key Features:
- **Dashboard**: Get an overview of food ordering activities, track product performance, and view insightful charts.
- **Orders**: Manage, track, and filter all customer orders.
- **Users**: Administer customer and courier accounts and data.
- **Stores**: View and manage the list of participating stores.
- **Categories**: Categorize and organize menu items and store types.
- **Couriers**: Monitor and manage courier activity and interactions.
- **Reviews**: Handle customer feedback, review ratings, and respond to comments.
This admin panel template can be used in for various app requirements like B2B applications, internal tools, admin panel, dashboard and all CRUD applications, providing a comprehensive platform for managing order interactions, restaurant management, and sales processes.`,
},
{
slug: "next-js-ecommerce-store",
title: "Swag Store",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-store.jpg",
],
runOnYourLocalPath: "store",
liveDemo: "https://store.refine.dev/",
github: "https://github.com/refinedev/refine/tree/master/examples/store",
reactPlatform: "Next.js",
uiFramework: "Ant Design",
dataProvider: "Medusa",
authProvider: "Medusa",
description: `
This template is a comprehensive e-commerce storefront app created using Refine, Medusa.js, Next.js, and Stripe, encompassing all the necessary features for an e-commerce web application. Its source code is open-source, allowing you to freely use or explore it to gain a deeper understanding of how Refine integrates with Next.js and Tailwind CSS.
The app interfaces with the Medusa API through Refine's Medusa data provider, and its user interface is crafted with Tailwind CSS. This template is designed to showcase the effectiveness of the Refine framework in streamlining and accelerating the development process for storefront apps, providing an optimal approach to using Refine in storefront app development.
With its production-ready status, this template offers a solid foundation for building your own storefront, whether you choose to use it as a starting point or implement it directly as it is.
### Key Features:
- **Authentication & Authorization**: Securely manages user logins and permissions.
- **Product Listing**: Displays a catalog of products available for purchase.
- **Account Information Pages**: Allows users to view and edit their account details.
- **Product Detail**: Provides detailed information about each product, including descriptions, prices, and images.
- **Shopping Cart**: Enables customers to add products to a virtual cart and manage their selections before checkout.
- **Payment with Stripe**: Offers a secure payment system through Stripe for processing transactions.
- **Gift Code Feature**: Allows gift codes for discounts or special offers during the checkout process.
- **Email Send and Verification**: Handles the sending of emails for purposes like account verification and marketing, and verifies email addresses.
`,
},
{
slug: "supabase-crud-app",
title: "Pixels",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-pixels.jpg",
],
runOnYourLocalPath: "pixels",
liveDemo: "https://pixels.refine.dev/",
github: "https://github.com/refinedev/refine/tree/master/examples/pixels",
tutorial: "https://refine.dev/week-of-refine-supabase/",
reactPlatform: "Vite",
uiFramework: "Ant Design",
dataProvider: "Supabase",
authProvider: "Supabase",
description: `
This example presents the RefineWeek series - a seven-part quickfire guide to help developers learn the ins and outs of Refine and Supabase's powerful capabilities and get going with Refine within a week.
The source code for this CRUD app is open-source, providing an opportunity for you to use or examine it to gain a deeper understanding of how Refine operates. The CRM application featured in this series utilizes a Supabase API through Refine's Supabase data provider, and its user interface is crafted using Ant Design.
This CRM application consumes a Supabase API through Refine's Supabase data provider, and its user interface is developed using Ant Design, which Refine offers built-in UI framework support.
We built this template to demonstrate how the Refine framework simplifies and speeds up development. It is a valuable resource, offering insights into Refine's best practices and modern development techniques.
### Key Features:
- **Realtime Collaboration Features**: Implements real-time updates using Supabase's Realtime for collaborative canvas drawing.
- **Role-Based Authorization**: Incorporates user role-based authorization with different permissions for editors and admins.
- **Audit Logging**: Implements an audit log for tracking pixel drawing activities in both the client and admin apps.
`,
},
{
slug: "react-pdf-invoice-generator",
title: "Invoice Generator - Internal Tool",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-invoice-generator.jpg",
],
runOnYourLocalPath: "refine-week-invoice-generator",
liveDemo: "https://invoice-generator.refine.dev/",
github: "https://github.com/refinedev/refine/tree/master/examples/refine-week-invoice-generator",
tutorial: "https://refine.dev/week-of-refine-strapi/",
reactPlatform: "Vite",
uiFramework: "Ant Design",
dataProvider: "Strapi",
authProvider: "Strapi",
description: `
The Invoicer is a template for an internal tool created as part of the #RefineWeek series, integrating Refine, Strapi, and Ant Design technologies. It's designed for users to manage company registrations, client and contact additions, task creations, and invoice issuance. A standout feature of this app is its ability to generate PDF invoices, enabling the creation of professional documents.
With its PDF generation feature, this open-source internal tool template is ideal for understanding Refine's capabilities. The app uses Strapi for data management, connecting through a Strapi data provider. Its interface is crafted using Ant Design, supported natively by Refine for a seamless UI experience.
We built this template to showcase the efficiency and ease of using the Refine framework in development along with third-party API management tools. It is a valuable resource, offering insights into Refine's best practices and modern development techniques. Being production-ready, this template can serve as a solid base for developing your own React-based internal tools, or it can be used as-is for immediate implementation.
### Key Features
- **Comprehensive CRUD Operations**: The app supports CRUD functionalities for companies, clients, contacts, missions, and invoices.
- **Authentication and Authorization**: Implements secure user authentication and role-based permissions using Refine's authProvider.
- **PDF Invoice Generation**: Includes the capability to generate and view PDF documents of invoices using the @react-pdf/renderer package.
`,
},
{
slug: "win-95-style-admin-panel",
title: "Win95 Style Admin Panel",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-win95.jpg",
],
runOnYourLocalPath: "blog-win95",
liveDemo: "https://win95.refine.dev/",
github: "https://github.com/refinedev/refine/tree/master/examples/blog-win95",
tutorial:
"https://refine.dev/blog/awesome-react-windows95-ui-with-refine/",
reactPlatform: "Vite",
uiFramework: "Headless",
dataProvider: "Supabase",
authProvider: "Supabase",
description: `
This Fun CRUD app example demonstrates how you can customize the Refine app design for specific needs. It is connected to a Supabase backend through Refines Supabase data provider and its user interface is developed using [React95](https://github.com/React95/React95).
The source code of the CRUD app is also open-source; feel free to use or inspect it to discover how Refine works along with external UI packages.
`,
},
{
slug: "react-crud-app",
title: "Realworld Example",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-realworld.jpg",
],
runOnYourLocalPath: "real-world-example",
liveDemo: "https://refine-real-world.netlify.app/",
github: "https://github.com/refinedev/real-world-example",
reactPlatform: "Vite",
uiFramework: "Headless",
dataProvider: "Rest API",
authProvider: "Custom",
description: `
This RealWorld app template displays a comprehensive full-stack application created using Refine, including features like CRUD operations, user authentication, routing, and pagination, among others.
The RealWorld example standard, often called the "Mother of All Demo Apps," is a specification for building full-stack applications that demonstrate real-world usage of various frontend and backend technologies. This standard provides consistent requirements for creating functionally equivalent applications across different technology stacks.
This RealWorld example aims to demonstrate the practical use of Refine in building standards and real-world applications, providing a more authentic and useful example than the usual "to-do list" demonstrations.
Since the source code of this RealWorld app is open-source, you have the freedom to use or explore it to gain insights into the workings of Refine and its integration with Next.js and Tailwind CSS.
### Key Features
- Authenticate users via JWT (login/register pages + logout button on settings page)
- CRUD users (sign up & settings page - no deleting required)
- CRUD Articles
- CRUD Comments on articles (no updating required)
- GET and display paginated lists of articles
- Favorite articles
- Follow other users
`,
},
{
slug: "multitenancy-strapi",
title: "Multitenancy App with Strapi",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-multitenancy-strapi.jpg",
],
runOnYourLocalPath: "multi-tenancy-strapi",
liveDemo: "https://multi-tenancy-strapi.refine.dev",
github: "https://github.com/refinedev/refine/tree/master/examples/multi-tenancy-strapi",
tutorial:
"https://refine.dev/docs/guides-concepts/multi-tenancy/#strapi",
reactPlatform: "Vite",
uiFramework: "Ant Design",
dataProvider: "Strapi",
authProvider: "Strapi",
description: `
Multitenancy is a design approach where a single software instance on a server provides services to multiple clients at the same time. This CRUD app template is an excellent example of implementing multitenant architecture in Refine applications. It connects to a Strapi API using a Strapi data provider and features a user interface developed with Ant Design, which is natively supported by Refine.
This template was developed to demonstrate how the Refine framework can simplify and expedite the development of apps with enterprise-level needs. It's a valuable resource that offers insights into Refine's best practices and modern development techniques.
The source code of this multitenancy app is open-source, allowing you to use or inspect it to see how Refine can fulfill enterprise-grade application requirements.
`,
},
{
slug: "multitenancy-appwrite",
title: "Multitenancy App with Appwrite",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-multitenancy-appwrite.jpg",
],
runOnYourLocalPath: "multi-tenancy-appwrite",
liveDemo: "https://multi-tenancy-appwrite.refine.dev",
github: "https://github.com/refinedev/refine/tree/master/examples/multi-tenancy-appwrite",
tutorial:
"https://refine.dev/docs/guides-concepts/multi-tenancy/#appwrite",
reactPlatform: "Vite",
uiFramework: "Ant Design",
dataProvider: "Appwrite",
authProvider: "Appwrite",
description: `
Multitenancy is a design approach where a single software instance on a server provides services to multiple clients at the same time. This CRUD app template is an excellent example of implementing multitenant architecture in Refine applications. It connects to a Appwrite API using a Appwrite data provider and features a user interface developed with Ant Design, which is natively supported by Refine.
This template was developed to demonstrate how the Refine framework can simplify and expedite the development of apps with enterprise-level needs. It's a valuable resource that offers insights into Refine's best practices and modern development techniques.
The source code of this multitenancy app is open-source, allowing you to use or inspect it to see how Refine can fulfill enterprise-grade application requirements.
`,
},
{
slug: "ant-design-template",
title: "Internal tool template with Ant Design ",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-ant-design-template.jpg",
],
runOnYourLocalPath: "auth-antd",
liveDemo:
"https://codesandbox.io/p/sandbox/github/refinedev/refine/tree/master/examples/auth-antd",
github: "https://github.com/refinedev/refine/tree/master/examples/auth-antd",
tutorial: "https://refine.dev/docs/tutorial/introduction/index/",
reactPlatform: "Vite",
uiFramework: "Ant Design",
dataProvider: "Rest API",
authProvider: "Custom",
description: `
Complete internal tool template built with Material UI. Features authentication and CRUD screens.
`,
},
{
slug: "material-ui-template",
title: "Generic Internal Tool Template with Material UI",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-material-ui-template.jpg",
],
runOnYourLocalPath: "auth-material-ui",
liveDemo:
"https://codesandbox.io/embed/github/refinedev/refine/tree/master/examples/auth-material-ui",
github: "https://github.com/refinedev/refine/tree/master/examples/auth-material-ui",
tutorial: "https://refine.dev/docs/tutorial/introduction/index/",
reactPlatform: "Vite",
uiFramework: "Material UI",
dataProvider: "Rest API",
authProvider: "Custom",
description: `
Complete internal tool template built with Material UI. Features authentication and CRUD screens.
`,
},
{
slug: "mantine-template",
title: "Generic Internal Tool Template with Mantine",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-mantine-template.jpg",
],
runOnYourLocalPath: "auth-mantine",
liveDemo:
"https://codesandbox.io/embed/github/refinedev/refine/tree/master/examples/auth-mantine",
github: "https://github.com/refinedev/refine/tree/master/examples/auth-mantine",
tutorial: "https://refine.dev/docs/tutorial/introduction/index/",
reactPlatform: "Vite",
uiFramework: "Mantine",
dataProvider: "Rest API",
authProvider: "Custom",
description: `
Complete internal tool template built with Mantine. Features authentication and CRUD screens.
`,
},
{
slug: "chakra-ui-template",
title: "Generic Internal Tool Template with Chakra UI",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-chakra-ui-template.jpg",
],
runOnYourLocalPath: "auth-chakra-ui",
liveDemo:
"https://codesandbox.io/embed/github/refinedev/refine/tree/master/examples/auth-chakra-ui",
github: "https://github.com/refinedev/refine/tree/master/examples/auth-chakra-ui",
tutorial: "https://refine.dev/docs/tutorial/introduction/index/",
reactPlatform: "Vite",
uiFramework: "Chakra UI",
dataProvider: "Rest API",
authProvider: "Custom",
description: `
Complete admin panel template built with Chakra UI. Features authentication and CRUD screens.
`,
},
];

View File

@@ -0,0 +1,485 @@
import type { Plugin } from "@docusaurus/types";
export default async function RefineTemplates(): Promise<Plugin> {
return {
name: "docusaurus-plugin-refine-templates",
contentLoaded: async (args) => {
const { content, actions } = args;
const { addRoute, createData } = actions;
await Promise.all(
(content as typeof templates).map(async (data) => {
const json = await createData(
`templates-${data.slug}.json`,
JSON.stringify(data, null, 2),
);
addRoute({
path: `/templates/${data.slug}`,
component:
"@site/src/components/templates-detail-page/index",
exact: true,
modules: {
content: json,
},
});
}),
);
},
loadContent: async () => {
return templates;
},
};
}
const templates = [
{
slug: "crm-application",
title: "CRM Application",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-refine-crm.jpg",
],
runOnYourLocalPath: "app-crm",
liveDemo: "https://example.crm.refine.dev/",
github: "https://github.com/refinedev/refine/tree/master/examples/app-crm",
reactPlatform: "Vite",
uiFramework: "Ant Design",
dataProvider: "Nestjs-query",
authProvider: "Custom",
description: `
This CRM app example, built with Refine, demonstrates a complete solution for enterprise-level CRM internal tool needs. It has a wide range of functionalities for real-use cases, which are extensively utilized by enterprise companies.
The app connected to GraphQL API through Refine's Nestjs-query data provider, and its user interface is developed using Ant Design, which Refine offers built-in UI framework support.
We built this template to demonstrate how the Refine framework simplifies and speeds up development. It is a valuable resource, offering insights into Refine's best practices and modern development techniques.
The source code of the CRM app is also open-source; feel free to use or inspect it to discover how Refine works. Being production-ready, you can either build your own CRM internal tool using it as a code base or directly implement it as is.
### Key Features:
- **Dashboard**: Overview of CRM activities, key metrics, and customer interactions.
- **Calendar**: Manage appointments and events.
- **Scrumboard-Project Kanban**: Streamline project management and task tracking.
- **Sales Pipeline**: Visualize sales stages and track lead conversions.
- **Companies**: Organize business contacts with detailed profiles.
- **Contacts**: Efficient management of individual customer interactions.
- **Quotes**: Create, send, and manage customer quotes.
- **Administration**: Customize CRM settings, user roles, and permissions.
This CRM app template can be used in for various app requirements like B2B applications, internal tools, admin panel, dashboard. Such as:
- Human Resource Management (HRM) Tools
- IT Service Management (ITSM) Tools
- Network Monitoring Tools
- Risk Management Tools
- Customer Support Tools
- Financial Planning Systems
- Customer Analytics Tools
- Inventory Management Systems,
- Sales and Marketing Tools,
- Supply Chain Management Tools
- Retail Management Systems
- Business Intelligence Tools
- Electronic Health Record (EHR) Systems
- Patient Management Systems
- Health Information Exchange (HIE) Systems
- Pharmacy Management Systems
`,
},
{
slug: "next-js-tailwind",
title: "E-Commerce Application Storefront",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-storefront.jpg",
],
runOnYourLocalPath: "finefoods-client",
liveDemo: "https://example.refine.dev/",
github: "https://github.com/refinedev/refine/tree/master/examples/finefoods-client",
reactPlatform: "Next.js",
uiFramework: "Headless",
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:
- With Refine's headless approach, we demonstrated how to apply a style of your choice, such as Tailwind CSS.
- SSR support with Next.js. Refine supports SSR with Next.js and Remix. You can use this template as a starter point for Next.js-powered Refine apps.
- REST API implementation
The source code is also open-source; feel free to use or inspect it to discover how to Refine works with any custom designs & UI library and supports SSR with Next.js.`,
},
{
slug: "react-admin-panel",
title: "B2B Admin Panel with Material UI",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-finefoods-material-ui.jpg",
],
runOnYourLocalPath: "finefoods-material-ui",
liveDemo: "https://example.mui.admin.refine.dev",
github: "https://github.com/refinedev/refine/tree/master/examples/finefoods-material-ui",
reactPlatform: "Vite",
uiFramework: "Material UI",
dataProvider: "Rest API",
authProvider: "Custom",
description: `
This example of a React admin panel, built with Refine, provides a comprehensive solution for the needs of enterprise-level admin panels. It features a full range of functionalities typical in products used by enterprise companies.
The admin panel connects to a REST API using a Simple REST data provider. Its user interface is developed with Material UI, which Refine offers built-in UI framework support.
We built this template to showcase the efficiency and ease of using the Refine framework in development. It is a valuable resource, offering insights into Refine's best practices and modern development techniques. As it's ready for production, you can use this template as a foundation to build your own React admin panel or implement it as it is.
The template is open-source, so you can freely use or examine it to understand how Refine works.
### Key Features:
- **Dashboard**: Get an overview of food ordering activities, track product performance, and view insightful charts.
- **Orders**: Manage, track, and filter all customer orders.
- **Users**: Administer customer and courier accounts and data.
- **Stores**: View and manage the list of participating stores.
- **Categories**: Categorize and organize menu items and store types.
- **Couriers**: Monitor and manage courier activity and interactions.
- **Reviews**: Handle customer feedback, review ratings, and respond to comments.
This admin panel template can be used in for various app requirements like B2B applications, internal tools, admin panel, dashboard. Such as:
- Food Ordering Systems
- Restaurant management Systems
- Portfolio Management Software
- Data Integration Tools
- Healthcare Analytics Software
- Telemedicine Platforms
- Customer Support Tools
- Financial Planning Systems
- Customer Analytics Tools
- Inventory Management Systems
- Workforce Management Systems
- Electronic Health Record (EHR) Systems
- Patient Management Systems
`,
},
{
slug: "react-admin-panel-ant-design",
title: "B2B Internal tool with Ant Design",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-finefoods-ant-design.jpg",
],
runOnYourLocalPath: "finefoods-antd",
liveDemo: "https://example.admin.refine.dev",
github: "https://github.com/refinedev/refine/tree/master/examples/finefoods-antd",
reactPlatform: "Vite",
uiFramework: "Ant Design",
dataProvider: "Rest API",
authProvider: "Custom",
description: `
This example of a B2B React admin panel, built with Refine, provides a comprehensive solution for the needs of enterprise-level internal tools. It features a full range of functionalities typical in products used by enterprise companies.
The admin panel connects to a REST API using a Simple REST data provider. Its user interface is developed with Ant Design, which Refine offers built-in UI framework support.
We built this template to showcase the efficiency and ease of using the Refine framework in development. It is a valuable resource, offering insights into Refine's best practices and modern development techniques. As it's ready for production, you can use this template as a foundation to build your own React admin panel or implement it as it is.
The template is open-source, so you can freely use or examine it to understand how Refine works.
### Key Features:
- **Dashboard**: Get an overview of food ordering activities, track product performance, and view insightful charts.
- **Orders**: Manage, track, and filter all customer orders.
- **Users**: Administer customer and courier accounts and data.
- **Stores**: View and manage the list of participating stores.
- **Categories**: Categorize and organize menu items and store types.
- **Couriers**: Monitor and manage courier activity and interactions.
- **Reviews**: Handle customer feedback, review ratings, and respond to comments.
This admin panel template can be used in for various app requirements like B2B applications, internal tools, admin panel, dashboard. Such as:
- Food Ordering Systems
- Restaurant management Systems
- Portfolio Management Software
- Data Integration Tools
- Healthcare Analytics Software
- Telemedicine Platforms
- Customer Support Tools
- Financial Planning Systems
- Customer Analytics Tools
- Inventory Management Systems
- Workforce Management Systems
- Electronic Health Record (EHR) Systems
- Patient Management Systems
`,
},
{
slug: "next-js-ecommerce-store",
title: "Swag Store",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-store.jpg",
],
runOnYourLocalPath: "store",
liveDemo: "https://store.refine.dev/",
github: "https://github.com/refinedev/refine/tree/master/examples/store",
reactPlatform: "Next.js",
uiFramework: "Ant Design",
dataProvider: "Medusa",
authProvider: "Medusa",
description: `
This template is a comprehensive e-commerce storefront app created using Refine, Medusa.js, Next.js, and Stripe, encompassing all the necessary features for an e-commerce web application. Its source code is open-source, allowing you to freely use or explore it to gain a deeper understanding of how Refine integrates with Next.js and Tailwind CSS.
The app interfaces with the Medusa API through Refine's Medusa data provider, and its user interface is crafted with Tailwind CSS. This template is designed to showcase the effectiveness of the Refine framework in streamlining and accelerating the development process for storefront apps, providing an optimal approach to using Refine in storefront app development.
With its production-ready status, this template offers a solid foundation for building your own storefront, whether you choose to use it as a starting point or implement it directly as it is.
### Key Features:
- **Authentication & Authorization**: Securely manages user logins and permissions.
- **Product Listing**: Displays a catalog of products available for purchase.
- **Account Information Pages**: Allows users to view and edit their account details.
- **Product Detail**: Provides detailed information about each product, including descriptions, prices, and images.
- **Shopping Cart**: Enables customers to add products to a virtual cart and manage their selections before checkout.
- **Payment with Stripe**: Offers a secure payment system through Stripe for processing transactions.
- **Gift Code Feature**: Allows gift codes for discounts or special offers during the checkout process.
- **Email Send and Verification**: Handles the sending of emails for purposes like account verification and marketing, and verifies email addresses.
`,
},
{
slug: "supabase-crud-app",
title: "Pixels",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-pixels.jpg",
],
runOnYourLocalPath: "pixels",
liveDemo: "https://pixels.refine.dev/",
github: "https://github.com/refinedev/refine/tree/master/examples/pixels",
tutorial: "https://refine.dev/week-of-refine-supabase/",
reactPlatform: "Vite",
uiFramework: "Ant Design",
dataProvider: "Supabase",
authProvider: "Supabase",
description: `
This example presents the RefineWeek series - a seven-part quickfire guide to help developers learn the ins and outs of Refine and Supabase's powerful capabilities and get going with Refine within a week.
The source code for this CRUD app is open-source, providing an opportunity for you to use or examine it to gain a deeper understanding of how Refine operates. The CRM application featured in this series utilizes a Supabase API through Refine's Supabase data provider, and its user interface is crafted using Ant Design.
This CRM application consumes a Supabase API through Refine's Supabase data provider, and its user interface is developed using Ant Design, which Refine offers built-in UI framework support.
We built this template to demonstrate how the Refine framework simplifies and speeds up development. It is a valuable resource, offering insights into Refine's best practices and modern development techniques.
### Key Features:
- **Realtime Collaboration Features**: Implements real-time updates using Supabase's Realtime for collaborative canvas drawing.
- **Role-Based Authorization**: Incorporates user role-based authorization with different permissions for editors and admins.
- **Audit Logging**: Implements an audit log for tracking pixel drawing activities in both the client and admin apps.
`,
},
{
slug: "react-pdf-invoice-generator",
title: "Invoice Generator - Internal Tool",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-invoice-generator.jpg",
],
runOnYourLocalPath: "refine-week-invoice-generator",
liveDemo: "https://invoice-generator.refine.dev/",
github: "https://github.com/refinedev/refine/tree/master/examples/refine-week-invoice-generator",
tutorial: "https://refine.dev/week-of-refine-strapi/",
reactPlatform: "Vite",
uiFramework: "Ant Design",
dataProvider: "Strapi",
authProvider: "Strapi",
description: `
The Invoicer is a template for an internal tool created as part of the #RefineWeek series, integrating Refine, Strapi, and Ant Design technologies. It's designed for users to manage company registrations, client and contact additions, task creations, and invoice issuance. A standout feature of this app is its ability to generate PDF invoices, enabling the creation of professional documents.
With its PDF generation feature, this open-source internal tool template is ideal for understanding Refine's capabilities. The app uses Strapi for data management, connecting through a Strapi data provider. Its interface is crafted using Ant Design, supported natively by Refine for a seamless UI experience.
We built this template to showcase the efficiency and ease of using the Refine framework in development along with third-party API management tools. It is a valuable resource, offering insights into Refine's best practices and modern development techniques. Being production-ready, this template can serve as a solid base for developing your own React-based internal tools, or it can be used as-is for immediate implementation.
### Key Features
- **Comprehensive CRUD Operations**: The app supports CRUD functionalities for companies, clients, contacts, missions, and invoices.
- **Authentication and Authorization**: Implements secure user authentication and role-based permissions using Refine's authProvider.
- **PDF Invoice Generation**: Includes the capability to generate and view PDF documents of invoices using the @react-pdf/renderer package.
`,
},
{
slug: "win-95-style-admin-panel",
title: "Win95 Style Admin Panel",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-win95.jpg",
],
runOnYourLocalPath: "blog-win95",
liveDemo: "https://win95.refine.dev/",
github: "https://github.com/refinedev/refine/tree/master/examples/blog-win95",
tutorial:
"https://refine.dev/blog/awesome-react-windows95-ui-with-refine/",
reactPlatform: "Vite",
uiFramework: "Headless",
dataProvider: "Supabase",
authProvider: "Supabase",
description: `
This Fun CRUD app example demonstrates how you can customize the Refine app design for specific needs. It is connected to a Supabase backend through Refines Supabase data provider and its user interface is developed using [React95](https://github.com/React95/React95).
The source code of the CRUD app is also open-source; feel free to use or inspect it to discover how Refine works along with external UI packages.
`,
},
{
slug: "react-crud-app",
title: "Realworld Example",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-realworld.jpg",
],
runOnYourLocalPath: "real-world-example",
liveDemo: "https://refine-real-world.netlify.app/",
github: "https://github.com/refinedev/real-world-example",
reactPlatform: "Vite",
uiFramework: "Headless",
dataProvider: "Rest API",
authProvider: "Custom",
description: `
This RealWorld app template displays a comprehensive full-stack application created using Refine, including features like CRUD operations, user authentication, routing, and pagination, among others.
The RealWorld example standard, often called the "Mother of All Demo Apps," is a specification for building full-stack applications that demonstrate real-world usage of various frontend and backend technologies. This standard provides consistent requirements for creating functionally equivalent applications across different technology stacks.
This RealWorld example aims to demonstrate the practical use of Refine in building standards and real-world applications, providing a more authentic and useful example than the usual "to-do list" demonstrations.
Since the source code of this RealWorld app is open-source, you have the freedom to use or explore it to gain insights into the workings of Refine and its integration with Next.js and Tailwind CSS.
### Key Features
- Authenticate users via JWT (login/register pages + logout button on settings page)
- CRUD users (sign up & settings page - no deleting required)
- CRUD Articles
- CRUD Comments on articles (no updating required)
- GET and display paginated lists of articles
- Favorite articles
- Follow other users
`,
},
{
slug: "multitenancy-strapi",
title: "Multitenancy App with Strapi",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-multitenancy-strapi.jpg",
],
runOnYourLocalPath: "multi-tenancy-strapi",
liveDemo: "https://multi-tenancy-strapi.refine.dev",
github: "https://github.com/refinedev/refine/tree/master/examples/multi-tenancy-strapi",
tutorial:
"https://refine.dev/docs/guides-concepts/multi-tenancy/#strapi",
reactPlatform: "Vite",
uiFramework: "Ant Design",
dataProvider: "Strapi",
authProvider: "Strapi",
description: `
Multitenancy is a design approach where a single software instance on a server provides services to multiple clients at the same time. This CRUD app template is an excellent example of implementing multitenant architecture in Refine applications. It connects to a Strapi API using a Strapi data provider and features a user interface developed with Ant Design, which is natively supported by Refine.
This template was developed to demonstrate how the Refine framework can simplify and expedite the development of apps with enterprise-level needs. It's a valuable resource that offers insights into Refine's best practices and modern development techniques.
The source code of this multitenancy app is open-source, allowing you to use or inspect it to see how Refine can fulfill enterprise-grade application requirements.
`,
},
{
slug: "multitenancy-appwrite",
title: "Multitenancy App with Appwrite",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-multitenancy-appwrite.jpg",
],
runOnYourLocalPath: "multi-tenancy-appwrite",
liveDemo: "https://multi-tenancy-appwrite.refine.dev",
github: "https://github.com/refinedev/refine/tree/master/examples/multi-tenancy-appwrite",
tutorial:
"https://refine.dev/docs/guides-concepts/multi-tenancy/#appwrite",
reactPlatform: "Vite",
uiFramework: "Ant Design",
dataProvider: "Appwrite",
authProvider: "Appwrite",
description: `
Multitenancy is a design approach where a single software instance on a server provides services to multiple clients at the same time. This CRUD app template is an excellent example of implementing multitenant architecture in Refine applications. It connects to a Appwrite API using a Appwrite data provider and features a user interface developed with Ant Design, which is natively supported by Refine.
This template was developed to demonstrate how the Refine framework can simplify and expedite the development of apps with enterprise-level needs. It's a valuable resource that offers insights into Refine's best practices and modern development techniques.
The source code of this multitenancy app is open-source, allowing you to use or inspect it to see how Refine can fulfill enterprise-grade application requirements.
`,
},
{
slug: "ant-design-template",
title: "Internal tool template with Ant Design ",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-ant-design-template.jpg",
],
runOnYourLocalPath: "auth-antd",
liveDemo:
"https://codesandbox.io/p/sandbox/github/refinedev/refine/tree/master/examples/auth-antd",
github: "https://github.com/refinedev/refine/tree/master/examples/auth-antd",
tutorial: "https://refine.dev/docs/tutorial/introduction/index/",
reactPlatform: "Vite",
uiFramework: "Ant Design",
dataProvider: "Rest API",
authProvider: "Custom",
description: `
Complete internal tool template built with Material UI. Features authentication and CRUD screens.
`,
},
{
slug: "material-ui-template",
title: "Generic Internal Tool Template with Material UI",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-material-ui-template.jpg",
],
runOnYourLocalPath: "auth-material-ui",
liveDemo:
"https://codesandbox.io/embed/github/refinedev/refine/tree/master/examples/auth-material-ui",
github: "https://github.com/refinedev/refine/tree/master/examples/auth-material-ui",
tutorial: "https://refine.dev/docs/tutorial/introduction/index/",
reactPlatform: "Vite",
uiFramework: "Material UI",
dataProvider: "Rest API",
authProvider: "Custom",
description: `
Complete internal tool template built with Material UI. Features authentication and CRUD screens.
`,
},
{
slug: "mantine-template",
title: "Generic Internal Tool Template with Mantine",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-mantine-template.jpg",
],
runOnYourLocalPath: "auth-mantine",
liveDemo:
"https://codesandbox.io/embed/github/refinedev/refine/tree/master/examples/auth-mantine",
github: "https://github.com/refinedev/refine/tree/master/examples/auth-mantine",
tutorial: "https://refine.dev/docs/tutorial/introduction/index/",
reactPlatform: "Vite",
uiFramework: "Mantine",
dataProvider: "Rest API",
authProvider: "Custom",
description: `
Complete internal tool template built with Mantine. Features authentication and CRUD screens.
`,
},
{
slug: "chakra-ui-template",
title: "Generic Internal Tool Template with Chakra UI",
images: [
"https://refine.ams3.cdn.digitaloceanspaces.com/templates/detail-chakra-ui-template.jpg",
],
runOnYourLocalPath: "auth-chakra-ui",
liveDemo:
"https://codesandbox.io/embed/github/refinedev/refine/tree/master/examples/auth-chakra-ui",
github: "https://github.com/refinedev/refine/tree/master/examples/auth-chakra-ui",
tutorial: "https://refine.dev/docs/tutorial/introduction/index/",
reactPlatform: "Vite",
uiFramework: "Chakra UI",
dataProvider: "Rest API",
authProvider: "Custom",
description: `
Complete admin panel template built with Chakra UI. Features authentication and CRUD screens.
`,
},
];