Couple of types clean up

This commit is contained in:
Joshen Lim committed 2026-10-01 14:31:30 +08:00
1 parent 51013d7fab
commit bc4b9ecd92
7 files changed
+30 -23

No files matched your search

@@ -387,6 +387,8 @@ export const CreateWrapperSheet = ({
<FormSection className="p-5!">
<FormSectionContent loading={false}>
<ForeignTablesSelector
// getWrapperCreationFormSchema's option fields are dynamic (index
// signature), which defeats RHF's field-array type inference.
tables={tablesField as unknown as FormattedWrapperTable[]}
wrapperTables={wrapperMeta.tables}
errorMessage={errors.tables?.message?.toString()}
@@ -245,6 +245,8 @@ export const EditWrapperSheet = ({
<FormSection>
<FormSectionContent className="flex flex-col space-y-2" loading={false}>
<ForeignTablesSelector
// getEditionFormSchema's option fields are dynamic (index signature),
// which defeats RHF's field-array type inference.
tables={tablesField as unknown as FormattedWrapperTable[]}
wrapperTables={wrapperMeta.tables}
errorMessage={errors.tables?.message?.toString()}
@@ -80,19 +80,12 @@ export const ForeignTablesSelector = ({
setSelectedTableFieldIndex(tableIndex)
setSelectedTableToEdit(table)
}}
tooltip={{
content: { side: 'bottom', text: `Edit ${table.table_name} foreign table` },
}}
tooltip={{ content: { side: 'bottom', text: 'Edit foreign table' } }}
/>
<ButtonTooltip
icon={<Trash />}
onClick={() => onRemove(tableIndex)}
tooltip={{
content: {
side: 'bottom',
text: `Remove ${table.table_name} foreign table`,
},
}}
tooltip={{ content: { side: 'bottom', text: 'Remove foreign table' } }}
/>
</div>
</div>
@@ -63,10 +63,15 @@ export const WrapperRow = ({ wrapper }: WrapperRowProps) => {
<TableCell className="space-y-2 p-4!">
{_tables?.map((table) => {
const target = table.table ?? table.object ?? table.src_key ?? table.table_name
const target =
(table.table as string | undefined) ??
(table.object as string | undefined) ??
(table.src_key as string | undefined) ??
table.table_name
const tableId = table.id as string
return (
<div key={table.id} className="flex items-center">
<div key={tableId} className="flex items-center">
<Badge className="bg-surface-300 gap-2 font-mono text-xs tracking-tight h-6 text-foreground rounded-r-none">
<div className="relative w-3 h-3 flex items-center justify-center">
{integration.icon({ className: 'p-0' })}
@@ -83,7 +88,7 @@ export const WrapperRow = ({ wrapper }: WrapperRowProps) => {
<ChevronRight size={12} strokeWidth={1.5} className="text-foreground-lighter/50" />
</Badge>
<Link href={`/project/${ref}/editor/${table.id}`}>
<Link href={`/project/${ref}/editor/${tableId}`}>
<Badge className="transition hover:bg-surface-300 px-2 rounded-l-none gap-1.5 h-6 font-mono text-xs tracking-tight border-l-0">
<Table2 size={12} strokeWidth={1.5} className="text-foreground-lighter/50" />
<Tooltip>
@@ -20,7 +20,7 @@ export const getWrapperCreationFormSchema = (wrapperMeta: WrapperMeta) => {
let wrapperSchema = {
// Common validation for all wrappers
server_name: z.string().min(1, 'Please provide a name for your server'),
} as Record<string, any>
} as Record<string, z.ZodTypeAny>
// Add wrapper specific options
wrapperMeta.server.options.forEach((option) => {
@@ -57,7 +57,7 @@ export const getEditionFormSchema = (wrapperMeta: WrapperMeta) => {
tables: z
.array(tableSchema, { required_error: 'Please provide at least one table' })
.min(1, 'Please provide at least one table'),
} as Record<string, any>
} as Record<string, z.ZodTypeAny>
// Add wrapper specific options
wrapperMeta.server.options.forEach((option) => {
@@ -81,7 +81,7 @@ export const getTableFormSchema = (table: Table) => {
type: z.string().min(1, 'Required'),
})
),
} as Record<string, any>
} as Record<string, z.ZodTypeAny>
table.options.forEach((option) => {
if (option.required) {
@@ -108,6 +108,10 @@ export const getTableFormSchema = (table: Table) => {
)
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null
}
export const makeValidateRequired = (options: { name: string; required: boolean }[]) => {
const requiredOptionsSet = new Set(
options.filter((option) => option.required).map((option) => option.name)
@@ -118,12 +122,12 @@ export const makeValidateRequired = (options: { name: string; required: boolean
)
const requiredArrayOptions = Array.from(requiredArrayOptionsSet)
return (values: Record<string, any>) => {
return (values: Record<string, unknown>) => {
const errors = Object.fromEntries(
Object.entries(values)
.flatMap(([key, value]) =>
.flatMap(([key, value]): [string, unknown][] =>
Array.isArray(value)
? [[key, value], ...value.map((v, i) => [`${key}.${i}`, v])]
? [[key, value], ...value.map((v, i): [string, unknown] => [`${key}.${i}`, v])]
: [[key, value]]
)
.filter(([_key, value]) => {
@@ -132,6 +136,7 @@ export const makeValidateRequired = (options: { name: string; required: boolean
if (
idx !== undefined &&
requiredOptionsSet.has(key) &&
isRecord(value) &&
Object.keys(value).some((subKey) => requiredArrayOptionsSet.has(`${key}.${subKey}`))
) {
const arrayOption = requiredArrayOptions.find((option) => option.startsWith(`${key}.`))
@@ -166,7 +171,7 @@ export interface FormattedWrapperTable {
schema_name: string
table_name: string
object?: string // From options object for Firebase/Stripe
[key: string]: any // For other dynamic options from table.options
[key: string]: unknown // For other dynamic options from table.options
}
export const formatWrapperTables = (
@@ -137,13 +137,13 @@ export const NamespaceWithTables = ({
const missingTables = useMemo(() => {
return (tablesData || []).filter(
(t) => !tables.find((table) => table.table.split('.')[1] === t)
(t) => !tables.find((table) => (table.table as string).split('.')[1] === t)
)
}, [tablesData, tables])
// Get all tables (connected + missing) for display
const allTables = useMemo(() => {
const connectedTableNames = tables.map((table) => table.table.split('.')[1])
const connectedTableNames = tables.map((table) => (table.table as string).split('.')[1])
const allTableNames = [...new Set([...connectedTableNames, ...missingTables])]
return allTableNames.map((tableName) => ({
@@ -121,11 +121,11 @@ export const AnalyticBucketDetails = () => {
const isPollingForData = pollIntervalNamespaces > 0 || pollIntervalNamespaceTables > 0
const namespaces = useMemo(() => {
const fdwNamespaces = wrapperTables.map((t) => t.table.split('.')[0]) as string[]
const fdwNamespaces = wrapperTables.map((t) => (t.table as string).split('.')[0])
const namespaces = uniq([...fdwNamespaces, ...(namespacesData ?? [])])
return namespaces.map((namespace) => {
const tables = wrapperTables.filter((t) => t.table.split('.')[0] === namespace)
const tables = wrapperTables.filter((t) => (t.table as string).split('.')[0] === namespace)
const schema = tables[0]?.schema
return {