mirror of
https://github.com/teableio/teable
synced 2024-11-21 14:51:09 +00:00
feat: inject dynamic env to next (#392)
* fix: auto disable template entry * feat: inject dynamic env to next
This commit is contained in:
parent
b6e533b9fa
commit
ed056d63ce
@ -15,9 +15,9 @@ NEXT_BUILD_ENV_SENTRY_TRACING=false
|
|||||||
NEXTJS_DISABLE_SENTRY=true
|
NEXTJS_DISABLE_SENTRY=true
|
||||||
NEXTJS_SENTRY_UPLOAD_DRY_RUN=true
|
NEXTJS_SENTRY_UPLOAD_DRY_RUN=true
|
||||||
# set metrics id
|
# set metrics id
|
||||||
NEXT_PUBLIC_MICROSOFT_CLARITY=your-metrics-id
|
MICROSOFT_CLARITY_ID=your-metrics-id
|
||||||
# help site link
|
# help site link
|
||||||
NEXT_PUBLIC_HELP_SITE_LINK=https://help.teable.io
|
HELP_SITE_LINK=https://help.teable.io
|
||||||
|
|
||||||
# ↓↓↓↓↓↓↓↓ backend(nestjs) env ↓↓↓↓↓↓↓↓
|
# ↓↓↓↓↓↓↓↓ backend(nestjs) env ↓↓↓↓↓↓↓↓
|
||||||
NEXTJS_DIR=../nextjs-app
|
NEXTJS_DIR=../nextjs-app
|
||||||
@ -48,7 +48,7 @@ BACKEND_MAIL_AUTH_PASS=usertoken
|
|||||||
# The spaceId where your template base is located, it is the basic info of template center operation
|
# The spaceId where your template base is located, it is the basic info of template center operation
|
||||||
TEMPLATE_SPACE_ID=your-template-space-id
|
TEMPLATE_SPACE_ID=your-template-space-id
|
||||||
# template site link, you need to set the current value to enable create from template
|
# template site link, you need to set the current value to enable create from template
|
||||||
NEXT_PUBLIC_TEMPLATE_SITE_LINK=https://template.teable.io
|
TEMPLATE_SITE_LINK=https://template.teable.io
|
||||||
|
|
||||||
# ↓↓↓↓↓↓↓↓ limitaions, time unit is ms ↓↓↓↓↓↓↓↓
|
# ↓↓↓↓↓↓↓↓ limitaions, time unit is ms ↓↓↓↓↓↓↓↓
|
||||||
MAX_COPY_CELLS=50000
|
MAX_COPY_CELLS=50000
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
import { Toaster as SoonerToaster } from '@teable/ui-lib/shadcn/ui/sonner';
|
import { Toaster as SoonerToaster } from '@teable/ui-lib/shadcn/ui/sonner';
|
||||||
import { Toaster } from '@teable/ui-lib/shadcn/ui/toaster';
|
import { Toaster } from '@teable/ui-lib/shadcn/ui/toaster';
|
||||||
import type { FC, PropsWithChildren } from 'react';
|
import type { FC, PropsWithChildren } from 'react';
|
||||||
|
import type { IServerEnv } from './lib/server-env';
|
||||||
|
import { EnvContext } from './lib/server-env';
|
||||||
|
|
||||||
type Props = PropsWithChildren;
|
type Props = PropsWithChildren;
|
||||||
|
|
||||||
export const AppProviders: FC<Props> = (props) => {
|
export const AppProviders: FC<Props & { env: IServerEnv }> = (props) => {
|
||||||
const { children } = props;
|
const { children, env } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<EnvContext.Provider value={env}>
|
||||||
{children}
|
{children}
|
||||||
<Toaster />
|
<Toaster />
|
||||||
<SoonerToaster />
|
<SoonerToaster />
|
||||||
</>
|
</EnvContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import type { IUserMeVo } from '@teable/openapi';
|
import type { IUserMeVo } from '@teable/openapi';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
|
import { useTranslation, Trans } from 'next-i18next';
|
||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useTranslation, Trans } from 'react-i18next';
|
|
||||||
import { ACTIONS, EVENTS, STATUS } from 'react-joyride';
|
import { ACTIONS, EVENTS, STATUS } from 'react-joyride';
|
||||||
import type { CallBackProps, Step, StoreHelpers } from 'react-joyride';
|
import type { CallBackProps, Step, StoreHelpers } from 'react-joyride';
|
||||||
import colors from 'tailwindcss/colors';
|
import colors from 'tailwindcss/colors';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import Script from 'next/script';
|
import Script from 'next/script';
|
||||||
|
|
||||||
export const MicrosoftClarity = () => {
|
export const MicrosoftClarity = ({ clarityId }: { clarityId?: string }) => {
|
||||||
if (!process.env.NEXT_PUBLIC_MICROSOFT_CLARITY) {
|
if (!clarityId) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ export const MicrosoftClarity = () => {
|
|||||||
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
|
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;
|
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);
|
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
|
||||||
})(window, document, "clarity", "script", "${process.env.NEXT_PUBLIC_MICROSOFT_CLARITY}");
|
})(window, document, "clarity", "script", "${clarityId}");
|
||||||
`,
|
`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import type { DriverClient } from '@teable/core';
|
||||||
import type { ShareViewGetVo } from '@teable/openapi';
|
import type { ShareViewGetVo } from '@teable/openapi';
|
||||||
import { AnchorContext, AppProvider, FieldProvider, ViewProvider } from '@teable/sdk/context';
|
import { AnchorContext, AppProvider, FieldProvider, ViewProvider } from '@teable/sdk/context';
|
||||||
import { getWsPath } from '@teable/sdk/context/app/useConnection';
|
import { getWsPath } from '@teable/sdk/context/app/useConnection';
|
||||||
@ -12,6 +13,7 @@ import { ViewProxy } from './ViewProxy';
|
|||||||
|
|
||||||
export interface IShareViewPageProps {
|
export interface IShareViewPageProps {
|
||||||
shareViewData: ShareViewGetVo;
|
shareViewData: ShareViewGetVo;
|
||||||
|
driver: DriverClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ShareViewPage = (props: IShareViewPageProps) => {
|
export const ShareViewPage = (props: IShareViewPageProps) => {
|
||||||
@ -30,7 +32,7 @@ export const ShareViewPage = (props: IShareViewPageProps) => {
|
|||||||
return (
|
return (
|
||||||
<ShareViewPageContext.Provider value={props.shareViewData}>
|
<ShareViewPageContext.Provider value={props.shareViewData}>
|
||||||
<AppLayout>
|
<AppLayout>
|
||||||
<AppProvider wsPath={wsPath} locale={sdkLocale}>
|
<AppProvider wsPath={wsPath} locale={sdkLocale} driver={props.driver}>
|
||||||
<AnchorContext.Provider
|
<AnchorContext.Provider
|
||||||
value={{
|
value={{
|
||||||
tableId,
|
tableId,
|
||||||
|
@ -2,10 +2,10 @@ import { HelpCircle, Settings, UserPlus } from '@teable/icons';
|
|||||||
import { useBase, useTableId } from '@teable/sdk/hooks';
|
import { useBase, useTableId } from '@teable/sdk/hooks';
|
||||||
import { Button } from '@teable/ui-lib/shadcn';
|
import { Button } from '@teable/ui-lib/shadcn';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { SpaceCollaboratorModalTrigger } from '@/features/app/components/collaborator-manage/space/SpaceCollaboratorModalTrigger';
|
import { SpaceCollaboratorModalTrigger } from '@/features/app/components/collaborator-manage/space/SpaceCollaboratorModalTrigger';
|
||||||
|
import { useEnv } from '@/features/app/hooks/useEnv';
|
||||||
import { spaceConfig } from '@/features/i18n/space.config';
|
import { spaceConfig } from '@/features/i18n/space.config';
|
||||||
import { getHelpLink } from '@/lib/off-site-link';
|
|
||||||
import { ExpandViewList } from '../../view/list/ExpandViewList';
|
import { ExpandViewList } from '../../view/list/ExpandViewList';
|
||||||
import { ViewList } from '../../view/list/ViewList';
|
import { ViewList } from '../../view/list/ViewList';
|
||||||
|
|
||||||
@ -16,6 +16,7 @@ import { TableInfo } from './TableInfo';
|
|||||||
export const TableHeader: React.FC = () => {
|
export const TableHeader: React.FC = () => {
|
||||||
const base = useBase();
|
const base = useBase();
|
||||||
const tableId = useTableId();
|
const tableId = useTableId();
|
||||||
|
const { helpSiteLink } = useEnv();
|
||||||
const { t } = useTranslation(spaceConfig.i18nNamespaces);
|
const { t } = useTranslation(spaceConfig.i18nNamespaces);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -42,7 +43,7 @@ export const TableHeader: React.FC = () => {
|
|||||||
</Link>
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
<Button asChild variant="ghost" size="xs" className="hidden sm:flex">
|
<Button asChild variant="ghost" size="xs" className="hidden sm:flex">
|
||||||
<a href={getHelpLink()} title="Help" target="_blank" rel="noreferrer">
|
<a href={helpSiteLink} title="Help" target="_blank" rel="noreferrer">
|
||||||
<HelpCircle className="size-4" />
|
<HelpCircle className="size-4" />
|
||||||
</a>
|
</a>
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable @next/next/no-html-link-for-pages */
|
/* eslint-disable @next/next/no-html-link-for-pages */
|
||||||
import { TeableNew } from '@teable/icons';
|
import { TeableNew } from '@teable/icons';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { Trans, useTranslation } from 'next-i18next';
|
||||||
import { tableConfig } from '@/features/i18n/table.config';
|
import { tableConfig } from '@/features/i18n/table.config';
|
||||||
|
|
||||||
export const BrandFooter = () => {
|
export const BrandFooter = () => {
|
||||||
|
@ -17,8 +17,8 @@ import {
|
|||||||
Textarea,
|
Textarea,
|
||||||
cn,
|
cn,
|
||||||
} from '@teable/ui-lib/shadcn';
|
} from '@teable/ui-lib/shadcn';
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { FieldOperator } from '@/features/app/components/field-setting';
|
import { FieldOperator } from '@/features/app/components/field-setting';
|
||||||
import { tableConfig } from '@/features/i18n/table.config';
|
import { tableConfig } from '@/features/i18n/table.config';
|
||||||
import { useFieldSettingStore } from '../../field/useFieldSettingStore';
|
import { useFieldSettingStore } from '../../field/useFieldSettingStore';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useFieldStaticGetter, useView } from '@teable/sdk/hooks';
|
import { useFieldStaticGetter, useView } from '@teable/sdk/hooks';
|
||||||
import type { FormView, IFieldInstance } from '@teable/sdk/model';
|
import type { FormView, IFieldInstance } from '@teable/sdk/model';
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { tableConfig } from '@/features/i18n/table.config';
|
import { tableConfig } from '@/features/i18n/table.config';
|
||||||
import { FormCellEditor } from './FormCellEditor';
|
import { FormCellEditor } from './FormCellEditor';
|
||||||
|
|
||||||
|
@ -11,8 +11,8 @@ import {
|
|||||||
TooltipProvider,
|
TooltipProvider,
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from '@teable/ui-lib/shadcn';
|
} from '@teable/ui-lib/shadcn';
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { tableConfig } from '@/features/i18n/table.config';
|
import { tableConfig } from '@/features/i18n/table.config';
|
||||||
|
|
||||||
interface IFormFieldEditorProps {
|
interface IFormFieldEditorProps {
|
||||||
|
@ -4,8 +4,8 @@ import { useFields, useTableId, useView } from '@teable/sdk/hooks';
|
|||||||
import { type FormView } from '@teable/sdk/model';
|
import { type FormView } from '@teable/sdk/model';
|
||||||
import { Button, cn, useToast } from '@teable/ui-lib/shadcn';
|
import { Button, cn, useToast } from '@teable/ui-lib/shadcn';
|
||||||
import { omit } from 'lodash';
|
import { omit } from 'lodash';
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useMemo, useRef, useState } from 'react';
|
import { useMemo, useRef, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { useLocalStorage, useMap, useSet } from 'react-use';
|
import { useLocalStorage, useMap, useSet } from 'react-use';
|
||||||
import { tableConfig } from '@/features/i18n/table.config';
|
import { tableConfig } from '@/features/i18n/table.config';
|
||||||
import { generateUniqLocalKey } from '../util';
|
import { generateUniqLocalKey } from '../util';
|
||||||
|
@ -13,9 +13,9 @@ import {
|
|||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
cn,
|
cn,
|
||||||
} from '@teable/ui-lib/shadcn';
|
} from '@teable/ui-lib/shadcn';
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { FieldOperator } from '@/features/app/components/field-setting';
|
import { FieldOperator } from '@/features/app/components/field-setting';
|
||||||
import { tableConfig } from '@/features/i18n/table.config';
|
import { tableConfig } from '@/features/i18n/table.config';
|
||||||
import { useFieldSettingStore } from '../../field/useFieldSettingStore';
|
import { useFieldSettingStore } from '../../field/useFieldSettingStore';
|
||||||
|
@ -3,8 +3,8 @@ import { Plus } from '@teable/icons';
|
|||||||
import { LinkCard } from '@teable/sdk/components';
|
import { LinkCard } from '@teable/sdk/components';
|
||||||
import type { LinkField } from '@teable/sdk/model';
|
import type { LinkField } from '@teable/sdk/model';
|
||||||
import { Button, Popover, PopoverContent, PopoverTrigger } from '@teable/ui-lib/shadcn';
|
import { Button, Popover, PopoverContent, PopoverTrigger } from '@teable/ui-lib/shadcn';
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { tableConfig } from '@/features/i18n/table.config';
|
import { tableConfig } from '@/features/i18n/table.config';
|
||||||
import { LinkRecordList } from './LinkRecordList';
|
import { LinkRecordList } from './LinkRecordList';
|
||||||
|
|
||||||
|
@ -14,8 +14,8 @@ import {
|
|||||||
cn,
|
cn,
|
||||||
} from '@teable/ui-lib/shadcn';
|
} from '@teable/ui-lib/shadcn';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { useUnmount } from 'react-use';
|
import { useUnmount } from 'react-use';
|
||||||
import { tableConfig } from '@/features/i18n/table.config';
|
import { tableConfig } from '@/features/i18n/table.config';
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { ArrowUpRight, Settings as Edit, Edit as Fill } from '@teable/icons';
|
import { ArrowUpRight, Settings as Edit, Edit as Fill } from '@teable/icons';
|
||||||
import { useTableId, useTablePermission, useViewId } from '@teable/sdk/hooks';
|
import { useTableId, useTablePermission, useViewId } from '@teable/sdk/hooks';
|
||||||
import { Button } from '@teable/ui-lib/shadcn';
|
import { Button } from '@teable/ui-lib/shadcn';
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { tableConfig } from '@/features/i18n/table.config';
|
import { tableConfig } from '@/features/i18n/table.config';
|
||||||
import { generateUniqLocalKey } from '../form/util';
|
import { generateUniqLocalKey } from '../form/util';
|
||||||
import { SharePopover } from './SharePopover';
|
import { SharePopover } from './SharePopover';
|
||||||
|
@ -13,7 +13,7 @@ import { useRouter } from 'next/router';
|
|||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { spaceConfig } from '@/features/i18n/space.config';
|
import { spaceConfig } from '@/features/i18n/space.config';
|
||||||
import { getTemplateCenterLink } from '@/lib/off-site-link';
|
import { useEnv } from '../../hooks/useEnv';
|
||||||
|
|
||||||
export const CreateBaseModalTrigger = ({
|
export const CreateBaseModalTrigger = ({
|
||||||
spaceId,
|
spaceId,
|
||||||
@ -33,7 +33,7 @@ export const CreateBaseModalTrigger = ({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const templateCenterLink = getTemplateCenterLink();
|
const { templateSiteLink } = useEnv();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog>
|
<Dialog>
|
||||||
@ -53,15 +53,13 @@ export const CreateBaseModalTrigger = ({
|
|||||||
{t('space:baseModal.fromScratch')}
|
{t('space:baseModal.fromScratch')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
asChild
|
|
||||||
className="flex h-auto grow flex-col items-center gap-4"
|
className="flex h-auto grow flex-col items-center gap-4"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
disabled={!templateCenterLink}
|
disabled={!templateSiteLink}
|
||||||
|
onClick={() => (window.location.href = templateSiteLink as string)}
|
||||||
>
|
>
|
||||||
<a href={templateCenterLink}>
|
<LayoutTemplate className="size-8" />
|
||||||
<LayoutTemplate className="size-8" />
|
{t('space:baseModal.fromTemplate')}
|
||||||
{t('space:baseModal.fromTemplate')}
|
|
||||||
</a>
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
@ -4,7 +4,8 @@ import { useTranslation } from 'next-i18next';
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useLocalStorage } from 'react-use';
|
import { useLocalStorage } from 'react-use';
|
||||||
import { dashboardConfig } from '@/features/i18n/dashboard.config';
|
import { dashboardConfig } from '@/features/i18n/dashboard.config';
|
||||||
import { getHelpLink } from '@/lib/off-site-link';
|
|
||||||
|
import { useEnv } from '../hooks/useEnv';
|
||||||
import { Pickers } from './components/Pickers';
|
import { Pickers } from './components/Pickers';
|
||||||
import { GridContent } from './GridContent';
|
import { GridContent } from './GridContent';
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ export function DashboardPage() {
|
|||||||
const [anchor, setAnchor] = useState<{ tableId?: string; viewId?: string }>({});
|
const [anchor, setAnchor] = useState<{ tableId?: string; viewId?: string }>({});
|
||||||
const { viewId, tableId } = anchor;
|
const { viewId, tableId } = anchor;
|
||||||
const [showDashboard, setShowDashboard] = useLocalStorage('showDashboard', false);
|
const [showDashboard, setShowDashboard] = useLocalStorage('showDashboard', false);
|
||||||
|
const { helpSiteLink } = useEnv();
|
||||||
return (
|
return (
|
||||||
<AnchorProvider viewId={viewId} tableId={tableId}>
|
<AnchorProvider viewId={viewId} tableId={tableId}>
|
||||||
<div className="h-full flex-col md:flex">
|
<div className="h-full flex-col md:flex">
|
||||||
@ -28,7 +29,7 @@ export function DashboardPage() {
|
|||||||
<li>
|
<li>
|
||||||
Visit the{' '}
|
Visit the{' '}
|
||||||
<a
|
<a
|
||||||
href={getHelpLink()}
|
href={helpSiteLink}
|
||||||
className="text-blue-500 hover:text-blue-700"
|
className="text-blue-500 hover:text-blue-700"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import type { ISelectFieldOptions } from '@teable/core';
|
import type { ISelectFieldOptions } from '@teable/core';
|
||||||
import { Colors, ColorUtils, CellValueType, FieldType } from '@teable/core';
|
import { Colors, ColorUtils, CellValueType, FieldType } from '@teable/core';
|
||||||
import { useBase, useFields, useTable, useView } from '@teable/sdk/hooks';
|
import { useBase, useFields, useTable, useView } from '@teable/sdk/hooks';
|
||||||
import { Base } from '@teable/sdk/model';
|
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
interface IData {
|
interface IData {
|
||||||
@ -35,7 +34,8 @@ export function useChartData() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nativeSql = Base.knex(table.dbTableName)
|
const nativeSql = base
|
||||||
|
.knex(table.dbTableName)
|
||||||
.select(`${groupingField.dbFieldName} as name`)
|
.select(`${groupingField.dbFieldName} as name`)
|
||||||
.sum(`${numberField.dbFieldName} as total`)
|
.sum(`${numberField.dbFieldName} as total`)
|
||||||
.groupBy(groupingField.dbFieldName)
|
.groupBy(groupingField.dbFieldName)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { CellValueType, FieldType } from '@teable/core';
|
import { CellValueType, FieldType } from '@teable/core';
|
||||||
import { useBase, useFields, useTable, useViewId } from '@teable/sdk/hooks';
|
import { useBase, useFields, useTable, useViewId } from '@teable/sdk/hooks';
|
||||||
import { Base } from '@teable/sdk/model';
|
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
interface IData {
|
interface IData {
|
||||||
@ -35,7 +34,8 @@ export function useLineChartData() {
|
|||||||
|
|
||||||
const nameColumn = selectField.dbFieldName;
|
const nameColumn = selectField.dbFieldName;
|
||||||
const numberColumn = numberField.dbFieldName;
|
const numberColumn = numberField.dbFieldName;
|
||||||
const nativeSql = Base.knex(table.dbTableName)
|
const nativeSql = base
|
||||||
|
.knex(table.dbTableName)
|
||||||
.select(nameColumn)
|
.select(nameColumn)
|
||||||
.min(numberColumn + ' as total')
|
.min(numberColumn + ' as total')
|
||||||
.avg(numberColumn + ' as average')
|
.avg(numberColumn + ' as average')
|
||||||
|
6
apps/nextjs-app/src/features/app/hooks/useEnv.ts
Normal file
6
apps/nextjs-app/src/features/app/hooks/useEnv.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
import { EnvContext } from '@/lib/server-env';
|
||||||
|
|
||||||
|
export function useEnv() {
|
||||||
|
return useContext(EnvContext);
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import type { ITableVo } from '@teable/core';
|
import type { DriverClient, ITableVo } from '@teable/core';
|
||||||
import type { IGetBaseVo } from '@teable/openapi';
|
import type { IGetBaseVo } from '@teable/openapi';
|
||||||
import { NotificationProvider, SessionProvider } from '@teable/sdk';
|
import { NotificationProvider, SessionProvider } from '@teable/sdk';
|
||||||
import type { IUser } from '@teable/sdk';
|
import type { IUser } from '@teable/sdk';
|
||||||
@ -14,15 +14,16 @@ export const BaseLayout: React.FC<{
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
tableServerData: ITableVo[];
|
tableServerData: ITableVo[];
|
||||||
baseServerData: IGetBaseVo;
|
baseServerData: IGetBaseVo;
|
||||||
|
driver?: DriverClient;
|
||||||
user?: IUser;
|
user?: IUser;
|
||||||
}> = ({ children, tableServerData, baseServerData, user }) => {
|
}> = ({ children, tableServerData, baseServerData, driver, user }) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { baseId, tableId, viewId } = router.query;
|
const { baseId, tableId, viewId } = router.query;
|
||||||
const sdkLocale = useSdkLocale();
|
const sdkLocale = useSdkLocale();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppLayout>
|
<AppLayout>
|
||||||
<AppProvider locale={sdkLocale}>
|
<AppProvider locale={sdkLocale} driver={driver as DriverClient}>
|
||||||
<SessionProvider user={user}>
|
<SessionProvider user={user}>
|
||||||
<NotificationProvider>
|
<NotificationProvider>
|
||||||
<AnchorContext.Provider
|
<AnchorContext.Provider
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import type { DriverClient } from '@teable/core';
|
||||||
import type { IUser } from '@teable/sdk';
|
import type { IUser } from '@teable/sdk';
|
||||||
import { AppProvider, SessionProvider } from '@teable/sdk';
|
import { AppProvider, SessionProvider } from '@teable/sdk';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@ -7,12 +8,13 @@ import { useSdkLocale } from '../hooks/useSdkLocale';
|
|||||||
export const SettingLayout: React.FC<{
|
export const SettingLayout: React.FC<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
user?: IUser;
|
user?: IUser;
|
||||||
|
driver: DriverClient;
|
||||||
dehydratedState?: unknown;
|
dehydratedState?: unknown;
|
||||||
}> = ({ children, user, dehydratedState }) => {
|
}> = ({ children, user, driver, dehydratedState }) => {
|
||||||
const sdkLocale = useSdkLocale();
|
const sdkLocale = useSdkLocale();
|
||||||
return (
|
return (
|
||||||
<AppLayout>
|
<AppLayout>
|
||||||
<AppProvider locale={sdkLocale} dehydratedState={dehydratedState}>
|
<AppProvider locale={sdkLocale} dehydratedState={dehydratedState} driver={driver}>
|
||||||
<SessionProvider user={user}>
|
<SessionProvider user={user}>
|
||||||
<div id="portal" className="relative flex h-screen w-full items-start px-5">
|
<div id="portal" className="relative flex h-screen w-full items-start px-5">
|
||||||
{children}
|
{children}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import type { DriverClient } from '@teable/core';
|
||||||
import type { IUser } from '@teable/sdk';
|
import type { IUser } from '@teable/sdk';
|
||||||
import { NotificationProvider, SessionProvider } from '@teable/sdk';
|
import { NotificationProvider, SessionProvider } from '@teable/sdk';
|
||||||
import { AppProvider } from '@teable/sdk/context';
|
import { AppProvider } from '@teable/sdk/context';
|
||||||
@ -10,12 +11,13 @@ export const SpaceLayout: React.FC<{
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
user?: IUser;
|
user?: IUser;
|
||||||
dehydratedState?: unknown;
|
dehydratedState?: unknown;
|
||||||
}> = ({ children, user, dehydratedState }) => {
|
driver: DriverClient;
|
||||||
|
}> = ({ children, user, driver, dehydratedState }) => {
|
||||||
const sdkLocale = useSdkLocale();
|
const sdkLocale = useSdkLocale();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppLayout>
|
<AppLayout>
|
||||||
<AppProvider locale={sdkLocale} dehydratedState={dehydratedState}>
|
<AppProvider locale={sdkLocale} dehydratedState={dehydratedState} driver={driver}>
|
||||||
<SessionProvider user={user}>
|
<SessionProvider user={user}>
|
||||||
<NotificationProvider>
|
<NotificationProvider>
|
||||||
<div id="portal" className="relative flex h-screen w-full items-start">
|
<div id="portal" className="relative flex h-screen w-full items-start">
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
export const getHelpLink = () => process.env.NEXT_PUBLIC_HELP_SITE_LINK || 'https://help.teable.io';
|
|
||||||
|
|
||||||
export const getTemplateCenterLink = () => process.env.NEXT_PUBLIC_TEMPLATE_SITE_LINK;
|
|
9
apps/nextjs-app/src/lib/server-env.ts
Normal file
9
apps/nextjs-app/src/lib/server-env.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export interface IServerEnv {
|
||||||
|
helpSiteLink?: string;
|
||||||
|
templateSiteLink?: string;
|
||||||
|
microsoftClarityId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const EnvContext = React.createContext<IServerEnv>({});
|
@ -13,6 +13,7 @@ import { getUserMe } from '@/backend/api/rest/get-user';
|
|||||||
import { Guide } from '@/components/Guide';
|
import { Guide } from '@/components/Guide';
|
||||||
import { MicrosoftClarity } from '@/components/Metrics';
|
import { MicrosoftClarity } from '@/components/Metrics';
|
||||||
import RouterProgressBar from '@/components/RouterProgress';
|
import RouterProgressBar from '@/components/RouterProgress';
|
||||||
|
import type { IServerEnv } from '@/lib/server-env';
|
||||||
import type { NextPageWithLayout } from '@/lib/type';
|
import type { NextPageWithLayout } from '@/lib/type';
|
||||||
import { colors } from '@/themes/colors';
|
import { colors } from '@/themes/colors';
|
||||||
import { INITIAL_THEME } from '@/themes/initial';
|
import { INITIAL_THEME } from '@/themes/initial';
|
||||||
@ -42,24 +43,20 @@ type AppPropsWithLayout = AppProps & {
|
|||||||
Component: NextPageWithLayout;
|
Component: NextPageWithLayout;
|
||||||
user?: IUser;
|
user?: IUser;
|
||||||
driver: string;
|
driver: string;
|
||||||
|
env: IServerEnv;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @link https://nextjs.org/docs/advanced-features/custom-app
|
* @link https://nextjs.org/docs/advanced-features/custom-app
|
||||||
*/
|
*/
|
||||||
const MyApp = (appProps: AppPropsWithLayout) => {
|
const MyApp = (appProps: AppPropsWithLayout) => {
|
||||||
const { Component, pageProps, err, user, driver } = appProps;
|
const { Component, pageProps, err, user, driver, env } = appProps;
|
||||||
// Use the layout defined at the page level, if available
|
// Use the layout defined at the page level, if available
|
||||||
const getLayout = Component.getLayout ?? ((page) => page);
|
const getLayout = Component.getLayout ?? ((page) => page);
|
||||||
|
|
||||||
const serverInfo = {
|
|
||||||
driver,
|
|
||||||
user,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<AppProviders>
|
<AppProviders env={env}>
|
||||||
<Head>
|
<Head>
|
||||||
<meta
|
<meta
|
||||||
name="viewport"
|
name="viewport"
|
||||||
@ -67,18 +64,17 @@ const MyApp = (appProps: AppPropsWithLayout) => {
|
|||||||
/>
|
/>
|
||||||
<style>{getColorsCssVariablesText(colors)}</style>
|
<style>{getColorsCssVariablesText(colors)}</style>
|
||||||
</Head>
|
</Head>
|
||||||
<MicrosoftClarity />
|
<MicrosoftClarity clarityId={env?.microsoftClarityId} />
|
||||||
<script dangerouslySetInnerHTML={{ __html: INITIAL_THEME }} />
|
<script dangerouslySetInnerHTML={{ __html: INITIAL_THEME }} />
|
||||||
<script
|
<script
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: `
|
__html: `
|
||||||
window.__s = ${JSON.stringify(serverInfo)};
|
|
||||||
window.clarity && window.clarity("identify", "${user?.email || user?.id}");
|
window.clarity && window.clarity("identify", "${user?.email || user?.id}");
|
||||||
`,
|
`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/* Workaround for https://github.com/vercel/next.js/issues/8592 */}
|
{/* Workaround for https://github.com/vercel/next.js/issues/8592 */}
|
||||||
{getLayout(<Component {...pageProps} err={err} />, { ...pageProps, user })}
|
{getLayout(<Component {...pageProps} err={err} />, { ...pageProps, user, driver })}
|
||||||
</AppProviders>
|
</AppProviders>
|
||||||
<Guide user={user} />
|
<Guide user={user} />
|
||||||
<RouterProgressBar />
|
<RouterProgressBar />
|
||||||
@ -106,6 +102,11 @@ MyApp.getInitialProps = async (appContext: AppContext) => {
|
|||||||
const initialProps = {
|
const initialProps = {
|
||||||
...appProps,
|
...appProps,
|
||||||
driver,
|
driver,
|
||||||
|
env: {
|
||||||
|
helpSiteLink: process.env.HELP_SITE_LINK,
|
||||||
|
templateSiteLink: process.env.TEMPLATE_SITE_LINK,
|
||||||
|
microsoftClarityId: process.env.MICROSOFT_CLARITY_ID,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
if (!isLoginPage && !needLoginPage) {
|
if (!isLoginPage && !needLoginPage) {
|
||||||
return initialProps;
|
return initialProps;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { IHttpError } from '@teable/core';
|
import { parseDsn, type DriverClient, type IHttpError } from '@teable/core';
|
||||||
import type { ShareViewGetVo } from '@teable/openapi';
|
import type { ShareViewGetVo } from '@teable/openapi';
|
||||||
import type { GetServerSideProps } from 'next';
|
import type { GetServerSideProps } from 'next';
|
||||||
import { SsrApi } from '@/backend/api/rest/table.ssr';
|
import { SsrApi } from '@/backend/api/rest/table.ssr';
|
||||||
@ -17,9 +17,11 @@ export const getServerSideProps: GetServerSideProps<IShareViewPageProps> = async
|
|||||||
res.setHeader('Content-Security-Policy', "frame-ancestors 'self' *;");
|
res.setHeader('Content-Security-Policy', "frame-ancestors 'self' *;");
|
||||||
ssrApi.axios.defaults.headers['cookie'] = req.headers.cookie || '';
|
ssrApi.axios.defaults.headers['cookie'] = req.headers.cookie || '';
|
||||||
const shareViewData = await ssrApi.getShareView(shareId as string);
|
const shareViewData = await ssrApi.getShareView(shareId as string);
|
||||||
|
const driver = parseDsn(process.env.PRISMA_DATABASE_URL as string).driver as DriverClient;
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
shareViewData,
|
shareViewData,
|
||||||
|
driver,
|
||||||
...(await getTranslationsProps(context, i18nNamespaces)),
|
...(await getTranslationsProps(context, i18nNamespaces)),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -40,6 +42,12 @@ export const getServerSideProps: GetServerSideProps<IShareViewPageProps> = async
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ShareView({ shareViewData }: { shareViewData: ShareViewGetVo }) {
|
export default function ShareView({
|
||||||
return <ShareViewPage shareViewData={shareViewData} />;
|
shareViewData,
|
||||||
|
driver,
|
||||||
|
}: {
|
||||||
|
shareViewData: ShareViewGetVo;
|
||||||
|
driver: DriverClient;
|
||||||
|
}) {
|
||||||
|
return <ShareViewPage shareViewData={shareViewData} driver={driver} />;
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,6 @@ FROM deps AS builder
|
|||||||
|
|
||||||
ARG INTEGRATION_TEST
|
ARG INTEGRATION_TEST
|
||||||
|
|
||||||
ARG NEXT_PUBLIC_MICROSOFT_CLARITY
|
|
||||||
|
|
||||||
ENV NEXT_BUILD_ENV_TYPECHECK=false
|
ENV NEXT_BUILD_ENV_TYPECHECK=false
|
||||||
ENV NEXT_BUILD_ENV_LINT=false
|
ENV NEXT_BUILD_ENV_LINT=false
|
||||||
ENV NEXT_BUILD_ENV_OUTPUT=classic
|
ENV NEXT_BUILD_ENV_OUTPUT=classic
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Hydrate, QueryClientProvider } from '@tanstack/react-query';
|
import { Hydrate, QueryClientProvider } from '@tanstack/react-query';
|
||||||
|
import type { DriverClient } from '@teable/core';
|
||||||
import { isObject } from 'lodash';
|
import { isObject } from 'lodash';
|
||||||
import { useEffect, useMemo } from 'react';
|
import { useEffect, useMemo } from 'react';
|
||||||
import { getDriver } from '../../utils/driver';
|
|
||||||
import { AppContext } from '../app/AppContext';
|
import { AppContext } from '../app/AppContext';
|
||||||
import type { ILocale, ILocalePartial } from './i18n';
|
import type { ILocale, ILocalePartial } from './i18n';
|
||||||
import { defaultLocale } from './i18n';
|
import { defaultLocale } from './i18n';
|
||||||
@ -15,11 +15,12 @@ interface IAppProviderProps {
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
wsPath?: string;
|
wsPath?: string;
|
||||||
locale?: ILocalePartial;
|
locale?: ILocalePartial;
|
||||||
|
driver: DriverClient;
|
||||||
dehydratedState?: unknown;
|
dehydratedState?: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AppProvider = (props: IAppProviderProps) => {
|
export const AppProvider = (props: IAppProviderProps) => {
|
||||||
const { children, wsPath, locale, dehydratedState } = props;
|
const { children, wsPath, locale, driver, dehydratedState } = props;
|
||||||
|
|
||||||
const { connected, connection } = useConnection(wsPath);
|
const { connected, connection } = useConnection(wsPath);
|
||||||
const themeProps = useTheme();
|
const themeProps = useTheme();
|
||||||
@ -34,11 +35,11 @@ export const AppProvider = (props: IAppProviderProps) => {
|
|||||||
return {
|
return {
|
||||||
connection,
|
connection,
|
||||||
connected,
|
connected,
|
||||||
driver: getDriver(),
|
driver,
|
||||||
locale: isObject(locale) ? ({ ...defaultLocale, ...locale } as ILocale) : defaultLocale,
|
locale: isObject(locale) ? ({ ...defaultLocale, ...locale } as ILocale) : defaultLocale,
|
||||||
...themeProps,
|
...themeProps,
|
||||||
};
|
};
|
||||||
}, [connection, connected, locale, themeProps]);
|
}, [connection, connected, driver, locale, themeProps]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppContext.Provider value={value}>
|
<AppContext.Provider value={value}>
|
||||||
|
@ -5,6 +5,7 @@ import type { FC, ReactNode } from 'react';
|
|||||||
import { useContext, useMemo } from 'react';
|
import { useContext, useMemo } from 'react';
|
||||||
import { Base } from '../../model';
|
import { Base } from '../../model';
|
||||||
import { AnchorContext } from '../anchor';
|
import { AnchorContext } from '../anchor';
|
||||||
|
import { AppContext } from '../app';
|
||||||
import { BaseContext } from './BaseContext';
|
import { BaseContext } from './BaseContext';
|
||||||
|
|
||||||
interface IBaseProviderProps {
|
interface IBaseProviderProps {
|
||||||
@ -14,6 +15,7 @@ interface IBaseProviderProps {
|
|||||||
|
|
||||||
export const BaseProvider: FC<IBaseProviderProps> = ({ children, serverData }) => {
|
export const BaseProvider: FC<IBaseProviderProps> = ({ children, serverData }) => {
|
||||||
const { baseId } = useContext(AnchorContext);
|
const { baseId } = useContext(AnchorContext);
|
||||||
|
const { driver } = useContext(AppContext);
|
||||||
const { data: baseData, isLoading } = useQuery({
|
const { data: baseData, isLoading } = useQuery({
|
||||||
queryKey: ['base', baseId],
|
queryKey: ['base', baseId],
|
||||||
queryFn: ({ queryKey }) => (queryKey[1] ? getBaseById(queryKey[1]) : undefined),
|
queryFn: ({ queryKey }) => (queryKey[1] ? getBaseById(queryKey[1]) : undefined),
|
||||||
@ -21,8 +23,8 @@ export const BaseProvider: FC<IBaseProviderProps> = ({ children, serverData }) =
|
|||||||
|
|
||||||
const value = useMemo(() => {
|
const value = useMemo(() => {
|
||||||
const base = isLoading ? serverData : baseData?.data;
|
const base = isLoading ? serverData : baseData?.data;
|
||||||
return { base: base ? new Base(base) : undefined };
|
return { base: base ? new Base(base, driver) : undefined };
|
||||||
}, [isLoading, baseData, serverData]);
|
}, [isLoading, serverData, baseData?.data, driver]);
|
||||||
|
|
||||||
return <BaseContext.Provider value={value}>{children}</BaseContext.Provider>;
|
return <BaseContext.Provider value={value}>{children}</BaseContext.Provider>;
|
||||||
};
|
};
|
||||||
|
@ -8,15 +8,6 @@ interface ISessionProviderProps {
|
|||||||
user?: IUser;
|
user?: IUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
__s: {
|
|
||||||
user?: IUser;
|
|
||||||
driver: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SessionProvider: React.FC<React.PropsWithChildren<ISessionProviderProps>> = (
|
export const SessionProvider: React.FC<React.PropsWithChildren<ISessionProviderProps>> = (
|
||||||
props
|
props
|
||||||
) => {
|
) => {
|
||||||
@ -25,9 +16,6 @@ export const SessionProvider: React.FC<React.PropsWithChildren<ISessionProviderP
|
|||||||
if (user) {
|
if (user) {
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
if (typeof window === 'object') {
|
|
||||||
return window.__s.user;
|
|
||||||
}
|
|
||||||
return undefined;
|
return undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,21 +1,18 @@
|
|||||||
import type { ICreateTableRo, SpaceRole } from '@teable/core';
|
import type { DriverClient, ICreateTableRo, SpaceRole } from '@teable/core';
|
||||||
import type { IGetBaseVo } from '@teable/openapi';
|
import type { IGetBaseVo } from '@teable/openapi';
|
||||||
import knex from 'knex';
|
import { knex, type Knex } from 'knex';
|
||||||
import { getDriver } from '../utils/driver';
|
|
||||||
import { Table } from './table/table';
|
import { Table } from './table/table';
|
||||||
|
|
||||||
export class Base implements IGetBaseVo {
|
export class Base implements IGetBaseVo {
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
static knex = knex({ client: getDriver() });
|
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
spaceId: string;
|
spaceId: string;
|
||||||
order: number;
|
order: number;
|
||||||
icon: string | null;
|
icon: string | null;
|
||||||
role: SpaceRole;
|
role: SpaceRole;
|
||||||
|
knex: Knex;
|
||||||
|
|
||||||
constructor(base: IGetBaseVo) {
|
constructor(base: IGetBaseVo, driver: DriverClient) {
|
||||||
const { id, name, order, spaceId, icon, role } = base;
|
const { id, name, order, spaceId, icon, role } = base;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
@ -23,6 +20,7 @@ export class Base implements IGetBaseVo {
|
|||||||
this.order = order;
|
this.order = order;
|
||||||
this.icon = icon;
|
this.icon = icon;
|
||||||
this.role = role;
|
this.role = role;
|
||||||
|
this.knex = knex({ client: driver });
|
||||||
}
|
}
|
||||||
|
|
||||||
async sqlQuery(tableId: string, viewId: string, sql: string) {
|
async sqlQuery(tableId: string, viewId: string, sql: string) {
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
import { DriverClient } from '@teable/core';
|
|
||||||
|
|
||||||
export function getDriver(): DriverClient {
|
|
||||||
if (typeof window === 'object') {
|
|
||||||
return (window.__s?.driver as DriverClient) || DriverClient.Sqlite;
|
|
||||||
}
|
|
||||||
return DriverClient.Sqlite;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user