feat(build): migrate from Prettier to Oxfmt for performant code formatting (#42434)

This commit is contained in:
Đỗ Trọng Hải
2026-08-04 00:27:05 +07:00
committed by GitHub
parent 10f7927603
commit e4ef84ca72
2328 changed files with 90728 additions and 32682 deletions
+4 -2
View File
@@ -39,7 +39,9 @@ interface FAQSchemaProps {
* { question: "How do I install it?", answer: "You can install via..." }
* ]} />
*/
export default function FAQSchema({ faqs }: FAQSchemaProps): JSX.Element | null {
export default function FAQSchema({
faqs,
}: FAQSchemaProps): JSX.Element | null {
// FAQPage schema requires a non-empty mainEntity array per schema.org specs
if (!faqs || faqs.length === 0) {
return null;
@@ -48,7 +50,7 @@ export default function FAQSchema({ faqs }: FAQSchemaProps): JSX.Element | null
const schema = {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: faqs.map((faq) => ({
mainEntity: faqs.map(faq => ({
'@type': 'Question',
name: faq.question,
acceptedAnswer: {
@@ -64,9 +64,7 @@ const Root = styled.div<{ $variant: 'hero' | 'navbar' }>`
text-decoration: none;
min-width: 0;
${({ $variant }) =>
$variant === 'hero'
? `padding: 10px 10px;`
: `padding: 7px 8px;`}
$variant === 'hero' ? `padding: 10px 10px;` : `padding: 7px 8px;`}
}
.split-main:hover {
@@ -79,9 +77,7 @@ const Root = styled.div<{ $variant: 'hero' | 'navbar' }>`
align-self: stretch;
background: rgba(255, 255, 255, 0.38);
${({ $variant }) =>
$variant === 'hero'
? `margin: 8px 0;`
: `margin: 6px 0;`}
$variant === 'hero' ? `margin: 8px 0;` : `margin: 6px 0;`}
}
.split-dropdown-trigger {
+96 -21
View File
@@ -72,7 +72,7 @@ function getProviders() {
// Configure Ant Design to render portals (tooltips, dropdowns, etc.)
// inside the closest .storybook-example container instead of document.body
// This fixes positioning issues in the docs pages
const getPopupContainer = (triggerNode) => {
const getPopupContainer = triggerNode => {
// Find the closest .storybook-example container
const container = triggerNode?.closest?.('.storybook-example');
return container || document.body;
@@ -190,7 +190,11 @@ const CHILDREN_PROP_NAMES = ['label', 'children', 'text', 'content'];
// Extract children from props based on common conventions
function extractChildren(props) {
for (const propName of CHILDREN_PROP_NAMES) {
if (props[propName] !== undefined && props[propName] !== null && props[propName] !== '') {
if (
props[propName] !== undefined &&
props[propName] !== null &&
props[propName] !== ''
) {
const { [propName]: childContent, ...restProps } = props;
return { children: childContent, restProps };
}
@@ -220,7 +224,11 @@ function generateSampleChildren(sampleChildren, sampleChildrenStyle) {
return <ChildComponent key={i} {...item.props} />;
}
// Fallback if component not found
return <div key={i}>{item.props?.children || `Unknown: ${item.component}`}</div>;
return (
<div key={i}>
{item.props?.children || `Unknown: ${item.component}`}
</div>
);
}
// Simple string
return (
@@ -252,7 +260,16 @@ function generateSampleChildren(sampleChildren, sampleChildrenStyle) {
// renderComponent allows overriding which component to actually render (useful when the named
// component is a namespace object like Icons, not a React component)
// triggerProp: for components like Modal that need a trigger, specify the boolean prop that controls visibility
function StoryWithControlsInner({ component, renderComponent, props, controls, sampleChildren, sampleChildrenStyle, triggerProp, onHideProp }) {
function StoryWithControlsInner({
component,
renderComponent,
props,
controls,
sampleChildren,
sampleChildrenStyle,
triggerProp,
onHideProp,
}) {
// Use renderComponent if provided, otherwise use the main component name
const componentToRender = renderComponent || component;
const Component = resolveComponent(componentToRender);
@@ -274,7 +291,7 @@ function StoryWithControlsInner({ component, renderComponent, props, controls, s
: extractChildren(stateProps);
// Filter out undefined values so they don't override component defaults
const filteredProps = Object.fromEntries(
Object.entries(restProps).filter(([, v]) => v !== undefined)
Object.entries(restProps).filter(([, v]) => v !== undefined),
);
// Resolve any prop values that are component descriptors
@@ -283,7 +300,12 @@ function StoryWithControlsInner({ component, renderComponent, props, controls, s
// e.g., items: [{ id: 'x', element: { component: 'div', props: { children: 'text' } } }]
Object.keys(filteredProps).forEach(key => {
const value = filteredProps[key];
if (value && typeof value === 'object' && !Array.isArray(value) && value.component) {
if (
value &&
typeof value === 'object' &&
!Array.isArray(value) &&
value.component
) {
const PropComponent = resolveComponent(value.component);
if (PropComponent) {
filteredProps[key] = <PropComponent {...value.props} />;
@@ -295,10 +317,18 @@ function StoryWithControlsInner({ component, renderComponent, props, controls, s
const resolved = { ...item };
Object.keys(resolved).forEach(field => {
const fieldValue = resolved[field];
if (fieldValue && typeof fieldValue === 'object' && !Array.isArray(fieldValue) && fieldValue.component) {
if (
fieldValue &&
typeof fieldValue === 'object' &&
!Array.isArray(fieldValue) &&
fieldValue.component
) {
const FieldComponent = resolveComponent(fieldValue.component);
if (FieldComponent) {
resolved[field] = React.createElement(FieldComponent, { key: `${key}-${idx}`, ...fieldValue.props });
resolved[field] = React.createElement(FieldComponent, {
key: `${key}-${idx}`,
...fieldValue.props,
});
}
}
});
@@ -312,14 +342,16 @@ function StoryWithControlsInner({ component, renderComponent, props, controls, s
// For List-like components with dataSource but no renderItem, provide a default
if (filteredProps.dataSource && !filteredProps.renderItem) {
const ListItem = resolveComponent('List')?.Item;
filteredProps.renderItem = (item) =>
filteredProps.renderItem = item =>
ListItem
? React.createElement(ListItem, null, String(item))
: React.createElement('div', null, String(item));
}
// Use sample children if provided, otherwise use props children
const children = generateSampleChildren(sampleChildren, sampleChildrenStyle) || propsChildren;
const children =
generateSampleChildren(sampleChildren, sampleChildrenStyle) ||
propsChildren;
// For components with a trigger (like Modal with show/onHide), add handlers.
// onHideProp supports comma-separated names for components with multiple close
@@ -356,7 +388,9 @@ function StoryWithControlsInner({ component, renderComponent, props, controls, s
Open {component}
</ButtonComponent>
)}
<Component {...filteredProps} {...triggerProps}>{children}</Component>
<Component {...filteredProps} {...triggerProps}>
{children}
</Component>
</>
) : (
<div style={{ color: '#999' }}>
@@ -384,7 +418,9 @@ function StoryWithControlsInner({ component, renderComponent, props, controls, s
{control.type === 'select' ? (
<select
value={stateProps[control.name] ?? ''}
onChange={e => updateProp(control.name, e.target.value || undefined)}
onChange={e =>
updateProp(control.name, e.target.value || undefined)
}
style={{ width: '100%', padding: '5px' }}
>
<option value=""> None </option>
@@ -394,19 +430,28 @@ function StoryWithControlsInner({ component, renderComponent, props, controls, s
</option>
))}
</select>
) : control.type === 'inline-radio' || control.type === 'radio' ? (
<div style={{ display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
) : control.type === 'inline-radio' ||
control.type === 'radio' ? (
<div
style={{ display: 'flex', gap: '10px', flexWrap: 'wrap' }}
>
{control.options?.map(option => (
<label
key={option}
style={{ display: 'flex', alignItems: 'center', gap: '4px' }}
style={{
display: 'flex',
alignItems: 'center',
gap: '4px',
}}
>
<input
type="radio"
name={control.name}
value={option}
checked={stateProps[control.name] === option}
onChange={e => updateProp(control.name, e.target.value)}
onChange={e =>
updateProp(control.name, e.target.value)
}
/>
{option}
</label>
@@ -422,7 +467,9 @@ function StoryWithControlsInner({ component, renderComponent, props, controls, s
<input
type="number"
value={stateProps[control.name]}
onChange={e => updateProp(control.name, Number(e.target.value))}
onChange={e =>
updateProp(control.name, Number(e.target.value))
}
style={{ width: '100%', padding: '5px' }}
/>
) : control.type === 'color' ? (
@@ -457,7 +504,16 @@ function StoryWithControlsInner({ component, renderComponent, props, controls, s
// A simple component to display a story with controls
// renderComponent: optional override for which component to render (e.g., 'Icons.InfoCircleOutlined' when component='Icons')
// triggerProp/onHideProp: for components like Modal that need a button to open (e.g., triggerProp="show", onHideProp="onHide")
export function StoryWithControls({ component: Component, renderComponent, props = {}, controls = [], sampleChildren, sampleChildrenStyle, triggerProp, onHideProp }) {
export function StoryWithControls({
component: Component,
renderComponent,
props = {},
controls = [],
sampleChildren,
sampleChildrenStyle,
triggerProp,
onHideProp,
}) {
return (
<BrowserOnly fallback={<LoadingPlaceholder />}>
{() => (
@@ -477,7 +533,13 @@ export function StoryWithControls({ component: Component, renderComponent, props
}
// Inner component for ComponentGallery (browser-only)
function ComponentGalleryInner({ component, sizes, styles, sizeProp, styleProp }) {
function ComponentGalleryInner({
component,
sizes,
styles,
sizeProp,
styleProp,
}) {
const Component = resolveComponent(component);
const Providers = getProviders();
@@ -495,7 +557,14 @@ function ComponentGalleryInner({ component, sizes, styles, sizeProp, styleProp }
{sizes.map(size => (
<div key={size} style={{ marginBottom: 40 }}>
<h4 style={{ marginBottom: 16, color: '#666' }}>{size}</h4>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '12px', alignItems: 'center' }}>
<div
style={{
display: 'flex',
flexWrap: 'wrap',
gap: '12px',
alignItems: 'center',
}}
>
{styles.map(style => (
<Component
key={`${style}_${size}`}
@@ -513,7 +582,13 @@ function ComponentGalleryInner({ component, sizes, styles, sizeProp, styleProp }
}
// A component to display a gallery of all variants (sizes x styles)
export function ComponentGallery({ component, sizes = [], styles = [], sizeProp = 'size', styleProp = 'variant' }) {
export function ComponentGallery({
component,
sizes = [],
styles = [],
sizeProp = 'size',
styleProp = 'variant',
}) {
return (
<BrowserOnly fallback={<LoadingPlaceholder />}>
{() => (
+83 -44
View File
@@ -18,7 +18,17 @@
*/
import React, { useState, useMemo } from 'react';
import { Card, Row, Col, Statistic, Table, Tag, Input, Select, Tooltip } from 'antd';
import {
Card,
Row,
Col,
Statistic,
Table,
Tag,
Input,
Select,
Tooltip,
} from 'antd';
import {
DatabaseOutlined,
CheckCircleOutlined,
@@ -37,7 +47,7 @@ interface DatabaseIndexProps {
// Type for table entries (includes both regular DBs and compatible DBs)
interface TableEntry {
name: string;
categories: string[]; // Multiple categories supported
categories: string[]; // Multiple categories supported
score: number;
max_score: number;
timeGrainCount: number;
@@ -66,20 +76,20 @@ interface TableEntry {
// Map category constant names to display names
const CATEGORY_DISPLAY_NAMES: Record<string, string> = {
'CLOUD_AWS': 'Cloud - AWS',
'CLOUD_GCP': 'Cloud - Google',
'CLOUD_AZURE': 'Cloud - Azure',
'CLOUD_DATA_WAREHOUSES': 'Cloud Data Warehouses',
'APACHE_PROJECTS': 'Apache Projects',
'TRADITIONAL_RDBMS': 'Traditional RDBMS',
'ANALYTICAL_DATABASES': 'Analytical Databases',
'SEARCH_NOSQL': 'Search & NoSQL',
'QUERY_ENGINES': 'Query Engines',
'TIME_SERIES': 'Time Series Databases',
'OTHER': 'Other Databases',
'OPEN_SOURCE': 'Open Source',
'HOSTED_OPEN_SOURCE': 'Hosted Open Source',
'PROPRIETARY': 'Proprietary',
CLOUD_AWS: 'Cloud - AWS',
CLOUD_GCP: 'Cloud - Google',
CLOUD_AZURE: 'Cloud - Azure',
CLOUD_DATA_WAREHOUSES: 'Cloud Data Warehouses',
APACHE_PROJECTS: 'Apache Projects',
TRADITIONAL_RDBMS: 'Traditional RDBMS',
ANALYTICAL_DATABASES: 'Analytical Databases',
SEARCH_NOSQL: 'Search & NoSQL',
QUERY_ENGINES: 'Query Engines',
TIME_SERIES: 'Time Series Databases',
OTHER: 'Other Databases',
OPEN_SOURCE: 'Open Source',
HOSTED_OPEN_SOURCE: 'Hosted Open Source',
PROPRIETARY: 'Proprietary',
};
// Category colors for visual distinction
@@ -98,7 +108,7 @@ const CATEGORY_COLORS: Record<string, string> = {
// Licensing categories
'Open Source': 'geekblue',
'Hosted Open Source': 'cyan',
'Proprietary': 'default',
Proprietary: 'default',
};
// Convert category constant to display name
@@ -110,7 +120,7 @@ function getCategoryDisplayName(cat: string): string {
// Falls back to name-based inference for compatible databases without categories
function getCategories(
name: string,
documentationCategories?: string[]
documentationCategories?: string[],
): string[] {
// Prefer categories from documentation metadata (computed by Python)
if (documentationCategories && documentationCategories.length > 0) {
@@ -221,10 +231,11 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ data }) => {
categories: getCategories(name, db.documentation?.categories),
timeGrainCount: countTimeGrains(db),
hasDrivers: (db.documentation?.drivers?.length ?? 0) > 0,
hasAuthMethods: (db.documentation?.authentication_methods?.length ?? 0) > 0,
hasAuthMethods:
(db.documentation?.authentication_methods?.length ?? 0) > 0,
hasConnectionString: Boolean(
db.documentation?.connection_string ||
(db.documentation?.drivers?.length ?? 0) > 0
(db.documentation?.drivers?.length ?? 0) > 0,
),
hasCustomErrors: (db.documentation?.custom_errors?.length ?? 0) > 0,
customErrorCount: db.documentation?.custom_errors?.length ?? 0,
@@ -233,10 +244,10 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ data }) => {
// Add compatible databases from this database's documentation
const compatibleDbs = db.documentation?.compatible_databases ?? [];
compatibleDbs.forEach((compat) => {
compatibleDbs.forEach(compat => {
// Check if this compatible DB already exists as a main entry
const existsAsMain = Object.keys(databases).some(
(dbName) => dbName.toLowerCase() === compat.name.toLowerCase()
dbName => dbName.toLowerCase() === compat.name.toLowerCase(),
);
if (!existsAsMain) {
@@ -277,14 +288,15 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ data }) => {
// Filter and sort databases
const filteredDatabases = useMemo(() => {
return databaseList
.filter((db) => {
.filter(db => {
const matchesSearch =
!searchText ||
db.name.toLowerCase().includes(searchText.toLowerCase()) ||
db.documentation?.description
?.toLowerCase()
.includes(searchText.toLowerCase());
const matchesCategory = !categoryFilter || db.categories.includes(categoryFilter);
const matchesCategory =
!categoryFilter || db.categories.includes(categoryFilter);
return matchesSearch && matchesCategory;
})
.sort((a, b) => b.score - a.score);
@@ -293,9 +305,9 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ data }) => {
// Get unique categories and counts for filter
const { categories, categoryCounts } = useMemo(() => {
const counts: Record<string, number> = {};
databaseList.forEach((db) => {
databaseList.forEach(db => {
// Count each category the database belongs to
db.categories.forEach((cat) => {
db.categories.forEach(cat => {
counts[cat] = (counts[cat] || 0) + 1;
});
});
@@ -314,12 +326,17 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ data }) => {
sorter: (a: TableEntry, b: TableEntry) => a.name.localeCompare(b.name),
render: (name: string, record: TableEntry) => {
// Convert name to URL slug
const toSlug = (n: string) => n.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
const toSlug = (n: string) =>
n
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-|-$/g, '');
// Link to parent for compatible DBs, otherwise to own page
const linkTarget = record.isCompatible && record.compatibleWith
? `/docs/databases/supported/${toSlug(record.compatibleWith)}`
: `/docs/databases/supported/${toSlug(name)}`;
const linkTarget =
record.isCompatible && record.compatibleWith
? `/docs/databases/supported/${toSlug(record.compatibleWith)}`
: `/docs/databases/supported/${toSlug(name)}`;
return (
<div>
@@ -337,7 +354,9 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ data }) => {
)}
<div style={{ fontSize: '12px', color: '#666' }}>
{record.documentation?.description?.slice(0, 80)}
{(record.documentation?.description?.length ?? 0) > 80 ? '...' : ''}
{(record.documentation?.description?.length ?? 0) > 80
? '...'
: ''}
</div>
</div>
);
@@ -348,13 +367,15 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ data }) => {
dataIndex: 'categories',
key: 'categories',
width: 220,
filters: categories.map((cat) => ({ text: cat, value: cat })),
filters: categories.map(cat => ({ text: cat, value: cat })),
onFilter: (value: React.Key | boolean, record: TableEntry) =>
record.categories.includes(value as string),
render: (cats: string[]) => (
<div style={{ display: 'flex', gap: '4px', flexWrap: 'wrap' }}>
{cats.map((cat) => (
<Tag key={cat} color={CATEGORY_COLORS[cat] || 'default'}>{cat}</Tag>
{cats.map(cat => (
<Tag key={cat} color={CATEGORY_COLORS[cat] || 'default'}>
{cat}
</Tag>
))}
</div>
),
@@ -382,16 +403,26 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ data }) => {
dataIndex: 'timeGrainCount',
key: 'timeGrainCount',
width: 100,
sorter: (a: TableEntry, b: TableEntry) => a.timeGrainCount - b.timeGrainCount,
sorter: (a: TableEntry, b: TableEntry) =>
a.timeGrainCount - b.timeGrainCount,
render: (count: number, record: TableEntry) => {
if (count === 0) return <span>-</span>;
const grains = getSupportedTimeGrains(record.time_grains);
return (
<Tooltip
title={
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '4px', maxWidth: 280 }}>
{grains.map((grain) => (
<Tag key={grain} style={{ margin: 0 }}>{grain}</Tag>
<div
style={{
display: 'flex',
flexWrap: 'wrap',
gap: '4px',
maxWidth: 280,
}}
>
{grains.map(grain => (
<Tag key={grain} style={{ margin: 0 }}>
{grain}
</Tag>
))}
</div>
}
@@ -450,13 +481,17 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ data }) => {
<div style={{ display: 'flex', gap: '4px', flexWrap: 'wrap' }}>
{record.joins && <Tag color="green">JOINs</Tag>}
{record.subqueries && <Tag color="green">Subqueries</Tag>}
{record.supports_dynamic_schema && <Tag color="blue">Dynamic Schema</Tag>}
{record.supports_dynamic_schema && (
<Tag color="blue">Dynamic Schema</Tag>
)}
{record.supports_catalog && <Tag color="purple">Catalog</Tag>}
{record.ssh_tunneling && <Tag color="cyan">SSH</Tag>}
{record.supports_file_upload && <Tag color="orange">File Upload</Tag>}
{record.query_cancelation && <Tag color="volcano">Query Cancel</Tag>}
{record.query_cost_estimation && <Tag color="gold">Cost Est.</Tag>}
{record.user_impersonation && <Tag color="magenta">Impersonation</Tag>}
{record.user_impersonation && (
<Tag color="magenta">Impersonation</Tag>
)}
{record.sql_validation && <Tag color="lime">SQL Validation</Tag>}
</div>
),
@@ -545,7 +580,7 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ data }) => {
placeholder="Search databases..."
prefix={<SearchOutlined />}
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
onChange={e => setSearchText(e.target.value)}
allowClear
/>
</Col>
@@ -556,7 +591,7 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ data }) => {
value={categoryFilter}
onChange={setCategoryFilter}
allowClear
options={categories.map((cat) => ({
options={categories.map(cat => ({
label: (
<span>
<Tag
@@ -578,11 +613,15 @@ const DatabaseIndex: React.FC<DatabaseIndexProps> = ({ data }) => {
<Table
dataSource={filteredDatabases}
columns={columns}
rowKey={(record) => record.isCompatible ? `${record.compatibleWith}-${record.name}` : record.name}
rowKey={record =>
record.isCompatible
? `${record.compatibleWith}-${record.name}`
: record.name
}
pagination={{
defaultPageSize: 20,
showSizeChanger: true,
showTotal: (total) => `${total} databases`,
showTotal: total => `${total} databases`,
}}
size="middle"
/>
@@ -35,7 +35,10 @@ const databases = Object.entries(typedData.databases)
.map(([name, db]) => ({
name,
logo: db.documentation.logo!,
docPath: `/user-docs/databases/supported/${name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')}`,
docPath: `/user-docs/databases/supported/${name
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-|-$/g, '')}`,
}));
export default function DatabaseLogoWall(): React.JSX.Element {
+12 -12
View File
@@ -77,7 +77,7 @@ export interface CompatibleDatabase {
description?: string;
logo?: string;
homepage_url?: string;
categories?: string[]; // Category classifications (e.g., ["TRADITIONAL_RDBMS", "OPEN_SOURCE"])
categories?: string[]; // Category classifications (e.g., ["TRADITIONAL_RDBMS", "OPEN_SOURCE"])
pypi_packages?: string[];
connection_string?: string;
parameters?: Record<string, string>;
@@ -87,27 +87,27 @@ export interface CompatibleDatabase {
}
export interface CustomError {
error_type: string; // e.g., "CONNECTION_INVALID_USERNAME_ERROR"
message_template: string; // e.g., 'The username "%(username)s" does not exist.'
regex_pattern?: string; // The regex pattern that matches this error (optional, for reference)
regex_name?: string; // The name of the regex constant (e.g., "CONNECTION_INVALID_USERNAME_REGEX")
invalid_fields?: string[]; // Fields that are invalid, e.g., ["username", "password"]
issue_codes?: number[]; // Related issue codes from ISSUE_CODES mapping
category?: string; // Error category: "Authentication", "Connection", "Query", etc.
description?: string; // Human-readable short description of the error type
error_type: string; // e.g., "CONNECTION_INVALID_USERNAME_ERROR"
message_template: string; // e.g., 'The username "%(username)s" does not exist.'
regex_pattern?: string; // The regex pattern that matches this error (optional, for reference)
regex_name?: string; // The name of the regex constant (e.g., "CONNECTION_INVALID_USERNAME_REGEX")
invalid_fields?: string[]; // Fields that are invalid, e.g., ["username", "password"]
issue_codes?: number[]; // Related issue codes from ISSUE_CODES mapping
category?: string; // Error category: "Authentication", "Connection", "Query", etc.
description?: string; // Human-readable short description of the error type
}
export interface DatabaseDocumentation {
description?: string;
logo?: string;
homepage_url?: string;
categories?: string[]; // Category classifications (e.g., ["TRADITIONAL_RDBMS", "OPEN_SOURCE"])
categories?: string[]; // Category classifications (e.g., ["TRADITIONAL_RDBMS", "OPEN_SOURCE"])
pypi_packages?: string[];
connection_string?: string;
default_port?: number;
parameters?: Record<string, string>;
notes?: string;
limitations?: string[]; // Known limitations or caveats
limitations?: string[]; // Known limitations or caveats
connection_examples?: ConnectionExample[];
host_examples?: HostExample[];
drivers?: Driver[];
@@ -122,7 +122,7 @@ export interface DatabaseDocumentation {
sqlalchemy_docs_url?: string;
advanced_features?: Record<string, string>;
compatible_databases?: CompatibleDatabase[];
custom_errors?: CustomError[]; // Database-specific error messages and troubleshooting info
custom_errors?: CustomError[]; // Database-specific error messages and troubleshooting info
}
export interface TimeGrains {
@@ -52,7 +52,7 @@ const ComponentIndex: React.FC<ComponentIndexProps> = ({ data }) => {
const filteredComponents = useMemo(() => {
return components
.filter((comp) => {
.filter(comp => {
const matchesSearch =
!searchText ||
comp.name.toLowerCase().includes(searchText.toLowerCase()) ||
@@ -67,7 +67,7 @@ const ComponentIndex: React.FC<ComponentIndexProps> = ({ data }) => {
const { categories, categoryCounts } = useMemo(() => {
const counts: Record<string, number> = {};
components.forEach((comp) => {
components.forEach(comp => {
counts[comp.category] = (counts[comp.category] || 0) + 1;
});
return {
@@ -103,7 +103,7 @@ const ComponentIndex: React.FC<ComponentIndexProps> = ({ data }) => {
dataIndex: 'category',
key: 'category',
width: 120,
filters: categories.map((cat) => ({
filters: categories.map(cat => ({
text: CATEGORY_LABELS[cat] || cat,
value: cat,
})),
@@ -120,9 +120,7 @@ const ComponentIndex: React.FC<ComponentIndexProps> = ({ data }) => {
dataIndex: 'package',
key: 'package',
width: 220,
render: (pkg: string) => (
<code style={{ fontSize: '12px' }}>{pkg}</code>
),
render: (pkg: string) => <code style={{ fontSize: '12px' }}>{pkg}</code>,
},
{
title: 'Tags',
@@ -215,7 +213,7 @@ const ComponentIndex: React.FC<ComponentIndexProps> = ({ data }) => {
placeholder="Search components..."
prefix={<SearchOutlined />}
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
onChange={e => setSearchText(e.target.value)}
allowClear
/>
</Col>
@@ -226,7 +224,7 @@ const ComponentIndex: React.FC<ComponentIndexProps> = ({ data }) => {
value={categoryFilter}
onChange={setCategoryFilter}
allowClear
options={categories.map((cat) => ({
options={categories.map(cat => ({
label: (
<span>
<Tag
@@ -251,7 +249,7 @@ const ComponentIndex: React.FC<ComponentIndexProps> = ({ data }) => {
pagination={{
defaultPageSize: 20,
showSizeChanger: true,
showTotal: (total) => `${total} components`,
showTotal: total => `${total} components`,
}}
size="middle"
/>